net.sourceforge.metrics.ui.dependencies.TangleAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.metrics.ui.dependencies.TangleAnalyzer.java

Source

/*
 * Copyright (c) 2003 Frank Sauer. All rights reserved.
 *
 * Licenced under CPL 1.0 (Common Public License Version 1.0).
 * The licence is available at http://www.eclipse.org/legal/cpl-v10.html.
 *
 *
 * DISCLAIMER OF WARRANTIES AND LIABILITY:
 *
 * THE SOFTWARE IS PROVIDED "AS IS".  THE AUTHOR MAKES  NO REPRESENTATIONS OR WARRANTIES,
 * EITHER EXPRESS OR IMPLIED.  TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL THE
 * AUTHOR  BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE,  PROFITS
 * OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL  OR PUNITIVE DAMAGES,
 * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF  LIABILITY, ARISING OUT OF OR RELATED TO
 * ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SOFTWARE, EVEN IF THE AUTHOR
 * HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 *
 * $id$
 */
package net.sourceforge.metrics.ui.dependencies;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import classycle.graph.StrongComponent;
import classycle.graph.Vertex;

/**
 * Analyze the details of class dependencies for all types in the packages of an single tangle (strong component).
 * 
 * @author Frank Sauer
 * 
 */
public class TangleAnalyzer {

    private StrongComponent packageTangle = null;
    private IProgressMonitor monitor = null;

    /**
     * IType-handle -> {IType-handle}*
     */
    private Map<String, Set<String>> result = null;

    /**
     * packageName -> {IType-handle}*
     */
    private Map<String, Set<String>> packages = null;

    /**
     * The results of analyzing will be stored in the two maps given. dependencies will contain IType-handle => {IType-handle}* associations and packages will contain package-name => {IType-handle}* associations
     * 
     * @param tangle
     * @param dependencies
     * @param packages
     */
    public TangleAnalyzer(StrongComponent tangle, Map<String, Set<String>> dependencies,
            Map<String, Set<String>> packages) {
        this.packageTangle = tangle;
        this.result = dependencies;
        this.packages = packages;
    }

