1 | /* |
2 | * Copyright (c) 2001-2009, Jean Tessier |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * |
12 | * * Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * * Neither the name of Jean Tessier nor the names of his contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR |
24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | package com.jeantessier.dependencyfinder.ant; |
34 | |
35 | import java.io.*; |
36 | import java.util.*; |
37 | |
38 | import javax.xml.parsers.*; |
39 | |
40 | import org.apache.log4j.*; |
41 | import org.apache.tools.ant.*; |
42 | import org.apache.tools.ant.types.*; |
43 | |
44 | import org.xml.sax.*; |
45 | |
46 | import com.jeantessier.dependency.*; |
47 | |
48 | public class DependencyReporter extends GraphTask { |
49 | private String scopeIncludes = "//"; |
50 | private String scopeExcludes = ""; |
51 | private boolean packageScope = false; |
52 | private String packageScopeIncludes = ""; |
53 | private String packageScopeExcludes = ""; |
54 | private boolean classScope = false; |
55 | private String classScopeIncludes = ""; |
56 | private String classScopeExcludes = ""; |
57 | private boolean featureScope = false; |
58 | private String featureScopeIncludes = ""; |
59 | private String featureScopeExcludes = ""; |
60 | private String filterIncludes = "//"; |
61 | private String filterExcludes = ""; |
62 | private boolean packageFilter = false; |
63 | private String packageFilterIncludes = ""; |
64 | private String packageFilterExcludes = ""; |
65 | private boolean classFilter = false; |
66 | private String classFilterIncludes = ""; |
67 | private String classFilterExcludes = ""; |
68 | private boolean featureFilter = false; |
69 | private String featureFilterIncludes = ""; |
70 | private String featureFilterExcludes = ""; |
71 | |
72 | private Path scopeIncludesList; |
73 | private Path scopeExcludesList; |
74 | private Path filterIncludesList; |
75 | private Path filterExcludesList; |
76 | |
77 | private boolean showInbounds = false; |
78 | private boolean showOutbounds = false; |
79 | private boolean showEmptyNodes = false; |
80 | |
81 | private boolean minimize = false; |
82 | private boolean maximize = false; |
83 | private boolean copyOnly = false; |
84 | private boolean xml = false; |
85 | private String encoding = XMLPrinter.DEFAULT_ENCODING; |
86 | private String dtdPrefix = XMLPrinter.DEFAULT_DTD_PREFIX; |
87 | private String indentText; |
88 | |
89 | public String getScopeincludes() { |
90 | return scopeIncludes; |
91 | } |
92 | |
93 | public void setScopeincludes(String scopeIncludes) { |
94 | this.scopeIncludes = scopeIncludes; |
95 | } |
96 | |
97 | public String getScopeexcludes() { |
98 | return scopeExcludes; |
99 | } |
100 | |
101 | public void setScopeexcludes(String scopeExcludes) { |
102 | this.scopeExcludes = scopeExcludes; |
103 | } |
104 | |
105 | public boolean getPackagescope() { |
106 | return packageScope; |
107 | } |
108 | |
109 | public void setPackagescope(boolean packageScope) { |
110 | this.packageScope = packageScope; |
111 | } |
112 | |
113 | public String getPackagescopeincludes() { |
114 | return packageScopeIncludes; |
115 | } |
116 | |
117 | public void setPackagescopeincludes(String packageScopeIncludes) { |
118 | this.packageScopeIncludes = packageScopeIncludes; |
119 | } |
120 | |
121 | public String getPackagescopeexcludes() { |
122 | return packageScopeExcludes; |
123 | } |
124 | |
125 | public void setPackagescopeexcludes(String packageScopeExcludes) { |
126 | this.packageScopeExcludes = packageScopeExcludes; |
127 | } |
128 | |
129 | public boolean getClassscope() { |
130 | return classScope; |
131 | } |
132 | |
133 | public void setClassscope(boolean classScope) { |
134 | this.classScope = classScope; |
135 | } |
136 | |
137 | public String getClassscopeincludes() { |
138 | return classScopeIncludes; |
139 | } |
140 | |
141 | public void setClassscopeincludes(String classScopeIncludes) { |
142 | this.classScopeIncludes = classScopeIncludes; |
143 | } |
144 | |
145 | public String getClassscopeexcludes() { |
146 | return classScopeExcludes; |
147 | } |
148 | |
149 | public void setClassscopeexcludes(String classScopeExcludes) { |
150 | this.classScopeExcludes = classScopeExcludes; |
151 | } |
152 | |
153 | public boolean getFeaturescope() { |
154 | return featureScope; |
155 | } |
156 | |
157 | public void setFeaturescope(boolean featureScope) { |
158 | this.featureScope = featureScope; |
159 | } |
160 | |
161 | public String getFeaturescopeincludes() { |
162 | return featureScopeIncludes; |
163 | } |
164 | |
165 | public void setFeaturescopeincludes(String featureScopeIncludes) { |
166 | this.featureScopeIncludes = featureScopeIncludes; |
167 | } |
168 | |
169 | public String getFeaturescopeexcludes() { |
170 | return featureScopeExcludes; |
171 | } |
172 | |
173 | public void setFeaturescopeexcludes(String featureScopeExcludes) { |
174 | this.featureScopeExcludes = featureScopeExcludes; |
175 | } |
176 | |
177 | public String getFilterincludes() { |
178 | return filterIncludes; |
179 | } |
180 | |
181 | public void setFilterincludes(String filterIncludes) { |
182 | this.filterIncludes = filterIncludes; |
183 | } |
184 | |
185 | public String getFilterexcludes() { |
186 | return filterExcludes; |
187 | } |
188 | |
189 | public void setFilterexcludes(String filterExcludes) { |
190 | this.filterExcludes = filterExcludes; |
191 | } |
192 | |
193 | public boolean getPackagefilter() { |
194 | return packageFilter; |
195 | } |
196 | |
197 | public void setPackagefilter(boolean packageFilter) { |
198 | this.packageFilter = packageFilter; |
199 | } |
200 | |
201 | public String getPackagefilterincludes() { |
202 | return packageFilterIncludes; |
203 | } |
204 | |
205 | public void setPackagefilterincludes(String packageFilterIncludes) { |
206 | this.packageFilterIncludes = packageFilterIncludes; |
207 | } |
208 | |
209 | public String getPackagefilterexcludes() { |
210 | return packageFilterExcludes; |
211 | } |
212 | |
213 | public void setPackagefilterexcludes(String packageFilterExcludes) { |
214 | this.packageFilterExcludes = packageFilterExcludes; |
215 | } |
216 | |
217 | public boolean getClassfilter() { |
218 | return classFilter; |
219 | } |
220 | |
221 | public void setClassfilter(boolean classFilter) { |
222 | this.classFilter = classFilter; |
223 | } |
224 | |
225 | public String getClassfilterincludes() { |
226 | return classFilterIncludes; |
227 | } |
228 | |
229 | public void setClassfilterincludes(String classFilterIncludes) { |
230 | this.classFilterIncludes = classFilterIncludes; |
231 | } |
232 | |
233 | public String getClassfilterexcludes() { |
234 | return classFilterExcludes; |
235 | } |
236 | |
237 | public void setClassfilterexcludes(String classFilterExcludes) { |
238 | this.classFilterExcludes = classFilterExcludes; |
239 | } |
240 | |
241 | public boolean getFeaturefilter() { |
242 | return featureFilter; |
243 | } |
244 | |
245 | public void setFeaturefilter(boolean featureFilter) { |
246 | this.featureFilter = featureFilter; |
247 | } |
248 | |
249 | public String getFeaturefilterincludes() { |
250 | return featureFilterIncludes; |
251 | } |
252 | |
253 | public void setFeaturefilterincludes(String featureFilterIncludes) { |
254 | this.featureFilterIncludes = featureFilterIncludes; |
255 | } |
256 | |
257 | public String getFeaturefilterexcludes() { |
258 | return featureFilterExcludes; |
259 | } |
260 | |
261 | public void setFeaturefilterexcludes(String featureFilterExcludes) { |
262 | this.featureFilterExcludes = featureFilterExcludes; |
263 | } |
264 | |
265 | public void setP2p(boolean value) { |
266 | setPackagescope(value); |
267 | setPackagefilter(value); |
268 | } |
269 | |
270 | public void setC2p(boolean value) { |
271 | setClassscope(value); |
272 | setPackagefilter(value); |
273 | } |
274 | |
275 | public void setC2c(boolean value) { |
276 | setClassscope(value); |
277 | setClassfilter(value); |
278 | } |
279 | |
280 | public void setF2f(boolean value) { |
281 | setFeaturescope(value); |
282 | setFeaturefilter(value); |
283 | } |
284 | |
285 | public void setIncludes(String value) { |
286 | setScopeincludes(value); |
287 | setFilterincludes(value); |
288 | } |
289 | |
290 | public void setExcludes(String value) { |
291 | setScopeexcludes(value); |
292 | setFilterexcludes(value); |
293 | } |
294 | |
295 | public Path createScopeincludeslist() { |
296 | if (scopeIncludesList == null) { |
297 | scopeIncludesList = new Path(getProject()); |
298 | } |
299 | |
300 | return scopeIncludesList; |
301 | } |
302 | |
303 | public Path getScopeincludeslist() { |
304 | return scopeIncludesList; |
305 | } |
306 | |
307 | public Path createScopeexcludeslist() { |
308 | if (scopeExcludesList == null) { |
309 | scopeExcludesList = new Path(getProject()); |
310 | } |
311 | |
312 | return scopeExcludesList; |
313 | } |
314 | |
315 | public Path getScopeexcludeslist() { |
316 | return scopeExcludesList; |
317 | } |
318 | |
319 | public Path createFilterincludeslist() { |
320 | if (filterIncludesList == null) { |
321 | filterIncludesList = new Path(getProject()); |
322 | } |
323 | |
324 | return filterIncludesList; |
325 | } |
326 | |
327 | public Path getFilterincludeslist() { |
328 | return filterIncludesList; |
329 | } |
330 | |
331 | public Path createFilterexcludeslist() { |
332 | if (filterExcludesList == null) { |
333 | filterExcludesList = new Path(getProject()); |
334 | } |
335 | |
336 | return filterExcludesList; |
337 | } |
338 | |
339 | public Path getFilterexcludeslist() { |
340 | return filterExcludesList; |
341 | } |
342 | |
343 | public boolean getShowinbounds() { |
344 | return showInbounds; |
345 | } |
346 | |
347 | public void setShowinbounds(boolean showInbounds) { |
348 | this.showInbounds = showInbounds; |
349 | } |
350 | |
351 | public boolean getShowoutbounds() { |
352 | return showOutbounds; |
353 | } |
354 | |
355 | public void setShowoutbounds(boolean showOutbounds) { |
356 | this.showOutbounds = showOutbounds; |
357 | } |
358 | |
359 | public boolean getShowemptynodes() { |
360 | return showEmptyNodes; |
361 | } |
362 | |
363 | public void setShowemptynodes(boolean showEmptyNodes) { |
364 | this.showEmptyNodes = showEmptyNodes; |
365 | } |
366 | |
367 | public void setShowAll(boolean showAll) { |
368 | setShowinbounds(showAll); |
369 | setShowoutbounds(showAll); |
370 | setShowemptynodes(showAll); |
371 | } |
372 | |
373 | public boolean getMinimize() { |
374 | return minimize; |
375 | } |
376 | |
377 | public void setMinimize(boolean minimize) { |
378 | this.minimize = minimize; |
379 | } |
380 | |
381 | public boolean getMaximize() { |
382 | return maximize; |
383 | } |
384 | |
385 | public void setMaximize(boolean maximize) { |
386 | this.maximize = maximize; |
387 | } |
388 | |
389 | public boolean getCopyOnly() { |
390 | return copyOnly; |
391 | } |
392 | |
393 | public void setCopyOnly(boolean copyOnly) { |
394 | this.copyOnly = copyOnly; |
395 | } |
396 | |
397 | public boolean getXml() { |
398 | return xml; |
399 | } |
400 | |
401 | public void setXml(boolean xml) { |
402 | this.xml = xml; |
403 | } |
404 | |
405 | public String getEncoding() { |
406 | return encoding; |
407 | } |
408 | |
409 | public void setEncoding(String encoding) { |
410 | this.encoding = encoding; |
411 | } |
412 | |
413 | public String getDtdprefix() { |
414 | return dtdPrefix; |
415 | } |
416 | |
417 | public void setDtdprefix(String dtdPrefix) { |
418 | this.dtdPrefix = dtdPrefix; |
419 | } |
420 | |
421 | public String getIndenttext() { |
422 | return indentText; |
423 | } |
424 | |
425 | public void setIntenttext(String indentText) { |
426 | this.indentText = indentText; |
427 | } |
428 | |
429 | protected void validateParameters() throws BuildException { |
430 | super.validateParameters(); |
431 | |
432 | if (hasScopeRegularExpressionSwitches() && hasScopeListSwitches()) { |
433 | throw new BuildException("Cannot have scope attributes for regular expressions and lists at the same time!"); |
434 | } |
435 | |
436 | if (hasFilterRegularExpressionSwitches() && hasFilterListSwitches()) { |
437 | throw new BuildException("Cannot have filter attributes for regular expressions and lists at the same time!"); |
438 | } |
439 | } |
440 | |
441 | public void execute() throws BuildException { |
442 | // first off, make sure that we've got what we need |
443 | validateParameters(); |
444 | |
445 | VerboseListener verboseListener = new VerboseListener(this); |
446 | |
447 | try { |
448 | GraphCopier copier; |
449 | if (getCopyOnly() || getMaximize()) { |
450 | copier = new GraphCopier(getStrategy()); |
451 | } else { |
452 | copier = new GraphSummarizer(getScopeCriteria(), getFilterCriteria()); |
453 | } |
454 | |
455 | for (String filename : getSrc().list()) { |
456 | log("Reading graph from " + filename); |
457 | |
458 | Collection<PackageNode> packages = Collections.emptyList(); |
459 | |
460 | if (filename.endsWith(".xml")) { |
461 | NodeLoader loader = new NodeLoader(getValidate()); |
462 | loader.addDependencyListener(verboseListener); |
463 | packages = loader.load(filename).getPackages().values(); |
464 | } |
465 | |
466 | if (getMaximize()) { |
467 | log("Maximizing ..."); |
468 | new LinkMaximizer().traverseNodes(packages); |
469 | } else if (getMinimize()) { |
470 | log("Minimizing ..."); |
471 | new LinkMinimizer().traverseNodes(packages); |
472 | } |
473 | |
474 | copier.traverseNodes(packages); |
475 | } |
476 | |
477 | log("Saving dependency graph to " + getDestfile().getAbsolutePath()); |
478 | |
479 | PrintWriter out = new PrintWriter(new FileWriter(getDestfile())); |
480 | |
481 | Printer printer; |
482 | if (getXml()) { |
483 | printer = new XMLPrinter(out, getEncoding(), getDtdprefix()); |
484 | } else { |
485 | printer = new TextPrinter(out); |
486 | } |
487 | |
488 | if (getIndenttext() != null) { |
489 | printer.setIndentText(getIndenttext()); |
490 | } |
491 | |
492 | if (getShowinbounds() || getShowoutbounds() || getShowemptynodes()) { |
493 | printer.setShowInbounds(getShowinbounds()); |
494 | printer.setShowOutbounds(getShowoutbounds()); |
495 | printer.setShowEmptyNodes(getShowemptynodes()); |
496 | } |
497 | |
498 | printer.traverseNodes(copier.getScopeFactory().getPackages().values()); |
499 | |
500 | out.close(); |
501 | } catch (SAXException ex) { |
502 | throw new BuildException(ex); |
503 | } catch (ParserConfigurationException ex) { |
504 | throw new BuildException(ex); |
505 | } catch (IOException ex) { |
506 | throw new BuildException(ex); |
507 | } |
508 | } |
509 | |
510 | protected SelectionCriteria getScopeCriteria() throws BuildException { |
511 | SelectionCriteria result = new ComprehensiveSelectionCriteria(); |
512 | |
513 | if (hasScopeRegularExpressionSwitches()) { |
514 | result = createRegularExpressionScopeCriteria(); |
515 | } else if (hasScopeListSwitches()) { |
516 | result = createCollectionSelectionCriteria(getScopeincludeslist(), getScopeexcludeslist()); |
517 | } |
518 | |
519 | return result; |
520 | } |
521 | |
522 | protected SelectionCriteria getFilterCriteria() throws BuildException { |
523 | SelectionCriteria result = new ComprehensiveSelectionCriteria(); |
524 | |
525 | if (hasFilterRegularExpressionSwitches()) { |
526 | result = createRegularExpressionFilterCriteria(); |
527 | } else if (hasFilterListSwitches()) { |
528 | result = createCollectionSelectionCriteria(getFilterincludeslist(), getFilterexcludeslist()); |
529 | } |
530 | |
531 | return result; |
532 | } |
533 | |
534 | protected RegularExpressionSelectionCriteria createRegularExpressionScopeCriteria() throws BuildException { |
535 | RegularExpressionSelectionCriteria result = new RegularExpressionSelectionCriteria(); |
536 | |
537 | if (getPackagescope() || getClassscope() || getFeaturescope()) { |
538 | result.setMatchingPackages(getPackagescope()); |
539 | result.setMatchingClasses(getClassscope()); |
540 | result.setMatchingFeatures(getFeaturescope()); |
541 | } |
542 | |
543 | result.setGlobalIncludes(getScopeincludes()); |
544 | result.setGlobalExcludes(getScopeexcludes()); |
545 | result.setPackageIncludes(getPackagescopeincludes()); |
546 | result.setPackageExcludes(getPackagescopeexcludes()); |
547 | result.setClassIncludes(getClassscopeincludes()); |
548 | result.setClassExcludes(getClassscopeexcludes()); |
549 | result.setFeatureIncludes(getFeaturescopeincludes()); |
550 | result.setFeatureExcludes(getFeaturescopeexcludes()); |
551 | |
552 | return result; |
553 | } |
554 | |
555 | protected RegularExpressionSelectionCriteria createRegularExpressionFilterCriteria() throws BuildException { |
556 | RegularExpressionSelectionCriteria result = new RegularExpressionSelectionCriteria(); |
557 | |
558 | if (getPackagefilter() || getClassfilter() || getFeaturefilter()) { |
559 | result.setMatchingPackages(getPackagefilter()); |
560 | result.setMatchingClasses(getClassfilter()); |
561 | result.setMatchingFeatures(getFeaturefilter()); |
562 | } |
563 | |
564 | result.setGlobalIncludes(getFilterincludes()); |
565 | result.setGlobalExcludes(getFilterexcludes()); |
566 | result.setPackageIncludes(getPackagefilterincludes()); |
567 | result.setPackageExcludes(getPackagefilterexcludes()); |
568 | result.setClassIncludes(getClassfilterincludes()); |
569 | result.setClassExcludes(getClassfilterexcludes()); |
570 | result.setFeatureIncludes(getFeaturefilterincludes()); |
571 | result.setFeatureExcludes(getFeaturefilterexcludes()); |
572 | |
573 | return result; |
574 | } |
575 | |
576 | private boolean hasScopeRegularExpressionSwitches() { |
577 | return |
578 | !getScopeincludes().equals("//") || |
579 | !getScopeexcludes().equals("") || |
580 | getPackagescope() || |
581 | !getPackagescopeincludes().equals("") || |
582 | !getPackagescopeexcludes().equals("") || |
583 | getClassscope() || |
584 | !getClassscopeincludes().equals("") || |
585 | !getClassscopeexcludes().equals("") || |
586 | getFeaturescope() || |
587 | !getFeaturescopeincludes().equals("") || |
588 | !getFeaturescopeexcludes().equals(""); |
589 | } |
590 | |
591 | private boolean hasScopeListSwitches() { |
592 | return |
593 | getScopeincludeslist() != null || |
594 | getScopeexcludeslist() != null; |
595 | } |
596 | |
597 | private boolean hasFilterRegularExpressionSwitches() { |
598 | return |
599 | !getFilterincludes().equals("//") || |
600 | !getFilterexcludes().equals("") || |
601 | getPackagefilter() || |
602 | !getPackagefilterincludes().equals("") || |
603 | !getPackagefilterexcludes().equals("") || |
604 | getClassfilter() || |
605 | !getClassfilterincludes().equals("") || |
606 | !getClassfilterexcludes().equals("") || |
607 | getFeaturefilter() || |
608 | !getFeaturefilterincludes().equals("") || |
609 | !getFeaturefilterexcludes().equals(""); |
610 | } |
611 | |
612 | private boolean hasFilterListSwitches() { |
613 | return |
614 | getFilterincludeslist() != null || |
615 | getFilterexcludeslist() != null; |
616 | } |
617 | |
618 | private CollectionSelectionCriteria createCollectionSelectionCriteria(Path includes, Path excludes) { |
619 | return new CollectionSelectionCriteria(loadCollection(includes), loadCollection(excludes)); |
620 | } |
621 | |
622 | private TraversalStrategy getStrategy() throws BuildException { |
623 | return new SelectiveTraversalStrategy(getScopeCriteria(), getFilterCriteria()); |
624 | } |
625 | |
626 | private Collection<String> loadCollection(Path path) { |
627 | Collection<String> result = null; |
628 | |
629 | if (path != null) { |
630 | result = new HashSet<String>(); |
631 | |
632 | for (String filename : path.list()) { |
633 | BufferedReader reader = null; |
634 | String line; |
635 | |
636 | try { |
637 | reader = new BufferedReader(new FileReader(filename)); |
638 | while ((line = reader.readLine()) != null) { |
639 | result.add(line); |
640 | } |
641 | } catch (IOException ex) { |
642 | Logger.getLogger(getClass()).error("Couldn't read file " + filename, ex); |
643 | } finally { |
644 | try { |
645 | if (reader != null) { |
646 | reader.close(); |
647 | } |
648 | } catch (IOException ex) { |
649 | Logger.getLogger(getClass()).error("Couldn't close file " + filename, ex); |
650 | } |
651 | } |
652 | } |
653 | } |
654 | |
655 | return result; |
656 | } |
657 | } |