|
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.
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:
|