    public void analyze() {
        Display d = Display.getDefault();
        d.syncExec(new Runnable() {

            public void run() {
                try {
                    new ProgressMonitorDialog(new Shell()).run(true, true, new IRunnableWithProgress() {

                        public void run(IProgressMonitor m) throws InvocationTargetException, InterruptedException {
                            analyze(m);
                            if (m.isCanceled()) {
                                result.clear();
                            }
                        }

                    });
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void analyze(IProgressMonitor m) {
        monitor = m;
        monitor.beginTask("Analyzing tangle details", 1000);
        List<String> packageNames = new ArrayList<String>();
        // get all package names from the StrongComponent
        for (int i = 0; i < packageTangle.getNumberOfVertices(); i++) {
            Vertex v = packageTangle.getVertex(i);
            packageNames.add(v.getAttributes().toString());
        }
        // find corresponding IPackageFragment objects
        List<IJavaElement> packages = getPackageFragments(packageNames);
        if (!m.isCanceled()) {
            getDependencies(packages);
        }
        monitor.done();
    }

    /**
     * @param packages
     * @return
     */
    private void getDependencies(List<IJavaElement> packages) {
        try {
            SearchEngine searchEngine = new SearchEngine();
            // fill in the packageName->{IType}* map by getting all type
            // declarations in scope
            IJavaElement[] packs = packages.toArray(new IJavaElement[] {});
            IJavaSearchScope scope = SearchEngine.createJavaSearchScope(packs);
            SearchPattern pattern = SearchPattern.createPattern("*", IJavaSearchConstants.TYPE,
                    IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
            TypeCollector c = new TypeCollector(result);
            monitor.subTask("Collecting types in packages");
            searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
                    scope, c, monitor);
            if (monitor.isCanceled()) {
                return;
            }
            // get all type references to these types.
            // Have to do multiple searches to get proper relationship :-(
            Set<String> typesInScope = result.keySet();
            monitor.worked(400);
            monitor.subTask("Collecting type dependencies");
            int scale = 500 / typesInScope.size();
            for (Object element : typesInScope) {
                if (monitor.isCanceled()) {
                    return;
                }
                String handle = (String) element;
                IJavaElement type = JavaCore.create(handle);
                searchEngine.searchDeclarationsOfReferencedTypes(type,
                        new RefCollector(result.get(handle), typesInScope, type), monitor);
                monitor.worked(scale);
            }
        } catch (CoreException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param packageNames
     * @return
     */
    private List<IJavaElement> getPackageFragments(List<String> packageNames) {
        monitor.subTask("Finding Packages in tangle");
        SearchEngine searchEngine = new SearchEngine();
        IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
        SearchPattern pattern = SearchPattern.createPattern("*", IJavaSearchConstants.PACKAGE,
                IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
        PackageCollector c = new PackageCollector(packageNames);
        try {
            searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
                    scope, c, monitor);
            monitor.worked(100);
            return c.getResult();
        } catch (CoreException e) {
            e.printStackTrace();
            return new ArrayList<IJavaElement>();
        }
    }

    /**
     * Find IPackageFragment objects from package names in component
     */
    public class PackageCollector extends SearchRequestor {

        private List<IJavaElement> packages = new ArrayList<IJavaElement>();
        private List<String> packageNames = null;

        public PackageCollector(List<String> packageNames) {
            this.packageNames = packageNames;
        }

        public List<IJavaElement> getResult() {
            return packages;
        }

        /*
         * count package references <em>outside</em>the current package
         * 
         * @see org.eclipse.jdt.core.search.IJavaSearchResultCollector#accept(org .eclipse.core.resources.IResource, int, int, org.eclipse.jdt.core.IJavaElement, int)
         */
        public void accept(IResource resource, int start, int end, IJavaElement enclosingElement, int accuracy)
                throws CoreException {
            if ((enclosingElement != null) && (packageNames.contains(enclosingElement.getElementName()))) {
                packages.add(enclosingElement);
            }
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jdt.core.search.SearchRequestor#acceptSearchMatch(org .eclipse.jdt.core.search.SearchMatch)
         */
        @Override
        public void acceptSearchMatch(SearchMatch match) {
            IJavaElement enclosingElement = (IJavaElement) match.getElement();
            if ((enclosingElement != null) && (packageNames.contains(enclosingElement.getElementName()))) {
                packages.add(enclosingElement);
            }
        }
    }

    /**
     * Collect all type declarations in the packages of the component
     */
    public class TypeCollector extends SearchRequestor
    // implements IJavaSearchResultCollector
    {

        Map<String, Set<String>> store = null;

        public TypeCollector(Map<String, Set<String>> store) {
            this.store = store;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jdt.core.search.SearchRequestor#acceptSearchMatch(org .eclipse.jdt.core.search.SearchMatch)
         */
        @Override
        public void acceptSearchMatch(SearchMatch match) {
            IJavaElement enclosingElement = (IJavaElement) match.getElement();
            try {
                if ((enclosingElement != null) && (enclosingElement.getElementType() == IJavaElement.TYPE)) {
                    String packName = enclosingElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT).getElementName();
                    Set<String> deps = new HashSet<String>();
                    store.put(enclosingElement.getHandleIdentifier(), deps);
                    Set<String> typesInPackage = packages.get(packName);
                    if (typesInPackage == null) {
                        typesInPackage = new HashSet<String>();
                        packages.put(packName, typesInPackage);
                    }
                    typesInPackage.add(enclosingElement.getHandleIdentifier());
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Collect all type references in the packages of the component. Only collect references to/from classes in these packages (both to AND from must be in the scope)
     */
    public class RefCollector extends SearchRequestor {

        Set<String> store = null;
        Set<String> types = null;
        IJavaElement from = null;
        IJavaElement fromPackage = null;

        public RefCollector(Set<String> store, Set<String> handles, IJavaElement from) {
            this.store = store;
            this.types = handles;
            this.from = from;
            this.fromPackage = from.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jdt.core.search.SearchRequestor#acceptSearchMatch(org .eclipse.jdt.core.search.SearchMatch)
         */
        @Override
        public void acceptSearchMatch(SearchMatch match) throws CoreException {
            IJavaElement enclosingElement = (IJavaElement) match.getElement();
            if ((enclosingElement != null) && (enclosingElement.getElementType() == IJavaElement.TYPE)) {
                // found type declaration is of one of the types we want
                if (types.contains(enclosingElement.getHandleIdentifier())) {
                    // it's in a different package than the from type
                    // if
                    // (!fromPackage.equals(enclosingElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT)))
                    // {
                    store.add(enclosingElement.getHandleIdentifier());
                    // }
                }
            }
        }
    }

}