com.windowtester.eclipse.ui.convert.WTAPIUsage.java Source code

Java tutorial

Introduction

Here is the source code for com.windowtester.eclipse.ui.convert.WTAPIUsage.java

Source

/*******************************************************************************
 *  Copyright (c) 2012 Google, Inc.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *  
 *  Contributors:
 *  Google, Inc. - initial API and implementation
 *******************************************************************************/
package com.windowtester.eclipse.ui.convert;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.Map.Entry;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;

import com.windowtester.eclipse.ui.convert.preprocessor.BundleManifestReader;

/**
 * Build a collection of all WindowTester API used by the selected project/package/class.
 * First {@link #scan(List, IProgressMonitor)} then {@link #getAPIUsageText()}.
 */
public class WTAPIUsage {
    private Collection<IJavaElement> visited = new HashSet<IJavaElement>(1000);
    private Map<String, Integer> apiUsed = new HashMap<String, Integer>(1000);
    private Collection<IJavaProject> projects = new HashSet<IJavaProject>(10);
    private int compUnitCount;
    private int wtCompUnitCount;
    private int exceptionCount;
    private Exception firstException;
    private String sourceWithException;

    //==============================================================================
    // Scanning

    /**
     * Recursively iterate over the specified java elements and their children to convert
     * each compilation to use the new WindowTester API.
     * 
     * @param elements a collection of {@link IJavaElement}s to be converted (not
     *            <code>null</code> and contains no <code>null</code>s)
     * @param monitor the progress monitor (not <code>null</code>)
     * @return a collection of API signatures
     */
    public void scan(List<IJavaElement> elements, IProgressMonitor monitor) throws JavaModelException {
        compUnitCount = 0;
        wtCompUnitCount = 0;
        exceptionCount = 0;
        firstException = null;
        sourceWithException = null;
        monitor.beginTask("Scanning WindowTester tests", elements.size());
        for (Iterator<IJavaElement> iter = elements.iterator(); iter.hasNext();) {
            scanElement(iter.next(), new SubProgressMonitor(monitor, 1));
            monitor.worked(1);
        }
        monitor.done();
    }

    /**
     * Recursively iterate over the specified java element and their children to convert
     * each compilation to use the new WindowTester API.
     * 
     * @param elem the java element (not <code>null</code>)
     * @param monitor the progress monitor (not <code>null</code>)
     */
    private void scanElement(IJavaElement elem, IProgressMonitor monitor) throws JavaModelException {
        projects.add(elem.getJavaProject());
        switch (elem.getElementType()) {
        case IJavaElement.JAVA_PROJECT:
            scanProject((IJavaProject) elem, monitor);
            break;
        case IJavaElement.PACKAGE_FRAGMENT_ROOT:
            scanPackageRoot((IPackageFragmentRoot) elem, monitor);
            break;
        case IJavaElement.PACKAGE_FRAGMENT:
            scanPackage((IPackageFragment) elem, monitor);
            break;
        case IJavaElement.COMPILATION_UNIT:
            scanCompilationUnit((ICompilationUnit) elem);
            break;
        default:
            break;
        }
    }

    /**
     * Recursively iterate over the elements in the specified java element to convert each
     * compilation to use the new WindowTester API.
     * 
     * @param proj the java project (not <code>null</code>)
     * @param monitor the progress monitor (not <code>null</code>)
     */
    private void scanProject(IJavaProject proj, IProgressMonitor monitor) throws JavaModelException {
        if (visited.contains(proj))
            return;
        visited.add(proj);
        IPackageFragmentRoot[] roots = proj.getPackageFragmentRoots();
        monitor.beginTask("Scanning " + proj.getPath(), roots.length);
        for (int i = 0; i < roots.length; i++) {
            scanPackageRoot(roots[i], new SubProgressMonitor(monitor, 1));
            monitor.worked(1);
        }
        monitor.done();
    }

    /**
     * Recursively iterate over the elements in the specified java element to convert each
     * compilation to use the new WindowTester API.
     * 
     * @param root the package fragment root (not <code>null</code>)
     * @param monitor the progress monitor (not <code>null</code>)
     */
    private void scanPackageRoot(IPackageFragmentRoot root, IProgressMonitor monitor) throws JavaModelException {
        if (root.getKind() != IPackageFragmentRoot.K_SOURCE || visited.contains(root))
            return;
        visited.add(root);
        IJavaElement[] children = root.getChildren();
        monitor.beginTask("Scanning " + root.getPath(), children.length);
        for (int i = 0; i < children.length; i++) {
            scanElement(children[i], new SubProgressMonitor(monitor, 1));
            monitor.worked(1);
        }
        monitor.done();
    }

