src/share/classes/com/sun/tools/jdeps/JdepsTask.java

changeset 2139
defadd528513
parent 1648
a03c4a86ea2b
child 2172
aa91bc6e8480
equal deleted inserted replaced
2138:4d8af6fda907 2139:defadd528513
22 * or visit www.oracle.com if you need additional information or have any 22 * or visit www.oracle.com if you need additional information or have any
23 * questions. 23 * questions.
24 */ 24 */
25 package com.sun.tools.jdeps; 25 package com.sun.tools.jdeps;
26 26
27 import com.sun.tools.classfile.AccessFlags;
27 import com.sun.tools.classfile.ClassFile; 28 import com.sun.tools.classfile.ClassFile;
28 import com.sun.tools.classfile.ConstantPoolException; 29 import com.sun.tools.classfile.ConstantPoolException;
29 import com.sun.tools.classfile.Dependencies; 30 import com.sun.tools.classfile.Dependencies;
30 import com.sun.tools.classfile.Dependencies.ClassFileError; 31 import com.sun.tools.classfile.Dependencies.ClassFileError;
31 import com.sun.tools.classfile.Dependency; 32 import com.sun.tools.classfile.Dependency;
33 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
32 import java.io.*; 34 import java.io.*;
35 import java.nio.file.DirectoryStream;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
33 import java.text.MessageFormat; 39 import java.text.MessageFormat;
34 import java.util.*; 40 import java.util.*;
35 import java.util.regex.Pattern; 41 import java.util.regex.Pattern;
36 42
37 /** 43 /**
65 return false; 71 return false;
66 } 72 }
67 73
68 boolean matches(String opt) { 74 boolean matches(String opt) {
69 for (String a : aliases) { 75 for (String a : aliases) {
70 if (a.equals(opt)) { 76 if (a.equals(opt))
71 return true; 77 return true;
72 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) { 78 if (hasArg && opt.startsWith(a + "="))
73 return true; 79 return true;
74 }
75 } 80 }
76 return false; 81 return false;
77 } 82 }
78 83
79 boolean ignoreRest() { 84 boolean ignoreRest() {
94 return true; 99 return true;
95 } 100 }
96 } 101 }
97 102
98 static Option[] recognizedOptions = { 103 static Option[] recognizedOptions = {
99 new Option(false, "-h", "-?", "--help") { 104 new Option(false, "-h", "-?", "-help") {
100 void process(JdepsTask task, String opt, String arg) { 105 void process(JdepsTask task, String opt, String arg) {
101 task.options.help = true; 106 task.options.help = true;
102 } 107 }
103 }, 108 },
104 new Option(false, "-s", "--summary") { 109 new Option(true, "-dotoutput") {
110 void process(JdepsTask task, String opt, String arg) throws BadArgs {
111 Path p = Paths.get(arg);
112 if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
113 throw new BadArgs("err.dot.output.path", arg);
114 }
115 task.options.dotOutputDir = arg;
116 }
117 },
118 new Option(false, "-s", "-summary") {
105 void process(JdepsTask task, String opt, String arg) { 119 void process(JdepsTask task, String opt, String arg) {
106 task.options.showSummary = true; 120 task.options.showSummary = true;
107 task.options.verbose = Analyzer.Type.SUMMARY; 121 task.options.verbose = Analyzer.Type.SUMMARY;
108 } 122 }
109 }, 123 },
110 new Option(false, "-v", "--verbose") { 124 new Option(false, "-v", "-verbose",
111 void process(JdepsTask task, String opt, String arg) { 125 "-verbose:package",
112 task.options.verbose = Analyzer.Type.VERBOSE; 126 "-verbose:class")
113 } 127 {
114 },
115 new Option(true, "-V", "--verbose-level") {
116 void process(JdepsTask task, String opt, String arg) throws BadArgs { 128 void process(JdepsTask task, String opt, String arg) throws BadArgs {
117 if ("package".equals(arg)) { 129 switch (opt) {
118 task.options.verbose = Analyzer.Type.PACKAGE; 130 case "-v":
119 } else if ("class".equals(arg)) { 131 case "-verbose":
120 task.options.verbose = Analyzer.Type.CLASS; 132 task.options.verbose = Analyzer.Type.VERBOSE;
121 } else { 133 break;
122 throw new BadArgs("err.invalid.arg.for.option", opt); 134 case "-verbose:package":
123 } 135 task.options.verbose = Analyzer.Type.PACKAGE;
124 } 136 break;
125 }, 137 case "-verbose:class":
126 new Option(true, "-c", "--classpath") { 138 task.options.verbose = Analyzer.Type.CLASS;
139 break;
140 default:
141 throw new BadArgs("err.invalid.arg.for.option", opt);
142 }
143 }
144 },
145 new Option(true, "-cp", "-classpath") {
127 void process(JdepsTask task, String opt, String arg) { 146 void process(JdepsTask task, String opt, String arg) {
128 task.options.classpath = arg; 147 task.options.classpath = arg;
129 } 148 }
130 }, 149 },
131 new Option(true, "-p", "--package") { 150 new Option(true, "-p", "-package") {
132 void process(JdepsTask task, String opt, String arg) { 151 void process(JdepsTask task, String opt, String arg) {
133 task.options.packageNames.add(arg); 152 task.options.packageNames.add(arg);
134 } 153 }
135 }, 154 },
136 new Option(true, "-e", "--regex") { 155 new Option(true, "-e", "-regex") {
137 void process(JdepsTask task, String opt, String arg) { 156 void process(JdepsTask task, String opt, String arg) {
138 task.options.regex = arg; 157 task.options.regex = arg;
139 } 158 }
140 }, 159 },
141 new Option(false, "-P", "--profile") { 160 new Option(true, "-include") {
161 void process(JdepsTask task, String opt, String arg) throws BadArgs {
162 task.options.includePattern = Pattern.compile(arg);
163 }
164 },
165 new Option(false, "-P", "-profile") {
142 void process(JdepsTask task, String opt, String arg) throws BadArgs { 166 void process(JdepsTask task, String opt, String arg) throws BadArgs {
143 task.options.showProfile = true; 167 task.options.showProfile = true;
144 if (Profiles.getProfileCount() == 0) { 168 if (Profile.getProfileCount() == 0) {
145 throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg")); 169 throw new BadArgs("err.option.unsupported", opt, getMessage("err.profiles.msg"));
146 } 170 }
147 } 171 }
148 }, 172 },
149 new Option(false, "-R", "--recursive") { 173 new Option(false, "-apionly") {
174 void process(JdepsTask task, String opt, String arg) {
175 task.options.apiOnly = true;
176 }
177 },
178 new Option(false, "-R", "-recursive") {
150 void process(JdepsTask task, String opt, String arg) { 179 void process(JdepsTask task, String opt, String arg) {
151 task.options.depth = 0; 180 task.options.depth = 0;
152 } 181 }
153 }, 182 },
154 new HiddenOption(true, "-d", "--depth") { 183 new Option(false, "-version") {
184 void process(JdepsTask task, String opt, String arg) {
185 task.options.version = true;
186 }
187 },
188 new HiddenOption(false, "-fullversion") {
189 void process(JdepsTask task, String opt, String arg) {
190 task.options.fullVersion = true;
191 }
192 },
193 new HiddenOption(true, "-depth") {
155 void process(JdepsTask task, String opt, String arg) throws BadArgs { 194 void process(JdepsTask task, String opt, String arg) throws BadArgs {
156 try { 195 try {
157 task.options.depth = Integer.parseInt(arg); 196 task.options.depth = Integer.parseInt(arg);
158 } catch (NumberFormatException e) { 197 } catch (NumberFormatException e) {
159 throw new BadArgs("err.invalid.arg.for.option", opt); 198 throw new BadArgs("err.invalid.arg.for.option", opt);
160 } 199 }
161 }
162 },
163 new Option(false, "--version") {
164 void process(JdepsTask task, String opt, String arg) {
165 task.options.version = true;
166 }
167 },
168 new HiddenOption(false, "--fullversion") {
169 void process(JdepsTask task, String opt, String arg) {
170 task.options.fullVersion = true;
171 } 200 }
172 }, 201 },
173 }; 202 };
174 203
175 private static final String PROGNAME = "jdeps"; 204 private static final String PROGNAME = "jdeps";
200 showHelp(); 229 showHelp();
201 } 230 }
202 if (options.version || options.fullVersion) { 231 if (options.version || options.fullVersion) {
203 showVersion(options.fullVersion); 232 showVersion(options.fullVersion);
204 } 233 }
205 if (classes.isEmpty() && !options.wildcard) { 234 if (classes.isEmpty() && options.includePattern == null) {
206 if (options.help || options.version || options.fullVersion) { 235 if (options.help || options.version || options.fullVersion) {
207 return EXIT_OK; 236 return EXIT_OK;
208 } else { 237 } else {
209 showHelp(); 238 showHelp();
210 return EXIT_CMDERR; 239 return EXIT_CMDERR;
231 } finally { 260 } finally {
232 log.flush(); 261 log.flush();
233 } 262 }
234 } 263 }
235 264
236 private final List<Archive> sourceLocations = new ArrayList<Archive>(); 265 private final List<Archive> sourceLocations = new ArrayList<>();
237 private boolean run() throws IOException { 266 private boolean run() throws IOException {
238 findDependencies(); 267 findDependencies();
239 Analyzer analyzer = new Analyzer(options.verbose); 268 Analyzer analyzer = new Analyzer(options.verbose);
240 analyzer.run(sourceLocations); 269 analyzer.run(sourceLocations);
241 if (options.verbose == Analyzer.Type.SUMMARY) { 270 if (options.dotOutputDir != null) {
242 printSummary(log, analyzer); 271 Path dir = Paths.get(options.dotOutputDir);
272 Files.createDirectories(dir);
273 generateDotFiles(dir, analyzer);
243 } else { 274 } else {
244 printDependencies(log, analyzer); 275 printRawOutput(log, analyzer);
245 } 276 }
246 return true; 277 return true;
247 } 278 }
248 279
280 private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
281 Path summary = dir.resolve("summary.dot");
282 try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
283 DotFileFormatter formatter = new DotFileFormatter(sw, "summary")) {
284 for (Archive archive : sourceLocations) {
285 analyzer.visitArchiveDependences(archive, formatter);
286 }
287 }
288 if (options.verbose != Analyzer.Type.SUMMARY) {
289 for (Archive archive : sourceLocations) {
290 if (analyzer.hasDependences(archive)) {
291 Path dotfile = dir.resolve(archive.getFileName() + ".dot");
292 try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
293 DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
294 analyzer.visitDependences(archive, formatter);
295 }
296 }
297 }
298 }
299 }
300
301 private void printRawOutput(PrintWriter writer, Analyzer analyzer) {
302 for (Archive archive : sourceLocations) {
303 RawOutputFormatter formatter = new RawOutputFormatter(writer);
304 analyzer.visitArchiveDependences(archive, formatter);
305 if (options.verbose != Analyzer.Type.SUMMARY) {
306 analyzer.visitDependences(archive, formatter);
307 }
308 }
309 }
249 private boolean isValidClassName(String name) { 310 private boolean isValidClassName(String name) {
250 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 311 if (!Character.isJavaIdentifierStart(name.charAt(0))) {
251 return false; 312 return false;
252 } 313 }
253 for (int i=1; i < name.length(); i++) { 314 for (int i=1; i < name.length(); i++) {
257 } 318 }
258 } 319 }
259 return true; 320 return true;
260 } 321 }
261 322
262 private void findDependencies() throws IOException { 323 private Dependency.Filter getDependencyFilter() {
263 Dependency.Finder finder = Dependencies.getClassDependencyFinder(); 324 if (options.regex != null) {
264 Dependency.Filter filter; 325 return Dependencies.getRegexFilter(Pattern.compile(options.regex));
265 if (options.regex != null) {
266 filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
267 } else if (options.packageNames.size() > 0) { 326 } else if (options.packageNames.size() > 0) {
268 filter = Dependencies.getPackageFilter(options.packageNames, false); 327 return Dependencies.getPackageFilter(options.packageNames, false);
269 } else { 328 } else {
270 filter = new Dependency.Filter() { 329 return new Dependency.Filter() {
330 @Override
271 public boolean accepts(Dependency dependency) { 331 public boolean accepts(Dependency dependency) {
272 return !dependency.getOrigin().equals(dependency.getTarget()); 332 return !dependency.getOrigin().equals(dependency.getTarget());
273 } 333 }
274 }; 334 };
275 } 335 }
276 336 }
277 List<Archive> archives = new ArrayList<Archive>(); 337
278 Deque<String> roots = new LinkedList<String>(); 338 private boolean matches(String classname, AccessFlags flags) {
339 if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) {
340 return false;
341 } else if (options.includePattern != null) {
342 return options.includePattern.matcher(classname.replace('/', '.')).matches();
343 } else {
344 return true;
345 }
346 }
347
348 private void findDependencies() throws IOException {
349 Dependency.Finder finder =
350 options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
351 : Dependencies.getClassDependencyFinder();
352 Dependency.Filter filter = getDependencyFilter();
353
354 List<Archive> archives = new ArrayList<>();
355 Deque<String> roots = new LinkedList<>();
279 for (String s : classes) { 356 for (String s : classes) {
280 File f = new File(s); 357 Path p = Paths.get(s);
281 if (f.exists()) { 358 if (Files.exists(p)) {
282 archives.add(new Archive(f, ClassFileReader.newInstance(f))); 359 archives.add(new Archive(p, ClassFileReader.newInstance(p)));
283 } else { 360 } else {
284 if (isValidClassName(s)) { 361 if (isValidClassName(s)) {
285 roots.add(s); 362 roots.add(s);
286 } else { 363 } else {
287 warning("warn.invalid.arg", s); 364 warning("warn.invalid.arg", s);
288 } 365 }
289 } 366 }
290 } 367 }
291 368
292 List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup 369 List<Archive> classpaths = new ArrayList<>(); // for class file lookup
293 if (options.wildcard) { 370 if (options.includePattern != null) {
294 // include all archives from classpath to the initial list
295 archives.addAll(getClassPathArchives(options.classpath)); 371 archives.addAll(getClassPathArchives(options.classpath));
296 } else { 372 } else {
297 classpaths.addAll(getClassPathArchives(options.classpath)); 373 classpaths.addAll(getClassPathArchives(options.classpath));
298 } 374 }
299 classpaths.addAll(PlatformClassPath.getArchives()); 375 classpaths.addAll(PlatformClassPath.getArchives());
303 sourceLocations.addAll(classpaths); 379 sourceLocations.addAll(classpaths);
304 380
305 // Work queue of names of classfiles to be searched. 381 // Work queue of names of classfiles to be searched.
306 // Entries will be unique, and for classes that do not yet have 382 // Entries will be unique, and for classes that do not yet have
307 // dependencies in the results map. 383 // dependencies in the results map.
308 Deque<String> deque = new LinkedList<String>(); 384 Deque<String> deque = new LinkedList<>();
309 Set<String> doneClasses = new HashSet<String>(); 385 Set<String> doneClasses = new HashSet<>();
310 386
311 // get the immediate dependencies of the input files 387 // get the immediate dependencies of the input files
312 for (Archive a : archives) { 388 for (Archive a : archives) {
313 for (ClassFile cf : a.reader().getClassFiles()) { 389 for (ClassFile cf : a.reader().getClassFiles()) {
314 String classFileName; 390 String classFileName;
316 classFileName = cf.getName(); 392 classFileName = cf.getName();
317 } catch (ConstantPoolException e) { 393 } catch (ConstantPoolException e) {
318 throw new ClassFileError(e); 394 throw new ClassFileError(e);
319 } 395 }
320 396
321 if (!doneClasses.contains(classFileName)) { 397 if (matches(classFileName, cf.access_flags)) {
322 doneClasses.add(classFileName); 398 if (!doneClasses.contains(classFileName)) {
323 } 399 doneClasses.add(classFileName);
324 for (Dependency d : finder.findDependencies(cf)) { 400 }
325 if (filter.accepts(d)) { 401 for (Dependency d : finder.findDependencies(cf)) {
326 String cn = d.getTarget().getName(); 402 if (filter.accepts(d)) {
327 if (!doneClasses.contains(cn) && !deque.contains(cn)) { 403 String cn = d.getTarget().getName();
328 deque.add(cn); 404 if (!doneClasses.contains(cn) && !deque.contains(cn)) {
405 deque.add(cn);
406 }
407 a.addClass(d.getOrigin(), d.getTarget());
329 } 408 }
330 a.addClass(d.getOrigin(), d.getTarget());
331 } 409 }
332 } 410 }
333 } 411 }
334 } 412 }
335 413
377 if (cf == null) { 455 if (cf == null) {
378 doneClasses.add(name); 456 doneClasses.add(name);
379 } 457 }
380 } 458 }
381 unresolved = deque; 459 unresolved = deque;
382 deque = new LinkedList<String>(); 460 deque = new LinkedList<>();
383 } while (!unresolved.isEmpty() && depth-- > 0); 461 } while (!unresolved.isEmpty() && depth-- > 0);
384 }
385
386 private void printSummary(final PrintWriter out, final Analyzer analyzer) {
387 Analyzer.Visitor visitor = new Analyzer.Visitor() {
388 public void visit(String origin, String target, String profile) {
389 if (options.showProfile) {
390 out.format("%-30s -> %s%n", origin, target);
391 }
392 }
393 public void visit(Archive origin, Archive target) {
394 if (!options.showProfile) {
395 out.format("%-30s -> %s%n", origin, target);
396 }
397 }
398 };
399 analyzer.visitSummary(visitor);
400 }
401
402 private void printDependencies(final PrintWriter out, final Analyzer analyzer) {
403 Analyzer.Visitor visitor = new Analyzer.Visitor() {
404 private String pkg = "";
405 public void visit(String origin, String target, String profile) {
406 if (!origin.equals(pkg)) {
407 pkg = origin;
408 out.format(" %s (%s)%n", origin, analyzer.getArchive(origin).getFileName());
409 }
410 out.format(" -> %-50s %s%n", target,
411 (options.showProfile && !profile.isEmpty())
412 ? profile
413 : analyzer.getArchiveName(target, profile));
414 }
415 public void visit(Archive origin, Archive target) {
416 out.format("%s -> %s%n", origin, target);
417 }
418 };
419 analyzer.visit(visitor);
420 } 462 }
421 463
422 public void handleOptions(String[] args) throws BadArgs { 464 public void handleOptions(String[] args) throws BadArgs {
423 // process options 465 // process options
424 for (int i=0; i < args.length; i++) { 466 for (int i=0; i < args.length; i++) {
425 if (args[i].charAt(0) == '-') { 467 if (args[i].charAt(0) == '-') {
426 String name = args[i]; 468 String name = args[i];
427 Option option = getOption(name); 469 Option option = getOption(name);
428 String param = null; 470 String param = null;
429 if (option.hasArg) { 471 if (option.hasArg) {
430 if (name.startsWith("--") && name.indexOf('=') > 0) { 472 if (name.startsWith("-") && name.indexOf('=') > 0) {
431 param = name.substring(name.indexOf('=') + 1, name.length()); 473 param = name.substring(name.indexOf('=') + 1, name.length());
432 } else if (i + 1 < args.length) { 474 } else if (i + 1 < args.length) {
433 param = args[++i]; 475 param = args[++i];
434 } 476 }
435 if (param == null || param.isEmpty() || param.charAt(0) == '-') { 477 if (param == null || param.isEmpty() || param.charAt(0) == '-') {
445 for (; i < args.length; i++) { 487 for (; i < args.length; i++) {
446 String name = args[i]; 488 String name = args[i];
447 if (name.charAt(0) == '-') { 489 if (name.charAt(0) == '-') {
448 throw new BadArgs("err.option.after.class", name).showUsage(true); 490 throw new BadArgs("err.option.after.class", name).showUsage(true);
449 } 491 }
450 if (name.equals("*") || name.equals("\"*\"")) { 492 classes.add(name);
451 options.wildcard = true;
452 } else {
453 classes.add(name);
454 }
455 } 493 }
456 } 494 }
457 } 495 }
458 } 496 }
459 497
516 boolean version; 554 boolean version;
517 boolean fullVersion; 555 boolean fullVersion;
518 boolean showProfile; 556 boolean showProfile;
519 boolean showSummary; 557 boolean showSummary;
520 boolean wildcard; 558 boolean wildcard;
521 String regex; 559 boolean apiOnly;
560 String dotOutputDir;
522 String classpath = ""; 561 String classpath = "";
523 int depth = 1; 562 int depth = 1;
524 Analyzer.Type verbose = Analyzer.Type.PACKAGE; 563 Analyzer.Type verbose = Analyzer.Type.PACKAGE;
525 Set<String> packageNames = new HashSet<String>(); 564 Set<String> packageNames = new HashSet<>();
526 } 565 String regex; // apply to the dependences
527 566 Pattern includePattern; // apply to classes
567 }
528 private static class ResourceBundleHelper { 568 private static class ResourceBundleHelper {
529 static final ResourceBundle versionRB; 569 static final ResourceBundle versionRB;
530 static final ResourceBundle bundle; 570 static final ResourceBundle bundle;
531 571
532 static { 572 static {
545 } 585 }
546 586
547 private List<Archive> getArchives(List<String> filenames) throws IOException { 587 private List<Archive> getArchives(List<String> filenames) throws IOException {
548 List<Archive> result = new ArrayList<Archive>(); 588 List<Archive> result = new ArrayList<Archive>();
549 for (String s : filenames) { 589 for (String s : filenames) {
550 File f = new File(s); 590 Path p = Paths.get(s);
551 if (f.exists()) { 591 if (Files.exists(p)) {
552 result.add(new Archive(f, ClassFileReader.newInstance(f))); 592 result.add(new Archive(p, ClassFileReader.newInstance(p)));
553 } else { 593 } else {
554 warning("warn.file.not.exist", s); 594 warning("warn.file.not.exist", s);
555 } 595 }
556 } 596 }
557 return result; 597 return result;
558 } 598 }
559 599
560 private List<Archive> getClassPathArchives(String paths) throws IOException { 600 private List<Archive> getClassPathArchives(String paths) throws IOException {
561 List<Archive> result = new ArrayList<Archive>(); 601 List<Archive> result = new ArrayList<>();
562 if (paths.isEmpty()) { 602 if (paths.isEmpty()) {
563 return result; 603 return result;
564 } 604 }
565 for (String p : paths.split(File.pathSeparator)) { 605 for (String p : paths.split(File.pathSeparator)) {
566 if (p.length() > 0) { 606 if (p.length() > 0) {
567 File f = new File(p); 607 List<Path> files = new ArrayList<>();
568 if (f.exists()) { 608 // wildcard to parse all JAR files e.g. -classpath dir/*
569 result.add(new Archive(f, ClassFileReader.newInstance(f))); 609 int i = p.lastIndexOf(".*");
610 if (i > 0) {
611 Path dir = Paths.get(p.substring(0, i));
612 try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
613 for (Path entry : stream) {
614 files.add(entry);
615 }
616 }
617 } else {
618 files.add(Paths.get(p));
619 }
620 for (Path f : files) {
621 if (Files.exists(f)) {
622 result.add(new Archive(f, ClassFileReader.newInstance(f)));
623 }
570 } 624 }
571 } 625 }
572 } 626 }
573 return result; 627 return result;
574 } 628 }
629
630
631 /**
632 * Returns the file name of the archive for non-JRE class or
633 * internal JRE classes. It returns empty string for SE API.
634 */
635 private static String getArchiveName(Archive source, String profile) {
636 String name = source.getFileName();
637 if (source instanceof JDKArchive)
638 return profile.isEmpty() ? "JDK internal API (" + name + ")" : "";
639 return name;
640 }
641
642 class RawOutputFormatter implements Analyzer.Visitor {
643 private final PrintWriter writer;
644 RawOutputFormatter(PrintWriter writer) {
645 this.writer = writer;
646 }
647
648 private String pkg = "";
649 @Override
650 public void visitDependence(String origin, Archive source,
651 String target, Archive archive, String profile) {
652 if (!origin.equals(pkg)) {
653 pkg = origin;
654 writer.format(" %s (%s)%n", origin, source.getFileName());
655 }
656 String name = (options.showProfile && !profile.isEmpty())
657 ? profile
658 : getArchiveName(archive, profile);
659 writer.format(" -> %-50s %s%n", target, name);
660 }
661
662 @Override
663 public void visitArchiveDependence(Archive origin, Archive target, String profile) {
664 writer.format("%s -> %s", origin, target);
665 if (options.showProfile && !profile.isEmpty()) {
666 writer.format(" (%s)%n", profile);
667 } else {
668 writer.format("%n");
669 }
670 }
671 }
672
673 class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
674 private final PrintWriter writer;
675 private final String name;
676 DotFileFormatter(PrintWriter writer, String name) {
677 this.writer = writer;
678 this.name = name;
679 writer.format("digraph \"%s\" {%n", name);
680 }
681 DotFileFormatter(PrintWriter writer, Archive archive) {
682 this.writer = writer;
683 this.name = archive.getFileName();
684 writer.format("digraph \"%s\" {%n", name);
685 writer.format(" // Path: %s%n", archive.toString());
686 }
687
688 @Override
689 public void close() {
690 writer.println("}");
691 }
692
693 private final Set<String> edges = new HashSet<>();
694 private String node = "";
695 @Override
696 public void visitDependence(String origin, Archive source,
697 String target, Archive archive, String profile) {
698 if (!node.equals(origin)) {
699 edges.clear();
700 node = origin;
701 }
702 // if -P option is specified, package name -> profile will
703 // be shown and filter out multiple same edges.
704 if (!edges.contains(target)) {
705 StringBuilder sb = new StringBuilder();
706 String name = options.showProfile && !profile.isEmpty()
707 ? profile
708 : getArchiveName(archive, profile);
709 writer.format(" %-50s -> %s;%n",
710 String.format("\"%s\"", origin),
711 name.isEmpty() ? String.format("\"%s\"", target)
712 : String.format("\"%s (%s)\"", target, name));
713 edges.add(target);
714 }
715 }
716
717 @Override
718 public void visitArchiveDependence(Archive origin, Archive target, String profile) {
719 String name = options.showProfile && !profile.isEmpty()
720 ? profile : "";
721 writer.format(" %-30s -> \"%s\";%n",
722 String.format("\"%s\"", origin.getFileName()),
723 name.isEmpty()
724 ? target.getFileName()
725 : String.format("%s (%s)", target.getFileName(), name));
726 }
727 }
575 } 728 }

mercurial