KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > classycle > Analyser


1 /*
2  * Copyright (c) 2003-2006, Franz-Josef Elmer, All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
15  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */

25 package classycle;
26
27 import java.io.FileWriter JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.text.DateFormat JavaDoc;
31 import java.text.SimpleDateFormat JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import classycle.graph.AtomicVertex;
39 import classycle.graph.StrongComponent;
40 import classycle.graph.StrongComponentAnalyser;
41 import classycle.renderer.AtomicVertexRenderer;
42 import classycle.renderer.PlainStrongComponentRenderer;
43 import classycle.renderer.StrongComponentRenderer;
44 import classycle.renderer.TemplateBasedClassRenderer;
45 import classycle.renderer.XMLClassRenderer;
46 import classycle.renderer.XMLStrongComponentRenderer;
47 import classycle.util.StringPattern;
48 import classycle.util.Text;
49 import classycle.util.TrueStringPattern;
50
51 /**
52  * Main class of the Classycle tool. Runs on the command line and
53  * produces a report.
54  *
55  * @author Franz-Josef Elmer
56  */

57 public class Analyser
58 {
59   private static final String JavaDoc VERSION = "1.3";
60   private static final DateFormat JavaDoc DATE_FORMAT
61                                         = new SimpleDateFormat JavaDoc("yyyy-MM-dd");
62   private static final String JavaDoc CSV_TEMPLATE
63                                       = "{0},{1},{3},{2},{4},{5},{6},{7}\n";
64   
65   private final String JavaDoc[] _classFiles;
66   private final StringPattern _pattern;
67   private final StringPattern _reflectionPattern;
68   private final boolean _mergeInnerClasses;
69   private StrongComponentAnalyser _classAnalyser;
70   private StrongComponentAnalyser _packageAnalyser;
71   
72   /**
73    * Creates an instance for the specified files or folders.
74    * @param classFiles Absolute or relative file names.
75    */

76   public Analyser(String JavaDoc[] classFiles)
77   {
78     this(classFiles, new TrueStringPattern(), null, false);
79   }
80
81   /**
82    * Creates an instance for the specified files or folders which are
83    * filtered by the specified {@link StringPattern} object.
84    * @param classFiles Absolute or relative file names.
85    * @param pattern Pattern fully-qualified class name have to match in order
86    * to be a part of the class graph.
87    * @param reflectionPattern Pattern ordinary string constants of a class
88    * file have to fullfill in order to be handled as a class references.
89    * In addition such strings have to be syntactically valid
90    * fully qualified class names. If <tt>null</tt> ordinary string
91    * constants will not be checked.
92    * @param mergeInnerClasses If <code>true</code>
93    * merge inner classes with its outer class
94    */

95   public Analyser(String JavaDoc[] classFiles, StringPattern pattern,
96                   StringPattern reflectionPattern, boolean mergeInnerClasses)
97   {
98     _classFiles = classFiles;
99     _pattern = pattern;
100     _reflectionPattern = reflectionPattern;
101     _mergeInnerClasses = mergeInnerClasses;
102   }
103   
104   /**
105    * Parses the class files and creates the class graph.
106    * @return the duration of this operation in milliseconds.
107    * @throws IOException if a problem occured during reading
108    */

109   public long createClassGraph() throws IOException JavaDoc
110   {
111     long time = System.currentTimeMillis();
112     AtomicVertex[] classGraph = Parser.readClassFiles(_classFiles, _pattern,
113                                                       _reflectionPattern,
114                                                       _mergeInnerClasses);
115     _classAnalyser = new StrongComponentAnalyser(classGraph);
116     return System.currentTimeMillis() - time;
117   }
118   
119   /**
120    * Returns the class graph. Invokes {@link #createClassGraph()} if not
121    * already invoked.
122    */

123   public AtomicVertex[] getClassGraph()
124   {
125     if (_classAnalyser == null)
126     {
127       try
128       {
129         createClassGraph();
130       } catch (IOException JavaDoc e)
131       {
132         throw new RuntimeException JavaDoc(e.toString());
133       }
134     }
135     return _classAnalyser.getGraph();
136   }
137   
138   /**
139    * Counts the number of external classes.
140    */

141   public int getNumberOfExternalClasses()
142   {
143     AtomicVertex[] graph = getClassGraph();
144     HashSet JavaDoc usedClasses = new HashSet JavaDoc();
145     int result = 0;
146     for (int i = 0; i < graph.length; i++)
147     {
148       AtomicVertex vertex = graph[i];
149       for (int j = 0, n = vertex.getNumberOfOutgoingArcs(); j < n; j++)
150       {
151         ClassAttributes attributes =
152             (ClassAttributes) vertex.getHeadVertex(j).getAttributes();
153         if (attributes.getType() == ClassAttributes.UNKNOWN)
154         {
155           if (!usedClasses.contains(attributes.getName()))
156           {
157             result++;
158             usedClasses.add(attributes.getName());
159           }
160         }
161       }
162     }
163     return result;
164   }
165
166   /**
167    * Condenses the class graph to an acyclic graph of its strong components.
168    * @return the duration of this operation in milliseconds.
169    * @throws IllegalStateException if this method is called before
170    * {@link #createClassGraph()}.
171    */

172   public long condenseClassGraph()
173   {
174     checkClassGraph("condenseClassGraph()");
175     long time = System.currentTimeMillis();
176     _classAnalyser.getCondensedGraph();
177     return System.currentTimeMillis() - time;
178   }
179
180   /**
181    * Returns the condensed the class graph, i.e.&nbsp;the acyclic graph of
182    * its strong components.
183    * @throws IllegalStateException if this method is called before
184    * {@link #createClassGraph()}.
185    */

186   public StrongComponent[] getCondensedClassGraph()
187   {
188     checkClassGraph("getCondenseClassGraph()");
189     return _classAnalyser.getCondensedGraph();
190   }
191
192   /**
193    * Calculates the for each class its layer index. The layer index of a
194    * class is the length of the longest path in the acyclic graph of strong
195    * components starting at the strong component to which the class belongs.
196    * @return the duration of this operation in milliseconds.
197    * @throws IllegalStateException if this method is called before
198    * {@link #createClassGraph()}.
199    */

200   public long calculateClassLayerMap()
201   {
202     checkClassGraph("calculateClassLayerMap()");
203     long time = System.currentTimeMillis();
204     _classAnalyser.getLayerMap();
205     return System.currentTimeMillis() - time;
206   }
207
208   /**
209    * Calculates the for each class its layer index and returns
210    * a <tt>Map</tt> where the classes are the keys (type {@link AtomicVertex})
211    * and the layer indices are the values (type <tt>Integer</tt>).
212    * @throws IllegalStateException if this method is called before
213    * {@link #createClassGraph()}.
214    */

215   public Map JavaDoc getClassLayerMap()
216   {
217     checkClassGraph("getClassLayerMap()");
218     return _classAnalyser.getLayerMap();
219   }
220
221   /**
222    * Creates the package graph from the class graph.
223    * @return the duration of this operation in milliseconds.
224    * @throws IllegalStateException if this method is called before
225    * {@link #createClassGraph()}.
226    */

227   public long createPackageGraph()
228   {
229     checkClassGraph("createPackageGraph()");
230     long time = System.currentTimeMillis();
231     PackageProcessor processor = new PackageProcessor();
232     processor.deepSearchFirst(_classAnalyser.getGraph());
233     _packageAnalyser = new StrongComponentAnalyser(processor.getGraph());
234     return System.currentTimeMillis() - time;
235   }
236   
237   /**
238    * Prints a CSV report into the specified writer. Delimiter is ','.
239    * First, a header with column titles is print. The columns are
240    * <ol><li>class name
241    * <li>inner class (<tt>false</tt> or <tt>true</tt>)
242    * <li>size (in bytes)
243    * <li>used by (number of classes using this class)
244    * <li>uses internal classes
245    * (number of classes of the graph used by this class)
246    * <li>uses external classes
247    * (number of external classes used by this class)
248    * <li>layer index
249    * </ol>
250    * @param writer Output stream.
251    */

252   public void printCSV(PrintWriter JavaDoc writer)
253   {
254     AtomicVertex[] graph = getClassGraph();
255     Map JavaDoc map = getClassLayerMap();
256     writer.println("class name,type,inner class,size,used by,"
257             + "uses internal classes,uses external classes,layer index");
258     render(graph, null, map, new TemplateBasedClassRenderer(CSV_TEMPLATE), writer);
259     writer.close();
260   }
261
262   /**
263    * Prints for each class a raw output into the specified writer.
264    * This output includes all classes used by the class.
265    * @param writer Output stream.
266    */

267   public void printRaw(PrintWriter JavaDoc writer)
268   {
269     AtomicVertex[] graph = getClassGraph();
270     for (int i = 0; i < graph.length; i++)
271     {
272       AtomicVertex vertex = graph[i];
273       writer.println(vertex.getAttributes());
274       for (int j = 0, n = vertex.getNumberOfOutgoingArcs(); j < n; j++)
275       {
276         writer.println(" " + vertex.getHeadVertex(j).getAttributes());
277       }
278     }
279     writer.close();
280   }
281
282   /**
283    * Prints for each strong component of the class graph a raw output into
284    * the specified writer. The strong component must have at least
285    * <tt>minSize</tt> classes in order to be printed out.
286    * This output includes all classes of the strong component.
287    * @param writer Output stream.
288    * @param minSize Minimum size of the strong component.
289    * @throws IllegalStateException if this method is called before
290    * {@link #createClassGraph()}.
291    */

292   public void printComponents(PrintWriter JavaDoc writer, int minSize)
293   {
294     checkClassGraph("printComponents()");
295     StrongComponent[] components = getCondensedClassGraph();
296     StrongComponentRenderer renderer = new PlainStrongComponentRenderer();
297     for (int i = 0; i < components.length; i++)
298     {
299       StrongComponent component = components[i];
300       if (component.getNumberOfVertices() >= minSize)
301       {
302         writer.println(renderer.render(component));
303       }
304     }
305     writer.close();
306   }
307
308   private void checkClassGraph(String JavaDoc method)
309   {
310     if (_classAnalyser == null)
311     {
312       throw new IllegalStateException JavaDoc(
313                   method + " should be invoked after createClassGraph().");
314     }
315   }
316
317   /**
318    * Returns the package graph created the class graph.
319    * @throws IllegalStateException if this method is called before
320    * {@link #createClassGraph()}.
321    */

322   public AtomicVertex[] getPackageGraph()
323   {
324     if (_packageAnalyser == null)
325     {
326       createPackageGraph();
327     }
328     return _packageAnalyser.getGraph();
329   }
330   
331   /**
332    * Condenses the package graph to an acyclic graph of its strong components.
333    * @return the duration of this operation in milliseconds.
334    * @throws IllegalStateException if this method is called before
335    * {@link #createPackageGraph()}.
336    */

337   public long condensePackageGraph()
338   {
339     checkPackageGraph("condensePackageGraph()");
340     long time = System.currentTimeMillis();
341     _packageAnalyser.getCondensedGraph();
342     return System.currentTimeMillis() - time;
343   }
344
345   /**
346    * Returns the condensed package graph, i.e.&nbsp;the acyclic graph of
347    * its strong components.
348    * @return the duration of this operation in milliseconds.
349    * @throws IllegalStateException if this method is called before
350    * {@link #createPackageGraph()}.
351    */

352   public StrongComponent[] getCondensedPackageGraph()
353   {
354     checkPackageGraph("getCondensedPackageGraph()");
355     return _packageAnalyser.getCondensedGraph();
356   }
357
358   /**
359    * Calculates the for each package its layer index. The layer index of a
360    * package is the length of the longest path in the acyclic graph of strong
361    * components starting at the strong component to which the package belongs.
362    * @return the duration of this operation in milliseconds.
363    * @throws IllegalStateException if this method is called before
364    * {@link #createPackageGraph()}.
365    */

366   public long calculatePackageLayerMap()
367   {
368     checkPackageGraph("calculatePackageLayerMap()");
369     long time = System.currentTimeMillis();
370     _packageAnalyser.getLayerMap();
371     return System.currentTimeMillis() - time;
372   }
373
374   /**
375    * Calculates the for each package its layer index and returns
376    * a <tt>Map</tt> where the packages are the keys (type {@link AtomicVertex})
377    * and the layer indices are the values (type <tt>Integer</tt>).
378    * @throws IllegalStateException if this method is called before
379    * {@link #createPackageGraph()}.
380    */

381   public Map JavaDoc getPackageLayerMap()
382   {
383     checkPackageGraph("getPackageLayerMap()");
384     return _packageAnalyser.getLayerMap();
385   }
386
387   /**
388    * Reads and analyses class files. Does only package analysis if
389    * <tt>packagesOnly == true</tt>. Reports progress of analysis on
390    * <tt>System.out</tt>.
391    * @throws IOException in case of reading problems.
392    */

393   public void readAndAnalyse(boolean packagesOnly) throws IOException JavaDoc
394   {
395     System.out.println("============== Classycle V" + VERSION
396                        + " ==============");
397     System.out.println("=========== by Franz-Josef Elmer ===========");
398     System.out.print("read class files and create class graph ... ");
399     long duration = createClassGraph();
400     System.out.println("done after " + duration + " ms: "
401                        + getClassGraph().length + " classes analysed.");
402     
403     if (!packagesOnly)
404     {
405       // Condense class graph
406
System.out.print("condense class graph ... ");
407       duration = condenseClassGraph();
408       System.out.println("done after " + duration + " ms: "
409                          + getCondensedClassGraph().length
410                          + " strong components found.");
411     
412       // Calculate class layer
413
System.out.print("calculate class layer indices ... ");
414       duration = calculateClassLayerMap();
415       System.out.println("done after " + duration + " ms.");
416     }
417     System.out.print("create package graph ... ");
418     duration = createPackageGraph();
419     System.out.println("done after " + duration + " ms: "
420                        + getPackageGraph().length + " packages.");
421     // Condense package graph
422
System.out.print("condense package graph ... ");
423     duration = condensePackageGraph();
424     System.out.println("done after " + duration + " ms: "
425                        + getCondensedPackageGraph().length
426                        + " strong components found.");
427     // Calculate package layer
428
System.out.print("calculate package layer indices ... ");
429     duration = calculatePackageLayerMap();
430     System.out.println("done after " + duration + " ms.");
431   }
432
433   /**
434    * Prints an XML report into the specified writer.
435    * @param title Title of the report.
436    * @param packagesOnly if <tt>true</tt> classes are omitted.
437    * @param writer Output stream.
438    * @throws IllegalStateException if this method is called before
439    * {@link #createPackageGraph()}.
440    */

441   public void printXML(String JavaDoc title, boolean packagesOnly, PrintWriter JavaDoc writer)
442   {
443     checkPackageGraph("printXML()");
444     writer.println("<?xml version='1.0' encoding='UTF-8'?>");
445     writer.println("<?xml-stylesheet type='text/xsl' "
446                    + "href='reportXMLtoHTML.xsl'?>");
447     writer.print("<classycle title='");
448     writer.print(Text.excapeForXML(title));
449     writer.print("' date='");
450     writer.print(DATE_FORMAT.format(new Date JavaDoc()));
451     writer.println("'>");
452     if (!packagesOnly)
453     {
454       StrongComponent[] components = getCondensedClassGraph();
455       writer.println(" <cycles>");
456       StrongComponentRenderer sRenderer
457           = new XMLStrongComponentRenderer(2);
458       for (int i = 0; i < components.length; i++)
459       {
460         writer.print(sRenderer.render(components[i]));
461       }
462       writer.println(" </cycles>");
463       writer.println(" <classes numberOfExternalClasses=\""
464                      + getNumberOfExternalClasses() + "\">");
465       AtomicVertex[] graph = getClassGraph();
466       Map JavaDoc layerMap = getClassLayerMap();
467       render(graph, components, layerMap, new XMLClassRenderer(), writer);
468       writer.println(" </classes>");
469     }
470     StrongComponent[] components = getCondensedPackageGraph();
471     writer.println(" <packageCycles>");
472     StrongComponentRenderer sRenderer
473           = new XMLPackageStrongComponentRenderer(2);
474     for (int i = 0; i < components.length; i++)
475     {
476       writer.print(sRenderer.render(components[i]));
477     }
478     writer.println(" </packageCycles>");
479     writer.println(" <packages>");
480     AtomicVertex[] graph = getPackageGraph();
481     Map JavaDoc layerMap = getPackageLayerMap();
482     render(graph, components, layerMap, new XMLPackageRenderer(), writer);
483     writer.println(" </packages>");
484     
485     writer.println("</classycle>");
486     writer.close();
487   }
488
489   private void render(AtomicVertex[] graph, StrongComponent[] cycles,
490           Map JavaDoc layerMap, AtomicVertexRenderer renderer, PrintWriter JavaDoc writer)
491   {
492     List JavaDoc list = getTrueCycles(cycles);
493     for (int i = 0; i < graph.length; i++)
494     {
495       AtomicVertex vertex = graph[i];
496       Integer JavaDoc layerIndex = (Integer JavaDoc) layerMap.get(vertex);
497       writer.print(renderer.render(vertex,
498                          getCycleFor(vertex, list),
499                          layerIndex == null ? -1 : layerIndex.intValue()));
500     }
501   }
502   
503   private List JavaDoc getTrueCycles(StrongComponent[] cycles)
504   {
505     List JavaDoc list = new ArrayList JavaDoc();
506     if (cycles != null)
507     {
508       for (int i = 0; i < cycles.length; i++)
509       {
510         if (cycles[i].getNumberOfVertices() > 1)
511         {
512           list.add(cycles[i]);
513         }
514       }
515     }
516     return list;
517   }
518
519   private StrongComponent getCycleFor(AtomicVertex vertex, List JavaDoc cycles)
520   {
521     for (int i = 0, n = cycles.size(); i < n; i++)
522     {
523       StrongComponent cycle = (StrongComponent) cycles.get(i);
524       for (int j = 0, m = cycle.getNumberOfVertices(); j < m; j++)
525       {
526         if (cycle.getVertex(j) == vertex)
527         {
528           return cycle;
529         }
530       }
531     }
532     return null;
533   }
534
535   private void checkPackageGraph(String JavaDoc method)
536   {
537     if (_packageAnalyser == null)
538     {
539       throw new IllegalStateException JavaDoc(
540                   method + " should be invoked after createPackageGraph().");
541     }
542   }
543
544   /**
545    * Main method of the Analyser. Prints on the console its usage if
546    * some invalid command line argument occurs or is missed.
547    * @param args command line arguments.
548    * @throws Exception
549    */

550   public static void main(String JavaDoc[] args) throws Exception JavaDoc {
551     AnalyserCommandLine commandLine = new AnalyserCommandLine(args);
552     if (!commandLine.isValid()) {
553       System.out.println("Usage: java -jar classycle.jar "
554                          + commandLine.getUsage());
555       System.exit(0);
556     }
557     
558     Analyser analyser = new Analyser(commandLine.getClassFiles(),
559                                      commandLine.getPattern(),
560                                      commandLine.getReflectionPattern(),
561                                      commandLine.isMergeInnerClasses());
562     analyser.readAndAnalyse(commandLine.isPackagesOnly());
563
564     // Create report(s)
565
if (commandLine.getXmlFile() != null)
566     {
567       analyser.printXML(commandLine.getTitle(), commandLine.isPackagesOnly(),
568                     new PrintWriter JavaDoc(new FileWriter JavaDoc(commandLine.getXmlFile())));
569     }
570     if (commandLine.getCsvFile() != null)
571     {
572       analyser.printCSV(
573                   new PrintWriter JavaDoc(new FileWriter JavaDoc(commandLine.getCsvFile())));
574     }
575     if (commandLine.isRaw())
576     {
577       analyser.printRaw(new PrintWriter JavaDoc(System.out));
578     }
579     if (commandLine.isCycles() || commandLine.isStrong())
580     {
581       analyser.printComponents(new PrintWriter JavaDoc(System.out),
582                                commandLine.isCycles() ? 2 : 1);
583     }
584   }
585 } //class
586
Popular Tags