byke.DependencyAnalysis.java Source code

Java tutorial

Introduction

Here is the source code for byke.DependencyAnalysis.java

Source

//Copyright (C) 2004 Klaus Wuestefeld and Rodrigo B de Oliveira and Kent Beck.
//This is free software. See the license distributed along with this file.
package byke;

import static java.util.Arrays.asList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import byke.dependencygraph.Node;

public class DependencyAnalysis implements NodeAccumulator {

    @SuppressWarnings("unchecked")
    private static final Node<IBinding>[] NODE_ARRAY = new Node[0];

    private final Map<String, Node<IBinding>> _nodesByKey = new HashMap<String, Node<IBinding>>();

    private IJavaElement _subject;

    public DependencyAnalysis(IJavaElement child) throws InvalidElement {
        _subject = enclosingSubject(child);
        if (_subject == null)
            throw new InvalidElement("No supported subject found for child: " + child);
    }

    private IJavaElement enclosingSubject(IJavaElement element) throws InvalidElement {
        if (element == null)
            return null;
        if (element instanceof IType)
            return element;
        if (element instanceof ICompilationUnit)
            return ((ICompilationUnit) element).findPrimaryType();
        if (element instanceof IMember)
            return enclosingTypeOf((IMember) element);

        return enclosingPackageOf(element);
    }

    private IJavaElement enclosingTypeOf(IMember element) {
        return element.getAncestor(IJavaElement.TYPE);
    }

    private IJavaElement enclosingPackageOf(IJavaElement element) throws InvalidElement {
        IPackageFragment result = (IPackageFragment) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
        assertValid(result);
        return result;
    }

    private void assertValid(IPackageFragment fragment) throws InvalidElement {
        if (fragment == null)
            throw new InvalidElement("Null Package Fragment");
        try {
            if (fragment.getKind() == IPackageFragmentRoot.K_BINARY)
                throw new InvalidElement("Binary Package");
        } catch (JavaModelException e) {
            throw new InvalidElement(e);
        }
    }

    public Collection<Node<IBinding>> dependencyGraph(IProgressMonitor monitor) {
        try {
            populateNodes(monitor);
        } catch (Exception x) {
            x.printStackTrace();
        }

        if (MergeClassPatterns.existsMergeClassValue())
            mergeDependencies();

        mergeSubpackagesDependencies();

        return _nodesByKey.values();
    }

    private void mergeSubpackagesDependencies() {
        if (!(_subject instanceof IPackageFragment))
            return;

        removeSubjectNode();

        List<Node<IBinding>> toRemove = new ArrayList<Node<IBinding>>();

        for (Node<IBinding> node : _nodesByKey.values()) {
            if (node.kind().equals(JavaType.PACKAGE))
                continue;

            ITypeBinding payload = (ITypeBinding) node.payload();
            if (!payload.getPackage().equals(_subject))
                for (Node<IBinding> packageToMerge : _nodesByKey.values())
                    if (packageToMerge.name().equals(payload.getPackage().getName())) {
                        switchProvider(node, packageToMerge);
                        packageToMerge.addProviders(node.providers());
                        toRemove.add(node);
                    }
        }

        _nodesByKey.values().removeAll(toRemove);
    }

    private void removeSubjectNode() {
        for (Node<IBinding> node : _nodesByKey.values())
            if (_subject.getElementName().equals(node.name())) {
                _nodesByKey.values().remove(node);
                for (Node<IBinding> nodeToRemoveSubjectNode : _nodesByKey.values())
                    nodeToRemoveSubjectNode.providers().remove(node);
                return;
            }
    }

    private void mergeDependencies() {
        List<Node<IBinding>> toRemove = new ArrayList<Node<IBinding>>();
        List<Pattern> patterns = MergeClassPatterns.getPatterns();
        for (Pattern pattern : patterns)
            for (Node<IBinding> node : _nodesByKey.values()) {
                Matcher matcher = pattern.matcher(node.name());
                if (matcher.find())
                    for (Node<IBinding> nodeToMerge : _nodesByKey.values())
                        if (nodeToMerge.name().equals(matcher.group(1))) {
                            switchProvider(node, nodeToMerge);
                            toRemove.add(node);
                        }
            }

        _nodesByKey.values().removeAll(toRemove);
    }

