com.google.devtools.cyclefinder.CycleFinder.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.cyclefinder.CycleFinder.java

Source

/*
 * 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 com.google.devtools.cyclefinder;

import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.file.RegularInputFile;
import com.google.devtools.j2objc.jdt.TreeConverter;
import com.google.devtools.j2objc.pipeline.J2ObjCIncompatibleStripper;
import com.google.devtools.j2objc.translate.OuterReferenceResolver;
import com.google.devtools.j2objc.util.ErrorUtil;
import com.google.devtools.j2objc.util.FileUtil;
import com.google.devtools.j2objc.util.JdtParser;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;

/**
 * A tool for finding possible reference cycles in a Java program.
 *
 * @author Keith Stanger
 */
public class CycleFinder {

    private final Options options;

    static {
        // Enable assertions in the cycle finder.
        ClassLoader loader = CycleFinder.class.getClassLoader();
        if (loader != null) {
            loader.setPackageAssertionStatus(CycleFinder.class.getPackage().getName(), true);
        }
    }

    public CycleFinder(Options options) throws IOException {
        this.options = options;
        com.google.devtools.j2objc.Options.load(
                new String[] { "-encoding", options.fileEncoding(), "-source", options.sourceVersion().flag() });
    }

    private static JdtParser createParser(Options options) {
        JdtParser parser = new JdtParser();
        parser.addSourcepathEntries(Strings.nullToEmpty(options.getSourcepath()));
        parser.addClasspathEntries(Strings.nullToEmpty(options.getBootclasspath()));
        parser.addClasspathEntries(Strings.nullToEmpty(options.getClasspath()));
        parser.setEncoding(options.fileEncoding());
        return parser;
    }

    private static void exitOnErrors() {
        int nErrors = ErrorUtil.errorCount();
        if (nErrors > 0) {
            System.err.println("Failed with " + nErrors + " errors:");
            for (String error : ErrorUtil.getErrorMessages()) {
                System.err.println("error: " + error);
            }
            System.exit(nErrors);
        }
    }

    private void testFileExistence() {
        for (String filePath : options.getSourceFiles()) {
            File f = new File(filePath);
            if (!f.exists()) {
                ErrorUtil.error("File not found: " + filePath);
            }
        }
    }

    private NameList getBlacklist() throws IOException {
        List<String> blackListFiles = options.getBlacklistFiles();
        if (blackListFiles.isEmpty()) {
            return null;
        }
        return NameList.createFromFiles(blackListFiles);
    }

    private File stripIncompatible(List<String> sourceFileNames, JdtParser parser) throws IOException {
        File strippedDir = null;
        for (int i = 0; i < sourceFileNames.size(); i++) {
            String fileName = sourceFileNames.get(i);
            RegularInputFile file = new RegularInputFile(fileName);
            String source = FileUtil.readFile(file);
            if (!source.contains("J2ObjCIncompatible")) {
                continue;
            }
            if (strippedDir == null) {
                strippedDir = Files.createTempDir();
                parser.prependSourcepathEntry(strippedDir.getPath());
            }
            org.eclipse.jdt.core.dom.CompilationUnit unit = parser.parseWithoutBindings(fileName, source);
            String qualifiedName = FileUtil.getQualifiedMainTypeName(file, unit);
            String newSource = J2ObjCIncompatibleStripper.strip(source, unit);
            String relativePath = qualifiedName.replace('.', File.separatorChar) + ".java";
            File strippedFile = new File(strippedDir, relativePath);
            Files.createParentDirs(strippedFile);
            Files.write(newSource, strippedFile, Charset.forName(options.fileEncoding()));
            sourceFileNames.set(i, strippedFile.getPath());
        }
        return strippedDir;
    }

    public List<List<Edge>> findCycles() throws IOException {
        final TypeCollector typeCollector = new TypeCollector();
        JdtParser parser = createParser(options);
        final OuterReferenceResolver outerResolver = new OuterReferenceResolver();

        List<String> sourceFiles = options.getSourceFiles();
        File strippedDir = stripIncompatible(sourceFiles, parser);

        JdtParser.Handler handler = new JdtParser.Handler() {
            @Override
            public void handleParsedUnit(String path, org.eclipse.jdt.core.dom.CompilationUnit jdtUnit) {
                String source = "";
                RegularInputFile file = new RegularInputFile(path);
                try {
                    source = FileUtil.readFile(file);
                } catch (IOException e) {
                    ErrorUtil.error("Error reading file " + path + ": " + e.getMessage());
                }
                CompilationUnit unit = TreeConverter.convertCompilationUnit(jdtUnit, path,
                        FileUtil.getMainTypeName(file), source, null);
                typeCollector.visitAST(unit);
                outerResolver.run(unit);
            }
        };
        parser.parseFiles(sourceFiles, handler, options.sourceVersion());

        FileUtil.deleteTempDir(strippedDir);

        if (ErrorUtil.errorCount() > 0) {
            return null;
        }

        // Construct the graph and find cycles.
        ReferenceGraph graph = new ReferenceGraph(typeCollector, outerResolver,
                NameList.createFromFiles(options.getWhitelistFiles()), getBlacklist());
        return graph.findCycles();
    }

    public static void printCycles(Collection<? extends Iterable<Edge>> cycles, PrintStream out) {
        for (Iterable<Edge> cycle : cycles) {
            out.println();
            out.println("***** Found reference cycle *****");
            for (Edge e : cycle) {
                out.println(e.toString());
            }
            out.println("----- Full Types -----");
            for (Edge e : cycle) {
                out.println(e.getOrigin().getKey());
            }
        }
        out.println();
        out.println(cycles.size() + " CYCLES FOUND.");
    }

    public static void main(String[] args) throws IOException {
        if (args.length == 0) {
            Options.help(true);
        }
        Options options = Options.parse(args);
        CycleFinder finder = new CycleFinder(options);
        finder.testFileExistence();
        exitOnErrors();
        List<List<Edge>> cycles = finder.findCycles();
        exitOnErrors();
        printCycles(cycles, System.out);
        System.exit(ErrorUtil.errorCount() + cycles.size());
    }
}