de.fu_berlin.inf.dpp.misc.pico.DotGraphMonitor.java Source code

Java tutorial

Introduction

Here is the source code for de.fu_berlin.inf.dpp.misc.pico.DotGraphMonitor.java

Source

package de.fu_berlin.inf.dpp.misc.pico;

/*****************************************************************************
 * Copyright (C) PicoContainer Organization. All rights reserved.            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *                                                                           *
 * Original code by Paul Hammant                                             *
 *****************************************************************************/

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.ComponentMonitor;
import org.picocontainer.Injector;
import org.picocontainer.PicoContainer;
import org.picocontainer.monitors.AbstractComponentMonitor;

import de.fu_berlin.inf.dpp.annotations.Component;
import de.fu_berlin.inf.dpp.util.Function;
import de.fu_berlin.inf.dpp.util.Pair;

/**
 * Component monitor which can be used to create a dependency graph of
 * components created and re-injected via PicoContainer (the latter is the
 * reason why this is an adapted implementation).
 * 
 * Based on DotDependencyGraphComponentMonitor
 * 
 * Run like this:
 * 
 * neato -Tsvg -Goverlap=false file.dot > diagram.svg
 * 
 * @author oezbek
 */
public final class DotGraphMonitor extends AbstractComponentMonitor {

    private static final long serialVersionUID = 7368290879876948459L;

    private static final Logger log = Logger.getLogger(DotGraphMonitor.class.getName());

    protected IdentityHashMap<Object, Instantiation> allInstantiated = new IdentityHashMap<Object, Instantiation>();

    public static boolean cluster = false;

    public DotGraphMonitor(final ComponentMonitor delegate) {
        super(delegate);
    }

    public void save(File file) {
        try {
            String output = "digraph G {\n" + "  node [shape=box];\n" + "  rank=source;\n"
                    + "  rankdir=LR;\n  node[penwidth=2.0];\n" + getClassDependencyGraph() + "\n" + "}";

            FileUtils.writeStringToFile(file, output);
        } catch (Exception e) {
            log.error("Internal error: ", e);
        }
    }

    public DotGraphMonitor() {
        // do nothing
    }

    @Override
    public <T> void instantiated(final PicoContainer container, final ComponentAdapter<T> componentAdapter,
            final Constructor<T> constructor, final Object instantiated, final Object[] injected,
            final long duration) {

        this.allInstantiated.put(instantiated, new Instantiation(constructor, instantiated, injected));

        super.instantiated(container, componentAdapter, constructor, instantiated, injected, duration);
    }

    @Override
    public void invoked(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member,
            Object instance, long duration, Object[] args, Object retVal) {

        if (!(componentAdapter instanceof Injector<?>))
            return;

        Instantiation i = allInstantiated.get(instance);
        if (i == null) {
            i = new Instantiation(null, instance, new Object[] {});
            allInstantiated.put(instance, i);
        }

        Class<?> clazz = null;

        if (member instanceof Field) {
            Field f = (Field) member;
            f.setAccessible(true);
            try {
                Object object = f.get(instance);
                if (object != null)
                    clazz = object.getClass();
                else
                    clazz = f.getType();
            } catch (Exception e) {
                clazz = f.getType();
            }

            i.fieldInjected.add(clazz);
        }

        super.invoked(container, componentAdapter, member, instance, duration, args, retVal);
    }

    public String getClassDependencyGraph() {

        Set<String> lines = new HashSet<String>();

        Set<Class<?>> allComponentClasses = new HashSet<Class<?>>();

        for (Instantiation instantiation : allInstantiated.values()) {

            Object instantiated = instantiation.getInstantiated();
            allComponentClasses.add(instantiated.getClass());

            Object[] injects = instantiation.getConstructorInjected();
            for (int j = 0; j < injects.length; j++) {
                Object injected = injects[j];
                allComponentClasses.add(injected.getClass());
                lines.add("  '" + instantiated.getClass().getSimpleName() + "' -> '"
                        + injected.getClass().getSimpleName() + "';\n");
            }

            for (Class<?> invocationInjected : instantiation.getInvocationInjected()) {
                allComponentClasses.add(invocationInjected);
                lines.add("  '" + instantiated.getClass().getSimpleName() + "' -> '"
                        + invocationInjected.getSimpleName() + "';\n");
            }
        }

        HashMap<String, String> colors = new HashMap<String, String>();
        colors.put("core", "red");
        colors.put("session", "crimson");
        colors.put("observables", "darkolivegreen1");
        colors.put("net", "blue");
        colors.put("util", "thistle1");
        colors.put("misc", "aquamarine");
        colors.put("undo", "gold1");
        colors.put("logging", "yellow");
        colors.put("consistency", "green");
        colors.put("ui", "gold1");
        colors.put("action", "coral");
        colors.put("integration", "deeppink");
        colors.put("feedback", "blueviolet");
        colors.put("prefs", "blueviolet");
        colors.put("eclipse", "darkviolet");
        colors.put("pico", "black");

        int i = 0;

        StringBuilder sb = new StringBuilder();

        if (!cluster) {
            sb.append("subgraph cluster").append(i++).append("{\n");
            sb.append("  style=rounded;  style=filled;" + " bgcolor=white; fontsize=40;\n");
            sb.append("  label=\"legend\";\n");
            for (Entry<String, String> entry : colors.entrySet()) {
                sb.append("\"").append(entry.getKey()).append("\" [color=").append(entry.getValue()).append("];\n");
            }
            sb.append("}\n");
        }

        for (Pair<String, List<Class<?>>> p : Pair.partition(allComponentClasses, new ModuleFunction())) {

            if (cluster) {
                sb.append("subgraph cluster").append(i++).append("{\n");
                sb.append("  style=rounded;  style=filled; color=gray92; fontsize=40;\n");
                sb.append("  label=\"").append(p.p).append("\";\n");
            }

            String color = colors.get(p.p);
            if (color == null) {
                log.warn("No color found for Module " + p.p);
                color = "black";
            }

            for (Class<?> clazz : p.v) {
                sb.append("  \"" + clazz.getSimpleName() + "\"");
                sb.append(" [color=" + color + "]");
                sb.append(";\n");
            }

            if (cluster)
                sb.append("}\n");
        }

        return sb.toString() + sortLines(lines);
    }