    private void switchProvider(Node<IBinding> node, Node<IBinding> nodeToSwitch) {
        for (Node<IBinding> nodeToMerge : _nodesByKey.values())
            if (nodeToMerge.providers().remove(node))
                nodeToMerge.addProvider(nodeToSwitch);
    }

    private void populateNodes(IProgressMonitor monitor) throws JavaModelException {
        if (monitor == null)
            monitor = new NullProgressMonitor();

        ASTParser parser = ASTParser.newParser(AST.JLS4);

        ASTVisitor visitor = _subject instanceof IType ? new TypeVisitor()
                : new PackageAnalyser(this, _subject.getElementName());

        List<ICompilationUnit> compilationUnits = compilationUnits();
        monitor.beginTask("dependency analysis", compilationUnits.size());

        for (ICompilationUnit each : compilationUnits) {
            if (monitor.isCanceled())
                break;
            populateNodes(monitor, parser, visitor, each);
        }

    }

    private void populateNodes(IProgressMonitor monitor, ASTParser parser, ASTVisitor visitor,
            ICompilationUnit each) {
        monitor.subTask(each.getElementName());

        parser.setResolveBindings(true);
        parser.setSource(each);
        CompilationUnit node = (CompilationUnit) parser.createAST(monitor);
        node.accept(visitor);
        monitor.worked(1);
    }

    private List<ICompilationUnit> compilationUnits() throws JavaModelException {
        if (!(_subject instanceof IPackageFragment))
            return asList(((ICompilationUnit) _subject.getAncestor(IJavaElement.COMPILATION_UNIT)));

        List<ICompilationUnit> ret = new ArrayList<ICompilationUnit>();
        for (IPackageFragment packge : withSubpackages((IPackageFragment) _subject))
            ret.addAll(asList(packge.getCompilationUnits()));

        return ret;
    }

    @Override
    public Node<IBinding> produceNode(IBinding binding, JavaType kind) {
        String key = binding.getKey();
        return produceNode(key, binding, kind);
    }

    @Override
    public Node<IBinding> produceNode(String key, JavaType kind) {
        return produceNode(key, null, kind);
    }

    private Node<IBinding> produceNode(String key, IBinding binding, JavaType kind) {
        Node<IBinding> node = _nodesByKey.get(key);
        if (null == node) {
            String name = binding == null ? key : binding.getName();
            if (binding != null && kind.equals(JavaType.METHOD)) {
                name += "(";

                ITypeBinding[] parameterTypes = ((IMethodBinding) binding).getParameterTypes();

                for (int i = 0; i < parameterTypes.length; i++) {
                    name += parameterTypes[i].getName();

                    if (i < parameterTypes.length - 1)
                        name += ",";
                }

                name += ")";
            }

            node = new Node<IBinding>(name, kind);
            node.payload(binding);
            _nodesByKey.put(key, node);
        }
        return node;
    }

    class TypeVisitor extends ASTVisitor {

        @Override
        public boolean visit(TypeDeclaration node) {
            ITypeBinding binding = node.resolveBinding();
            IJavaElement javaElement = binding.getJavaElement();
            if (!javaElement.getHandleIdentifier().equals(_subject.getHandleIdentifier()))
                return true;

            new TypeAnalyser(node, DependencyAnalysis.this, binding);
            return false;
        }
    }

    public IJavaElement subject() {
        return _subject;
    }

    @Override
    public void remove(Node<IBinding> node) {
        Node<IBinding> removed = _nodesByKey.remove(node.payload().getKey());
        if (removed != node)
            throw new IllegalStateException("Node to be removed not found: " + node.name());
    }

    @Override
    public Node<IBinding>[] nodes() {
        return _nodesByKey.values().toArray(NODE_ARRAY);
    }

    private static List<IPackageFragment> withSubpackages(IPackageFragment packge) throws JavaModelException {
        IJavaElement[] allPackages = ((IPackageFragmentRoot) packge.getParent()).getChildren();
        List<IPackageFragment> ret = new ArrayList<IPackageFragment>();
        for (IJavaElement candidate : allPackages)
            if (candidate.getElementName().startsWith(packge.getElementName()))
                ret.add((IPackageFragment) candidate);

        return ret;
    }
}