    /**
     * Recursively iterate over the elements in the specified java element to convert each
     * compilation to use the new WindowTester API.
     * 
     * @param pkg the package fragment (not <code>null</code>)
     * @param monitor the progress monitor (not <code>null</code>)
     */
    private void scanPackage(IPackageFragment pkg, IProgressMonitor monitor) throws JavaModelException {
        if (visited.contains(pkg))
            return;
        visited.add(pkg);
        IJavaElement[] children = pkg.getChildren();
        monitor.beginTask("Scanning " + pkg.getPath(), children.length);
        for (int i = 0; i < children.length; i++) {
            if (monitor.isCanceled())
                throw new OperationCanceledException();
            scanElement(children[i], new SubProgressMonitor(monitor, 1));
            monitor.worked(1);
        }
        monitor.done();
    }

    /**
     * Convert the specified compilation unit to use the new WindowTester API.
     * 
     * @param compUnit the compilation unit (not <code>null</code>)
     */
    private void scanCompilationUnit(ICompilationUnit compUnit) throws JavaModelException {
        if (visited.contains(compUnit))
            return;
        visited.add(compUnit);
        compUnitCount++;
        try {
            parseScanResult(new WTConvertAPIContextBuilder().buildContext(compUnit));
        } catch (Exception e) {
            exceptionCount++;
            if (firstException == null) {
                firstException = e;
                sourceWithException = compUnit.getSource();
            }
        }
    }

    /**
     * Convert the specified compilation unit's source. This is used for testing purposes
     * and typically not called outside this class during the normal course of events
     * 
     * @param source the compilation unit source
     * @return the context containing the converted source (not <code>null</code>)
     */
    public void scanCompilationUnitSource(String source) {
        parseScanResult(new WTConvertAPIContextBuilder().buildContext(source));
    }

    /**
     * Fold the results of the API scan into the report
     * 
     * @param context the scan result (not <code>null</code>)
     */
    private void parseScanResult(WTConvertAPIContext context) {
        if (context.getWTTypeNames().size() > 0) {
            wtCompUnitCount++;
            context.accept(new WTAPIUsageVisitor(context) {
                public void apiUsed(String signature) {
                    Integer count = apiUsed.get(signature);
                    apiUsed.put(signature, (count != null ? count : 0) + 1);
                }
            });
        }
    }

    //==============================================================================================
    // Reporting

    /**
     * Return text describing what WindowTester APIs were detected
     * 
     * @return a String (not <code>null</code>)
     */
    public String getAPIUsageText() {
        StringWriter stringWriter = new StringWriter(2000);
        PrintWriter writer = new PrintWriter(stringWriter);
        printHeader(writer);
        printReferencedAPI(writer);
        printReferencedPlugins(writer);
        printExceptionIfAny(writer);
        return stringWriter.toString();
    }

    private void printHeader(PrintWriter writer) {
        writer.println();
        writer.println("WindowTester API Usage Report");
        writer.println("==========================================================");
        printInt(writer, projects.size());
        writer.println("projects scanned");
        printInt(writer, compUnitCount);
        writer.println("compilation units scanned");
        printInt(writer, wtCompUnitCount);
        writer.println("compilation units scanned that referenced WindowTester types");
        if (exceptionCount > 0) {
            printInt(writer, exceptionCount);
            writer.println("exceptions while scanning source");
        }
    }

    private void printReferencedAPI(PrintWriter writer) {
        TreeSet<Map.Entry<String, Integer>> sorted = new TreeSet<Map.Entry<String, Integer>>(
                new Comparator<Map.Entry<String, Integer>>() {
                    public int compare(Entry<String, Integer> entry1, Entry<String, Integer> entry2) {
                        String line1 = entry1.getKey();
                        String line2 = entry2.getKey();
                        String type1 = line1.substring(0, line1.indexOf('#'));
                        String type2 = line2.substring(0, line2.indexOf('#'));
                        String package1 = type1.substring(0, type1.lastIndexOf('.'));
                        String package2 = type2.substring(0, type2.lastIndexOf('.'));
                        int delta = package1.compareToIgnoreCase(package2);
                        if (delta == 0) {
                            delta = type1.compareToIgnoreCase(type2);
                            if (delta == 0)
                                delta = line1.compareToIgnoreCase(line2);
                        }
                        return delta;
                    }
                });
        sorted.addAll(apiUsed.entrySet());

        writer.println();
        writer.println("API Usage:");
        for (Map.Entry<String, Integer> entry : sorted) {
            Integer value = entry.getValue();
            printInt(writer, value);
            writer.println(entry.getKey());
        }
    }

