alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (JdepsTask.java)

This example Java source code file (JdepsTask.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

archive, arraylist, badargs, edge, exit_cmderr, hiddenoption, ioexception, list, object, option, override, path, profile, regex, string, text, util

The JdepsTask.java Java example source code

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.tools.jdeps;

import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies;
import com.sun.tools.classfile.Dependencies.ClassFileError;
import com.sun.tools.classfile.Dependency;
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * Implementation for the jdeps tool for static class dependency analysis.
 */
class JdepsTask {
    static class BadArgs extends Exception {
        static final long serialVersionUID = 8765093759964640721L;
        BadArgs(String key, Object... args) {
            super(JdepsTask.getMessage(key, args));
            this.key = key;
            this.args = args;
        }

        BadArgs showUsage(boolean b) {
            showUsage = b;
            return this;
        }
        final String key;
        final Object[] args;
        boolean showUsage;
    }

    static abstract class Option {
        Option(boolean hasArg, String... aliases) {
            this.hasArg = hasArg;
            this.aliases = aliases;
        }

        boolean isHidden() {
            return false;
        }

        boolean matches(String opt) {
            for (String a : aliases) {
                if (a.equals(opt))
                    return true;
                if (hasArg && opt.startsWith(a + "="))
                    return true;
            }
            return false;
        }

        boolean ignoreRest() {
            return false;
        }

        abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
        final boolean hasArg;
        final String[] aliases;
    }

    static abstract class HiddenOption extends Option {
        HiddenOption(boolean hasArg, String... aliases) {
            super(hasArg, aliases);
        }

        boolean isHidden() {
            return true;
        }
    }

    static Option[] recognizedOptions = {
        new Option(false, "-h", "-?", "-help") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.help = true;
            }
        },
        new Option(true, "-dotoutput") {
            void process(JdepsTask task, String opt, String arg) throws BadArgs {
                Path p = Paths.get(arg);
                if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
                    throw new BadArgs("err.dot.output.path", arg);
                }
                task.options.dotOutputDir = arg;
            }
        },
        new Option(false, "-s", "-summary") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.showSummary = true;
                task.options.verbose = Analyzer.Type.SUMMARY;
            }
        },
        new Option(false, "-v", "-verbose",
                          "-verbose:package",
                          "-verbose:class")
        {
            void process(JdepsTask task, String opt, String arg) throws BadArgs {
                switch (opt) {
                    case "-v":
                    case "-verbose":
                        task.options.verbose = Analyzer.Type.VERBOSE;
                        break;
                    case "-verbose:package":
                            task.options.verbose = Analyzer.Type.PACKAGE;
                            break;
                    case "-verbose:class":
                            task.options.verbose = Analyzer.Type.CLASS;
                            break;
                    default:
                        throw new BadArgs("err.invalid.arg.for.option", opt);
                }
            }
        },
        new Option(true, "-cp", "-classpath") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.classpath = arg;
            }
        },
        new Option(true, "-p", "-package") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.packageNames.add(arg);
            }
        },
        new Option(true, "-e", "-regex") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.regex = arg;
            }
        },
        new Option(true, "-include") {
            void process(JdepsTask task, String opt, String arg) throws BadArgs {
                task.options.includePattern = Pattern.compile(arg);
            }
        },
        new Option(false, "-P", "-profile") {
            void process(JdepsTask task, String opt, String arg) throws BadArgs {
                task.options.showProfile = true;
                if (Profile.getProfileCount() == 0) {
                    throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg"));
                }
            }
        },
        new Option(false, "-apionly") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.apiOnly = true;
            }
        },
        new Option(false, "-R", "-recursive") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.depth = 0;
            }
        },
        new Option(false, "-jdkinternals") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.findJDKInternals = true;
                task.options.verbose = Analyzer.Type.CLASS;
                if (task.options.includePattern == null) {
                    task.options.includePattern = Pattern.compile(".*");
                }
            }
        },
        new Option(false, "-version") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.version = true;
            }
        },
        new HiddenOption(false, "-fullversion") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.fullVersion = true;
            }
        },
        new HiddenOption(false, "-showlabel") {
            void process(JdepsTask task, String opt, String arg) {
                task.options.showLabel = true;
            }
        },
        new HiddenOption(true, "-depth") {
            void process(JdepsTask task, String opt, String arg) throws BadArgs {
                try {
                    task.options.depth = Integer.parseInt(arg);
                } catch (NumberFormatException e) {
                    throw new BadArgs("err.invalid.arg.for.option", opt);
                }
            }
        },
    };

    private static final String PROGNAME = "jdeps";
    private final Options options = new Options();
    private final List<String> classes = new ArrayList();

    private PrintWriter log;
    void setLog(PrintWriter out) {
        log = out;
    }

    /**
     * Result codes.
     */
    static final int EXIT_OK = 0, // Completed with no errors.
                     EXIT_ERROR = 1, // Completed but reported errors.
                     EXIT_CMDERR = 2, // Bad command-line arguments
                     EXIT_SYSERR = 3, // System error or resource exhaustion.
                     EXIT_ABNORMAL = 4;// terminated abnormally

    int run(String[] args) {
        if (log == null) {
            log = new PrintWriter(System.out);
        }
        try {
            handleOptions(args);
            if (options.help) {
                showHelp();
            }
            if (options.version || options.fullVersion) {
                showVersion(options.fullVersion);
            }
            if (classes.isEmpty() && options.includePattern == null) {
                if (options.help || options.version || options.fullVersion) {
                    return EXIT_OK;
                } else {
                    showHelp();
                    return EXIT_CMDERR;
                }
            }
            if (options.regex != null && options.packageNames.size() > 0) {
                showHelp();
                return EXIT_CMDERR;
            }
            if (options.findJDKInternals &&
                   (options.regex != null || options.packageNames.size() > 0 || options.showSummary)) {
                showHelp();
                return EXIT_CMDERR;
            }
            if (options.showSummary && options.verbose != Analyzer.Type.SUMMARY) {
                showHelp();
                return EXIT_CMDERR;
            }
            boolean ok = run();
            return ok ? EXIT_OK : EXIT_ERROR;
        } catch (BadArgs e) {
            reportError(e.key, e.args);
            if (e.showUsage) {
                log.println(getMessage("main.usage.summary", PROGNAME));
            }
            return EXIT_CMDERR;
        } catch (IOException e) {
            return EXIT_ABNORMAL;
        } finally {
            log.flush();
        }
    }

    private final List<Archive> sourceLocations = new ArrayList<>();
    private boolean run() throws IOException {
        findDependencies();
        Analyzer analyzer = new Analyzer(options.verbose);
        analyzer.run(sourceLocations);
        if (options.dotOutputDir != null) {
            Path dir = Paths.get(options.dotOutputDir);
            Files.createDirectories(dir);
            generateDotFiles(dir, analyzer);
        } else {
            printRawOutput(log, analyzer);
        }
        return true;
    }

    private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
        Path summary = dir.resolve("summary.dot");
        boolean verbose = options.verbose == Analyzer.Type.VERBOSE;
        DotGraph<?> graph = verbose ? new DotSummaryForPackage()
                                    : new DotSummaryForArchive();
        for (Archive archive : sourceLocations) {
            analyzer.visitArchiveDependences(archive, graph);
            if (verbose || options.showLabel) {
                // traverse detailed dependences to generate package-level
                // summary or build labels for edges
                analyzer.visitDependences(archive, graph);
            }
        }
        try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) {
            graph.writeTo(sw);
        }
        // output individual .dot file for each archive
        if (options.verbose != Analyzer.Type.SUMMARY) {
            for (Archive archive : sourceLocations) {
                if (analyzer.hasDependences(archive)) {
                    Path dotfile = dir.resolve(archive.getFileName() + ".dot");
                    try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
                         DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
                        analyzer.visitDependences(archive, formatter);
                    }
                }
            }
        }
    }

    private void printRawOutput(PrintWriter writer, Analyzer analyzer) {
        for (Archive archive : sourceLocations) {
            RawOutputFormatter formatter = new RawOutputFormatter(writer);
            analyzer.visitArchiveDependences(archive, formatter);
            if (options.verbose != Analyzer.Type.SUMMARY) {
                analyzer.visitDependences(archive, formatter);
            }
        }
    }
    private boolean isValidClassName(String name) {
        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
            return false;
        }
        for (int i=1; i < name.length(); i++) {
            char c = name.charAt(i);
            if (c != '.'  && !Character.isJavaIdentifierPart(c)) {
                return false;
            }
        }
        return true;
    }

    private Dependency.Filter getDependencyFilter() {
         if (options.regex != null) {
            return Dependencies.getRegexFilter(Pattern.compile(options.regex));
        } else if (options.packageNames.size() > 0) {
            return Dependencies.getPackageFilter(options.packageNames, false);
        } else {
            return new Dependency.Filter() {
                @Override
                public boolean accepts(Dependency dependency) {
                    return !dependency.getOrigin().equals(dependency.getTarget());
                }
            };
        }
    }

    private boolean matches(String classname, AccessFlags flags) {
        if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) {
            return false;
        } else if (options.includePattern != null) {
            return options.includePattern.matcher(classname.replace('/', '.')).matches();
        } else {
            return true;
        }
    }

    private void findDependencies() throws IOException {
        Dependency.Finder finder =
            options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
                            : Dependencies.getClassDependencyFinder();
        Dependency.Filter filter = getDependencyFilter();

        List<Archive> archives = new ArrayList<>();
        Deque<String> roots = new LinkedList<>();
        for (String s : classes) {
            Path p = Paths.get(s);
            if (Files.exists(p)) {
                archives.add(new Archive(p, ClassFileReader.newInstance(p)));
            } else {
                if (isValidClassName(s)) {
                    roots.add(s);
                } else {
                    warning("warn.invalid.arg", s);
                }
            }
        }
        sourceLocations.addAll(archives);

        List<Archive> classpaths = new ArrayList<>(); // for class file lookup
        classpaths.addAll(getClassPathArchives(options.classpath));
        if (options.includePattern != null) {
            archives.addAll(classpaths);
        }
        classpaths.addAll(PlatformClassPath.getArchives());

        // add all classpath archives to the source locations for reporting
        sourceLocations.addAll(classpaths);

        // Work queue of names of classfiles to be searched.
        // Entries will be unique, and for classes that do not yet have
        // dependencies in the results map.
        Deque<String> deque = new LinkedList<>();
        Set<String> doneClasses = new HashSet<>();

        // get the immediate dependencies of the input files
        for (Archive a : archives) {
            for (ClassFile cf : a.reader().getClassFiles()) {
                String classFileName;
                try {
                    classFileName = cf.getName();
                } catch (ConstantPoolException e) {
                    throw new ClassFileError(e);
                }

                if (matches(classFileName, cf.access_flags)) {
                    if (!doneClasses.contains(classFileName)) {
                        doneClasses.add(classFileName);
                    }
                    for (Dependency d : finder.findDependencies(cf)) {
                        if (filter.accepts(d)) {
                            String cn = d.getTarget().getName();
                            if (!doneClasses.contains(cn) && !deque.contains(cn)) {
                                deque.add(cn);
                            }
                            a.addClass(d.getOrigin(), d.getTarget());
                        }
                    }
                }
            }
        }

        // add Archive for looking up classes from the classpath
        // for transitive dependency analysis
        Deque<String> unresolved = roots;
        int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE;
        do {
            String name;
            while ((name = unresolved.poll()) != null) {
                if (doneClasses.contains(name)) {
                    continue;
                }
                ClassFile cf = null;
                for (Archive a : classpaths) {
                    cf = a.reader().getClassFile(name);
                    if (cf != null) {
                        String classFileName;
                        try {
                            classFileName = cf.getName();
                        } catch (ConstantPoolException e) {
                            throw new ClassFileError(e);
                        }
                        if (!doneClasses.contains(classFileName)) {
                            // if name is a fully-qualified class name specified
                            // from command-line, this class might already be parsed
                            doneClasses.add(classFileName);
                            for (Dependency d : finder.findDependencies(cf)) {
                                if (depth == 0) {
                                    // ignore the dependency
                                    a.addClass(d.getOrigin());
                                    break;
                                } else if (filter.accepts(d)) {
                                    a.addClass(d.getOrigin(), d.getTarget());
                                    String cn = d.getTarget().getName();
                                    if (!doneClasses.contains(cn) && !deque.contains(cn)) {
                                        deque.add(cn);
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
                if (cf == null) {
                    doneClasses.add(name);
                }
            }
            unresolved = deque;
            deque = new LinkedList<>();
        } while (!unresolved.isEmpty() && depth-- > 0);
    }

    public void handleOptions(String[] args) throws BadArgs {
        // process options
        for (int i=0; i < args.length; i++) {
            if (args[i].charAt(0) == '-') {
                String name = args[i];
                Option option = getOption(name);
                String param = null;
                if (option.hasArg) {
                    if (name.startsWith("-") && name.indexOf('=') > 0) {
                        param = name.substring(name.indexOf('=') + 1, name.length());
                    } else if (i + 1 < args.length) {
                        param = args[++i];
                    }
                    if (param == null || param.isEmpty() || param.charAt(0) == '-') {
                        throw new BadArgs("err.missing.arg", name).showUsage(true);
                    }
                }
                option.process(this, name, param);
                if (option.ignoreRest()) {
                    i = args.length;
                }
            } else {
                // process rest of the input arguments
                for (; i < args.length; i++) {
                    String name = args[i];
                    if (name.charAt(0) == '-') {
                        throw new BadArgs("err.option.after.class", name).showUsage(true);
                    }
                    classes.add(name);
                }
            }
        }
    }

    private Option getOption(String name) throws BadArgs {
        for (Option o : recognizedOptions) {
            if (o.matches(name)) {
                return o;
            }
        }
        throw new BadArgs("err.unknown.option", name).showUsage(true);
    }

    private void reportError(String key, Object... args) {
        log.println(getMessage("error.prefix") + " " + getMessage(key, args));
    }

    private void warning(String key, Object... args) {
        log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
    }

    private void showHelp() {
        log.println(getMessage("main.usage", PROGNAME));
        for (Option o : recognizedOptions) {
            String name = o.aliases[0].substring(1); // there must always be at least one name
            name = name.charAt(0) == '-' ? name.substring(1) : name;
            if (o.isHidden() || name.equals("h")) {
                continue;
            }
            log.println(getMessage("main.opt." + name));
        }
    }

    private void showVersion(boolean full) {
        log.println(version(full ? "full" : "release"));
    }

    private String version(String key) {
        // key=version:  mm.nn.oo[-milestone]
        // key=full:     mm.mm.oo[-milestone]-build
        if (ResourceBundleHelper.versionRB == null) {
            return System.getProperty("java.version");
        }
        try {
            return ResourceBundleHelper.versionRB.getString(key);
        } catch (MissingResourceException e) {
            return getMessage("version.unknown", System.getProperty("java.version"));
        }
    }

    static String getMessage(String key, Object... args) {
        try {
            return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
        } catch (MissingResourceException e) {
            throw new InternalError("Missing message: " + key);
        }
    }

    private static class Options {
        boolean help;
        boolean version;
        boolean fullVersion;
        boolean showProfile;
        boolean showSummary;
        boolean wildcard;
        boolean apiOnly;
        boolean showLabel;
        boolean findJDKInternals;
        String dotOutputDir;
        String classpath = "";
        int depth = 1;
        Analyzer.Type verbose = Analyzer.Type.PACKAGE;
        Set<String> packageNames = new HashSet<>();
        String regex;             // apply to the dependences
        Pattern includePattern;   // apply to classes
    }
    private static class ResourceBundleHelper {
        static final ResourceBundle versionRB;
        static final ResourceBundle bundle;

        static {
            Locale locale = Locale.getDefault();
            try {
                bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
            } catch (MissingResourceException e) {
                throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
            }
            try {
                versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
            } catch (MissingResourceException e) {
                throw new InternalError("version.resource.missing");
            }
        }
    }

    private List<Archive> getArchives(List filenames) throws IOException {
        List<Archive> result = new ArrayList();
        for (String s : filenames) {
            Path p = Paths.get(s);
            if (Files.exists(p)) {
                result.add(new Archive(p, ClassFileReader.newInstance(p)));
            } else {
                warning("warn.file.not.exist", s);
            }
        }
        return result;
    }

    private List<Archive> getClassPathArchives(String paths) throws IOException {
        List<Archive> result = new ArrayList<>();
        if (paths.isEmpty()) {
            return result;
        }
        for (String p : paths.split(File.pathSeparator)) {
            if (p.length() > 0) {
                List<Path> files = new ArrayList<>();
                // wildcard to parse all JAR files e.g. -classpath dir/*
                int i = p.lastIndexOf(".*");
                if (i > 0) {
                    Path dir = Paths.get(p.substring(0, i));
                    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
                        for (Path entry : stream) {
                            files.add(entry);
                        }
                    }
                } else {
                    files.add(Paths.get(p));
                }
                for (Path f : files) {
                    if (Files.exists(f)) {
                        result.add(new Archive(f, ClassFileReader.newInstance(f)));
                    }
                }
            }
        }
        return result;
    }

    /**
     * If the given archive is JDK archive and non-null Profile,
     * this method returns the profile name only if -profile option is specified;
     * a null profile indicates it accesses a private JDK API and this method
     * will return "JDK internal API".
     *
     * For non-JDK archives, this method returns the file name of the archive.
     */
    private String getProfileArchiveInfo(Archive source, Profile profile) {
        if (options.showProfile && profile != null)
            return profile.toString();

        if (source instanceof JDKArchive) {
            return profile == null ? "JDK internal API (" + source.getFileName() + ")" : "";
        }
        return source.getFileName();
    }

    /**
     * Returns the profile name or "JDK internal API" for JDK archive;
     * otherwise empty string.
     */
    private String profileName(Archive archive, Profile profile) {
        if (archive instanceof JDKArchive) {
            return Objects.toString(profile, "JDK internal API");
        } else {
            return "";
        }
    }

    class RawOutputFormatter implements Analyzer.Visitor {
        private final PrintWriter writer;
        RawOutputFormatter(PrintWriter writer) {
            this.writer = writer;
        }

        private String pkg = "";
        @Override
        public void visitDependence(String origin, Archive source,
                                    String target, Archive archive, Profile profile) {
            if (options.findJDKInternals &&
                    !(archive instanceof JDKArchive && profile == null)) {
                // filter dependences other than JDK internal APIs
                return;
            }
            if (options.verbose == Analyzer.Type.VERBOSE) {
                writer.format("   %-50s -> %-50s %s%n",
                              origin, target, getProfileArchiveInfo(archive, profile));
            } else {
                if (!origin.equals(pkg)) {
                    pkg = origin;
                    writer.format("   %s (%s)%n", origin, source.getFileName());
                }
                writer.format("      -> %-50s %s%n",
                              target, getProfileArchiveInfo(archive, profile));
            }
        }

        @Override
        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
            writer.format("%s -> %s", origin.getPathName(), target.getPathName());
            if (options.showProfile && profile != null) {
                writer.format(" (%s)%n", profile);
            } else {
                writer.format("%n");
            }
        }
    }

    class DotFileFormatter extends DotGraph<String> implements AutoCloseable {
        private final PrintWriter writer;
        private final String name;
        DotFileFormatter(PrintWriter writer, Archive archive) {
            this.writer = writer;
            this.name = archive.getFileName();
            writer.format("digraph \"%s\" {%n", name);
            writer.format("    // Path: %s%n", archive.getPathName());
        }

        @Override
        public void close() {
            writer.println("}");
        }

        @Override
        public void visitDependence(String origin, Archive source,
                                    String target, Archive archive, Profile profile) {
            if (options.findJDKInternals &&
                    !(archive instanceof JDKArchive && profile == null)) {
                // filter dependences other than JDK internal APIs
                return;
            }
            // if -P option is specified, package name -> profile will
            // be shown and filter out multiple same edges.
            String name = getProfileArchiveInfo(archive, profile);
            writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile)));
        }
        @Override
        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
            throw new UnsupportedOperationException();
        }
    }

    class DotSummaryForArchive extends DotGraph<Archive> {
        @Override
        public void visitDependence(String origin, Archive source,
                                    String target, Archive archive, Profile profile) {
            Edge e = findEdge(source, archive);
            assert e != null;
            // add the dependency to the label if enabled and not compact1
            if (profile == Profile.COMPACT1) {
                return;
            }
            e.addLabel(origin, target, profileName(archive, profile));
        }
        @Override
        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
            // add an edge with the archive's name with no tag
            // so that there is only one node for each JDK archive
            // while there may be edges to different profiles
            Edge e = addEdge(origin, target, "");
            if (target instanceof JDKArchive) {
                // add a label to print the profile
                if (profile == null) {
                    e.addLabel("JDK internal API");
                } else if (options.showProfile && !options.showLabel) {
                    e.addLabel(profile.toString());
                }
            }
        }
    }

    // DotSummaryForPackage generates the summary.dot file for verbose mode
    // (-v or -verbose option) that includes all class dependencies.
    // The summary.dot file shows package-level dependencies.
    class DotSummaryForPackage extends DotGraph<String> {
        private String packageOf(String cn) {
            int i = cn.lastIndexOf('.');
            return i > 0 ? cn.substring(0, i) : "<unnamed>";
        }
        @Override
        public void visitDependence(String origin, Archive source,
                                    String target, Archive archive, Profile profile) {
            // add a package dependency edge
            String from = packageOf(origin);
            String to = packageOf(target);
            Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile));

            // add the dependency to the label if enabled and not compact1
            if (!options.showLabel || profile == Profile.COMPACT1) {
                return;
            }

            // trim the package name of origin to shorten the label
            int i = origin.lastIndexOf('.');
            String n1 = i < 0 ? origin : origin.substring(i+1);
            e.addLabel(n1, target, profileName(archive, profile));
        }
        @Override
        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
            // nop
        }
    }
    abstract class DotGraph<T> implements Analyzer.Visitor  {
        private final Set<Edge> edges = new LinkedHashSet<>();
        private Edge curEdge;
        public void writeTo(PrintWriter writer) {
            writer.format("digraph \"summary\" {%n");
            for (Edge e: edges) {
                writeEdge(writer, e);
            }
            writer.println("}");
        }

        void writeEdge(PrintWriter writer, Edge e) {
            writer.format("   %-50s -> \"%s\"%s;%n",
                          String.format("\"%s\"", e.from.toString()),
                          e.tag.isEmpty() ? e.to
                                          : String.format("%s (%s)", e.to, e.tag),
                          getLabel(e));
        }

        Edge addEdge(T origin, T target, String tag) {
            Edge e = new Edge(origin, target, tag);
            if (e.equals(curEdge)) {
                return curEdge;
            }

            if (edges.contains(e)) {
                for (Edge e1 : edges) {
                   if (e.equals(e1)) {
                       curEdge = e1;
                   }
                }
            } else {
                edges.add(e);
                curEdge = e;
            }
            return curEdge;
        }

        Edge findEdge(T origin, T target) {
            for (Edge e : edges) {
                if (e.from.equals(origin) && e.to.equals(target)) {
                    return e;
                }
            }
            return null;
        }

        String getLabel(Edge e) {
            String label = e.label.toString();
            return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label);
        }

        class Edge {
            final T from;
            final T to;
            final String tag;  // optional tag
            final StringBuilder label = new StringBuilder();
            Edge(T from, T to, String tag) {
                this.from = from;
                this.to = to;
                this.tag = tag;
            }
            void addLabel(String s) {
                label.append(s).append("\\n");
            }
            void addLabel(String origin, String target, String profile) {
                label.append(origin).append(" -> ").append(target);
                if (!profile.isEmpty()) {
                    label.append(" (" + profile + ")");
                }
                label.append("\\n");
            }
            @Override @SuppressWarnings("unchecked")
            public boolean equals(Object o) {
                if (o instanceof DotGraph<?>.Edge) {
                    DotGraph<?>.Edge e = (DotGraph.Edge)o;
                    return this.from.equals(e.from) &&
                           this.to.equals(e.to) &&
                           this.tag.equals(e.tag);
                }
                return false;
            }
            @Override
            public int hashCode() {
                int hash = 7;
                hash = 67 * hash + Objects.hashCode(this.from) +
                       Objects.hashCode(this.to) + Objects.hashCode(this.tag);
                return hash;
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java JdepsTask.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.