    public static String getColorOld(Class<?> clazz, HashMap<String, String> colors) {
        String name;
        Package myPackage = clazz.getPackage();
        if (myPackage == null) {
            name = "misc";
        } else {
            name = myPackage.getName();
        }

        while (name != null && name.length() > 0) {

            String color = colors.get(name);
            if (color != null) {
                return "  \"" + clazz.getSimpleName() + "\" [color=" + color + "];\n";
            }

            int index = name.lastIndexOf('.');
            if (index == -1)
                break;

            name = name.substring(0, index);
        }

        return null;
    }

    private String sortLines(final Set<String> lines) {
        List<String> list = new ArrayList<String>(lines);
        Collections.sort(list);

        StringBuilder dependencies = new StringBuilder();
        for (Object aList : list) {
            dependencies.append(aList);
        }
        return dependencies.toString().replaceAll("'", "\"");
    }

    public String getInterfaceDependencyGraph() {
        Set<String> lines = new HashSet<String>();

        for (Instantiation instantiation : allInstantiated.values()) {

            for (int j = 0; j < instantiation.getConstructorInjected().length; j++) {
                Object injected = instantiation.getConstructorInjected()[j];
                Class<?> injectedType = instantiation.getConstructor().getParameterTypes()[j];
                Object instantiated = instantiation.getInstantiated();
                if (injected.getClass() != injectedType) {
                    lines.add("  '" + instantiated.getClass().getName() + "' -> '" + injectedType.getName()
                            + "' [style=dotted,label='needs'];\n");
                    lines.add("  '" + injected.getClass().getName() + "' -> '" + injectedType.getName()
                            + "' [style=dotted, color=red,label='isA'];\n");
                    lines.add("  '" + injectedType.getName() + "' [shape=box, label=" + printClassName(injectedType)
                            + "];\n");
                } else {
                    lines.add("  '" + instantiated.getClass().getName() + "' -> '" + injected.getClass().getName()
                            + "' [label='needs'];\n");
                }
                lines.add("  '" + instantiated.getClass().getName() + "' [label="
                        + printClassName(instantiated.getClass()) + "];\n");
            }
        }

        return sortLines(lines);
    }

    private String printClassName(final Class<?> clazz) {
        String className = clazz.getName();
        return "'" + className.substring(className.lastIndexOf(".") + 1) + "\\n" + clazz.getPackage().getName()
                + "'";

    }

    public static final class ModuleFunction implements Function<Class<?>, String> {
        @Override
        public String apply(Class<?> u) {
            if (u.isArray()) {
                u = u.getComponentType();
            }
            Component c = u.getAnnotation(Component.class);
            if (c == null) {
                log.debug("Injected component with no @Component annotation: " + u.getSimpleName());
                return Component.DEFAULT_MODULE;
            } else {
                return c.module();
            }
        }
    }

    private static final class Instantiation {

        public List<Class<?>> fieldInjected = new ArrayList<Class<?>>();
        final Constructor<?> constructor;
        final Object instantiated;
        final Object[] injected;

        public Instantiation(final Constructor<?> constructor, final Object instantiated, final Object[] injected) {
            this.constructor = constructor;
            this.instantiated = instantiated;
            this.injected = injected;
        }

        public List<Class<?>> getInvocationInjected() {
            return fieldInjected;
        }

        public Constructor<?> getConstructor() {
            return constructor;
        }

        public Object getInstantiated() {
            return instantiated;
        }

        public Object[] getConstructorInjected() {
            return injected;
        }
    }
}