net.oneandone.maven.plugins.cycles.analyzer.ComponentAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for net.oneandone.maven.plugins.cycles.analyzer.ComponentAnalyzer.java

Source

/**
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.oneandone.maven.plugins.cycles.analyzer;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.SortedSet;
import java.util.TreeSet;

import net.oneandone.maven.plugins.cycles.classes.ClassDependency;
import net.oneandone.maven.plugins.cycles.graph.FeedbackArcSet;
import net.oneandone.maven.plugins.cycles.graph.GraphDotUtils;
import net.oneandone.maven.plugins.cycles.graph.GraphStringUtils;
import net.oneandone.maven.plugins.cycles.graph.InstabilityVertexEvaluator;
import net.oneandone.maven.plugins.cycles.graph.WeightedEdge;
import net.oneandone.maven.plugins.cycles.graph.WeightedEdgeComparator;

import com.google.common.base.Charsets;
import com.google.common.collect.Sets;
import com.google.common.io.Files;

import edu.uci.ics.jung.graph.DirectedGraph;

/**
 * Outputs the results of a {@link ComponentAnalysis} to a string.
 * 
 * @author chschmitz
 */
public final class ComponentAnalyzer {
    private File classDir;
    private String filterPrefix;
    private boolean shorten;
    private boolean writeDotFiles;
    private int cycleCount;
    private int packageDepth;
    private boolean showClassDeps;

    /**
     * @param classDir source of our class files
     * @param filterPrefix class name filter prefix
     * @param shorten whether to shorten the class names
     * @param writeDotFiles iff true, .dot files will be written
     * @param packageDepth prefix depth to which packages are aggregated 
     */
    public ComponentAnalyzer(File classDir, String filterPrefix, boolean shorten, boolean writeDotFiles,
            int packageDepth, boolean showClassDeps) {
        this.classDir = classDir;
        this.filterPrefix = filterPrefix;
        this.shorten = shorten;
        this.writeDotFiles = writeDotFiles;
        this.packageDepth = packageDepth;
        this.showClassDeps = showClassDeps;
    }

    /**
     * Perform the actual analysis.
     * 
     * @return human-readable output of the analysis results
     * @throws IOException if parsing the classes fails
     */
    public String analyze() throws IOException {
        ComponentAnalysis analysis = new ComponentAnalysis(classDir, filterPrefix, packageDepth);

        if (!analysis.hasNonTrivialComponents()) {
            return ("No package cycles.");
        }

        StringBuilder builder = new StringBuilder();
        printCycles(analysis.getStrongComponents(), builder);
        return builder.toString();
    }

    private void printCycles(Collection<DirectedGraph<String, WeightedEdge>> components, StringBuilder builder)
            throws IOException {
        cycleCount = 0;
        for (DirectedGraph<String, WeightedEdge> component : components) {
            if (component.getVertices().size() > 1) {
                printCycle(component, builder);
                writeCycleGraph(component);
                cycleCount++;
            }
        }
    }

    private void printCycle(DirectedGraph<String, WeightedEdge> component, StringBuilder builder) {
        builder.append(String.format("=== Cycle / strongly connected component (%d packages, %d dependencies)",
                component.getVertexCount(), component.getEdgeCount()));
        builder.append("\n");
        printPackages(component, builder);
        printDependencies(component, builder);
        printFeedbackArcs(component, builder);
    }

    private void writeCycleGraph(DirectedGraph<String, WeightedEdge> component) throws IOException {
        if (!writeDotFiles) {
            return;
        }
        File dotFile = new File(classDir.getParent(), "graph-" + cycleCount + ".dot");
        String dotString = GraphDotUtils.toDot(component, shorten);
        Files.write(dotString, dotFile, Charsets.UTF_8);
    }

    private void printFeedbackArcs(DirectedGraph<String, WeightedEdge> component, StringBuilder builder) {
        builder.append("\n= Cycle-breaking dependencies\n");
        for (WeightedEdge dependency : FeedbackArcSet.feedbackArcs(component,
                new InstabilityVertexEvaluator<String>())) {
            builder.append(GraphStringUtils.edgeToString(dependency, component, shorten));
            builder.append("\n");
        }
    }

    private void printDependencies(DirectedGraph<String, WeightedEdge> component, StringBuilder builder) {
        builder.append("\n= Dependencies\n");
        TreeSet<WeightedEdge> sortedEdges = Sets.newTreeSet(new WeightedEdgeComparator(component));
        sortedEdges.addAll(component.getEdges());
        for (WeightedEdge dependency : sortedEdges) {
            builder.append(GraphStringUtils.edgeToString(dependency, component, shorten));
            builder.append("\n");
            if (showClassDeps) {
                printClassDependencies(builder, dependency);
            }
        }
    }

    private void printClassDependencies(StringBuilder builder, WeightedEdge dependency) {
        SortedSet<String> depsStrs = Sets.newTreeSet();
        for (ClassDependency classDependency : dependency.getDependencies()) {
            depsStrs.add(GraphStringUtils.dependencyToString(classDependency, shorten));
        }
        for (String depsStr : depsStrs) {
            builder.append("\t[");
            builder.append(depsStr);
            builder.append("]\n");
        }
    }

    private void printPackages(DirectedGraph<String, WeightedEdge> component, StringBuilder builder) {
        builder.append("\n= Packages\n");
        for (String pkg : Sets.newTreeSet(component.getVertices())) {
            builder.append(GraphStringUtils.vertexToString(pkg, shorten));
            builder.append("\n");
        }
    }
}