    private void printReferencedPlugins(PrintWriter writer) {
        Collection<String> pluginIds = collectReferencedPlugins(writer);
        writer.println();
        writer.println("Plugins:");
        for (String id : new TreeSet<String>(pluginIds)) {
            writer.print("   ");
            writer.println(id);
        }
    }

    private Collection<String> collectReferencedPlugins(PrintWriter writer) {
        Collection<String> pluginIds = new HashSet<String>();
        try {
            for (IJavaProject proj : projects)
                for (IClasspathEntry entry : proj.getRawClasspath())
                    collectPluginsReferencedByClasspathEntry(writer, pluginIds, proj, entry);
        } catch (JavaModelException e) {
            writer.println();
            e.printStackTrace(writer);
        } catch (IOException e) {
            writer.println();
            e.printStackTrace(writer);
        }
        return pluginIds;
    }

    private void collectPluginsReferencedByClasspathEntry(PrintWriter writer, Collection<String> pluginIds,
            IJavaProject proj, IClasspathEntry entry) throws IOException {
        IPath path = entry.getPath();
        switch (entry.getEntryKind()) {

        case IClasspathEntry.CPE_LIBRARY:
        case IClasspathEntry.CPE_VARIABLE:
            for (int i = path.segmentCount() - 1; i >= 0; i--) {
                String segment = path.segment(i);
                if (segment.startsWith("com.windowtester.")) {
                    String id = segment;
                    i++;
                    while (i < path.segmentCount())
                        id += "/" + path.segment(i++);
                    pluginIds.add(id);
                    break;
                }
            }
            break;

        case IClasspathEntry.CPE_CONTAINER:
            if (path.segmentCount() >= 1 && path.segment(0).equals("org.eclipse.pde.core.requiredPlugins"))
                collectPluginsReferencedInManifest(pluginIds, proj);
            break;

        case IClasspathEntry.CPE_SOURCE:
        case IClasspathEntry.CPE_PROJECT:
            // ignored
            break;

        default:
            pluginIds.add("unknown " + entry.getEntryKind() + " - " + entry);
            break;
        }
    }

    private void collectPluginsReferencedInManifest(Collection<String> pluginIds, IJavaProject proj)
            throws IOException {
        IPath location = proj.getResource().getLocation();
        if (location != null) {
            File pluginXmlFile = null;
            for (File dir : location.toFile().listFiles()) {
                if (!dir.isDirectory() || !dir.getName().equalsIgnoreCase("META-INF"))
                    continue;
                for (File file : dir.listFiles()) {
                    String name = file.getName();
                    if (!name.equalsIgnoreCase("MANIFEST.MF")) {
                        if (name.equalsIgnoreCase("plugin.xml"))
                            pluginXmlFile = file;
                        continue;
                    }
                    BundleManifestReader reader = new BundleManifestReader();
                    reader.process(file);
                    for (String id : reader.getRequiredPlugins()) {
                        if (id.startsWith("com.windowtester."))
                            pluginIds.add(id);
                    }
                    return;
                }
            }
            if (pluginXmlFile != null)
                pluginIds.add("unknown plugins referenced in " + proj.getResource().getName() + "/"
                        + pluginXmlFile.getName());
        }
    }

    private void printExceptionIfAny(PrintWriter writer) {
        if (firstException != null) {
            writer.println();
            writer.println("==========================================================");
            firstException.printStackTrace(writer);
            writer.println("==========================================================");
            writer.println(sourceWithException);
        }
    }

    private void printInt(PrintWriter writer, int value) {
        String count = Integer.toString(value);
        for (int i = 5 - count.length(); i > 0; i--)
            writer.print(" ");
        writer.print(count);
        writer.print(" ");
    }
}