1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.river.tool.classdepend;
19
20 import org.apache.river.tool.classdepend.ClassDependParameters.CDPBuilder;
21 import java.io.BufferedInputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.net.URLClassLoader;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.StringTokenizer;
35 import java.util.regex.Pattern;
36
37
38
39
40
41 public class ClassDepend {
42
43
44 private static final String systemClasspath =
45 System.getProperty("java.class.path");
46
47
48
49
50 private final ClassLoader loader;
51
52
53
54
55
56 private final ClassLoader platformLoader;
57
58
59
60
61 private final PackageClasses packageClasses;
62
63 private volatile boolean printClassesWithFileSeparator = false;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public static ClassDepend newInstance(String classpath, String platform, boolean warn)
95 throws MalformedURLException, IOException{
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 ClassDepend classDepend = !warn
112 ? new ClassDepend(classpath, platform)
113 : new ClassDepend(classpath, platform) {
114 protected void noteClassNotFound(String name) {
115 System.err.println("Warning: Class not found: " + name);
116 }
117 protected void noteClassLoadingFailed(String name, IOException e) {
118 System.err.println("Warning: Problem loading class "
119 + name + ": " + e.getMessage());
120 }
121 };
122 return classDepend;
123 }
124
125 public static void main(String[] args) {
126 try {
127 CDPBuilder cdpb = new CDPBuilder();
128 String classpath = null;
129 String platform = null;
130 Set rootClasses = new HashSet();
131 boolean recurse = true;
132 boolean warn = false;
133 boolean files = false;
134 boolean graph = false;
135 for (int i = 0; i < args.length; i++) {
136 String arg = args[i];
137 if (arg.equals("-cp")) {
138 classpath = args[++i];
139 } else if (arg.equals("-platform")) {
140 platform = args[++i];
141 } else if (arg.equals("-exclude")) {
142 cdpb.addOutsidePackageOrClass(args[++i]);
143 } else if (arg.equals("-norecurse")) {
144 recurse = false;
145 } else if (arg.equals("-warn")) {
146 warn = true;
147 } else if (arg.equals("-files")) {
148 files = true;
149 } else if (arg.equals("-graph")) {
150 graph = true;
151 } else if (arg.equals("-excljava")) {
152 cdpb.excludePlatformClasses(true);
153 } else if (arg.startsWith("-")) {
154 throw new IllegalArgumentException("Bad option: " + arg);
155 } else {
156 rootClasses.add(arg);
157 }
158 }
159 ClassDependParameters cdp = cdpb.build();
160 ClassDepend classDepend = ClassDepend.newInstance(classpath, platform, warn);
161 Set result = classDepend
162 .filterClassDependencyRelationShipMap(
163 classDepend.getDependencyRelationshipMap(rootClasses, recurse),
164 cdp);
165 Iterator results = result.iterator();
166 while (results.hasNext()){
167 Object rezult = results.next();
168 if ( !(rezult instanceof ClassDependencyRelationship )) continue;
169 ClassDependencyRelationship cl = (ClassDependencyRelationship) rezult;
170 String str = cl.toString();
171 if (files) {
172 str = str.replace('.', File.separatorChar).concat(".class");
173 System.out.println(str);
174 }
175 if (graph) {
176 Set deps = cl.getProviders();
177 Iterator itr = deps.iterator();
178 while (itr.hasNext()){
179 Object dep = itr.next();
180 if ( result.contains(dep)) {
181 System.out.println("\"" + cl + "\""+ " -> " +
182 "\"" + dep + "\"" + ";");
183 }
184 }
185 }
186 }
187 } catch (Throwable e) {
188 e.printStackTrace();
189 System.exit(1);
190 }
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 ClassDepend(String classpath, String platform)
220 throws MalformedURLException, IOException {
221 if (classpath == null) {
222 classpath = systemClasspath;
223 }
224 ClassLoader system = ClassLoader.getSystemClassLoader();
225 ClassLoader parent = system.getParent();
226 loader = (systemClasspath.equals(classpath))
227 ? system
228 : new URLClassLoader(getClasspathURLs(classpath), parent);
229 packageClasses = new PackageClasses(classpath);
230 platformLoader = (platform == null)
231 ? parent
232 : new URLClassLoader(getClasspathURLs(platform), parent);
233
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256 public Map getDependencyRelationshipMap(Collection rootClasses, boolean recurse)
257 throws IOException, ClassNotFoundException {
258 Map result = new HashMap();
259 Set seen = new HashSet();
260 Set compute = computeClasses(rootClasses);
261 while (!compute.isEmpty()) {
262 Set computeNext = new HashSet();
263 Iterator computeIterator = compute.iterator();
264 while (computeIterator.hasNext()) {
265 String name = (String) computeIterator.next();
266 if ( !seen.contains(name)){
267 seen.add(name);
268 if (rootClasses.contains(name)){
269
270 ClassDependencyRelationship rootClass = new ClassDependencyRelationship(name, true);
271 result.put(name, rootClass);
272 }
273 Set providerClassNames = new HashSet();
274 String resource = getResourceName(name);
275 if (recurse) {
276 InputStream in = loader.getResourceAsStream(resource);
277 if (in == null) {
278 noteClassNotFound(name);
279 } else {
280 try {
281
282 providerClassNames = ReferencedClasses.compute(
283 new BufferedInputStream(in));
284 computeNext.addAll(providerClassNames);
285 } catch (IOException e) {
286 noteClassLoadingFailed(name, e);
287 } finally {
288 try {
289 in.close();
290 } catch (IOException e) {
291 }
292 }
293 }
294 } else if (loader.getResource(resource) == null) {
295 noteClassNotFound(name);
296 }
297
298
299
300
301
302 Iterator iter = providerClassNames.iterator();
303 while (iter.hasNext()){
304 String provider = (String) iter.next();
305 ClassDependencyRelationship providerClass;
306 if (!result.containsKey(provider)){
307 providerClass = new ClassDependencyRelationship(provider);
308 result.put(provider, providerClass);
309 }else{
310 providerClass = (ClassDependencyRelationship) result.get(provider);
311 }
312 ((ClassDependencyRelationship) result.get(name)).addProvider(providerClass);
313 }
314 }
315 }
316
317
318
319 compute = computeNext;
320 }
321 return result;
322 }
323
324
325
326
327
328
329
330
331
332
333 public Set filterClassDependencyRelationShipMap(Map dependencyRelationShipMap, ClassDependParameters cdp){
334 Set result = new HashSet();
335 Set preliminaryResult = new HashSet();
336
337 Pattern excludePattern = createPattern(cdp.outsidePackagesOrClasses());
338 Pattern includePattern = createPattern(cdp.insidePackages());
339 Pattern hidePattern = createPattern(cdp.hidePackages());
340 Pattern showPattern = createPattern(cdp.showPackages());
341 Collection classRelations = dependencyRelationShipMap.values();
342
343 Set rootClasses = new HashSet();
344 {
345 Iterator itr = classRelations.iterator();
346 while (itr.hasNext()){
347 ClassDependencyRelationship cdr = (ClassDependencyRelationship) itr.next();
348 if (cdr.isRootClass()){
349 rootClasses.add(cdr);
350 }
351 }
352 }
353
354 while ( !rootClasses.isEmpty() ){
355 Set computeNext = new HashSet();
356 Iterator computeIterator = rootClasses.iterator();
357 while (computeIterator.hasNext()){
358 ClassDependencyRelationship cdr = (ClassDependencyRelationship) computeIterator.next();
359 String name = cdr.toString();
360
361 if ( !preliminaryResult.contains(cdr) &&
362 ( !cdp.excludePlatformClasses() || !classPresent(name, platformLoader) ) &&
363 !matches(name, excludePattern) &&
364 ( cdp.insidePackages().size() == 0 || matches(name, includePattern)))
365 {
366
367 preliminaryResult.add(cdr);
368 computeNext.addAll(cdr.getProviders());
369 }
370 }
371 rootClasses = computeNext;
372 }
373
374 if (cdp.edges()){
375 Iterator itr = preliminaryResult.iterator();
376 while (itr.hasNext()) {
377 ClassDependencyRelationship cdr = (ClassDependencyRelationship) itr.next();
378 result.addAll(cdr.getProviders());
379 }
380
381
382
383
384 result.removeAll(preliminaryResult);
385 }else{
386 result = preliminaryResult;
387 }
388
389
390
391
392 Set remove = new HashSet();
393 Iterator itr = result.iterator();
394 while (itr.hasNext()){
395 ClassDependencyRelationship cdr = (ClassDependencyRelationship) itr.next();
396 String name = cdr.toString();
397 if(( matches(name,hidePattern)|| ( showPattern != null && !matches(name, showPattern))))
398 {
399 remove.add(cdr);
400 }
401 }
402 result.removeAll(remove);
403 return result;
404 }
405
406
407
408
409
410
411
412
413
414
415 protected void noteClassNotFound(String name)
416 throws ClassNotFoundException
417 {
418 throw new ClassNotFoundException("Class not found: " + name);
419 }
420
421
422
423
424
425
426
427
428
429 protected void noteClassLoadingFailed(String name, IOException e)
430 throws IOException
431 {
432 throw e;
433 }
434
435
436
437
438
439 private Set computeClasses(Collection names)
440 throws IOException
441 {
442 Set result = new HashSet();
443 Iterator namesIterator = names.iterator();
444 while (namesIterator.hasNext()) {
445 String name = (String) namesIterator.next();
446 if (name.endsWith(".*")) {
447 name = name.substring(0, name.length() - 2);
448 result.addAll(packageClasses.compute(false, name));
449 } else if (name.endsWith(".**")) {
450 name = name.substring(0, name.length() - 3);
451 result.addAll(packageClasses.compute(true, name));
452 } else {
453 result.add(name);
454 }
455 }
456 return result;
457 }
458
459
460 private URL[] getClasspathURLs(String classpath)
461 throws MalformedURLException
462 {
463 StringTokenizer tokens =
464 new StringTokenizer(classpath, File.pathSeparator);
465 URL[] urls = new URL[tokens.countTokens()];
466 for (int i = 0; tokens.hasMoreTokens(); i++) {
467 String file = tokens.nextToken();
468 try {
469 urls[i] = new File(file).toURI().toURL();
470 } catch (MalformedURLException e) {
471 urls[i] = new URL(file);
472 }
473 }
474 return urls;
475 }
476
477
478 private boolean classPresent(String name, ClassLoader loader) {
479 return loader.getResource(getResourceName(name)) != null;
480 }
481
482
483 private String getResourceName(String classname) {
484 return classname.replace('.', '/').concat(".class");
485 }
486
487
488
489
490
491
492
493
494
495 public Pattern createPattern(Collection names) {
496 if (names.isEmpty()) {
497 return null;
498 }
499 StringBuffer sb = new StringBuffer();
500 boolean first = true;
501 Iterator namesItr = names.iterator();
502 while (namesItr.hasNext()) {
503 String name = (String) namesItr.next();
504 if (!first) {
505 sb.append('|');
506 } else {
507 first = false;
508 }
509 if (name.endsWith(".*")) {
510 sb.append(
511 quote( name.substring(0, name.length() - 1)) +
512 "[^.]+");
513 } else if (name.endsWith(".**")) {
514 sb.append(
515 quote(name.substring(0, name.length() - 2)) +
516 ".+");
517 } else {
518 sb.append(quote(name));
519 }
520 }
521 return Pattern.compile(sb.toString());
522 }
523
524
525
526
527
528
529
530
531 public boolean matches(String string, Pattern pattern) {
532 return pattern != null && pattern.matcher(string).matches();
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548 private String quote(String s) {
549 StringBuffer sb = new StringBuffer(s.length() * 2).append("\\Q");
550 int previousEndQuotationIndex = 0;
551 int endQuotationIndex = 0;
552 while ((endQuotationIndex = s.indexOf("\\E", previousEndQuotationIndex)) >= 0) {
553 sb.append(s.substring(previousEndQuotationIndex, endQuotationIndex));
554 sb.append("\\E\\\\E\\Q");
555 previousEndQuotationIndex = endQuotationIndex + 2;
556 }
557 sb.append(s.substring(previousEndQuotationIndex));
558 sb.append("\\E");
559 String literalPattern = sb.toString();
560 return literalPattern;
561 }
562
563 public boolean printClassesWithFileSeparator() {
564 return printClassesWithFileSeparator;
565 }
566
567 public void setPrintClassesWithFileSeparator(boolean printClassesWithFileSeparator) {
568 this.printClassesWithFileSeparator = printClassesWithFileSeparator;
569 }
570 }