root/trunk/src/java/org/jcoderz/phoenix/report/JDepend.java

Revision 1011, 11.0 kB (checked in by amandel, 4 years ago)

Aligned svn keyword settings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.phoenix.report;
34
35import java.io.File;
36import java.io.FileNotFoundException;
37import java.io.FileOutputStream;
38import java.io.PrintWriter;
39import java.util.ArrayList;
40import java.util.Iterator;
41import java.util.List;
42
43import org.apache.bcel.classfile.Constant;
44import org.apache.bcel.classfile.ConstantClass;
45import org.apache.bcel.classfile.ConstantPool;
46import org.apache.bcel.classfile.JavaClass;
47import org.apache.bcel.util.ClassLoader;
48import org.apache.bcel.util.ClassLoaderRepository;
49
50/**
51 * @author Michael Griffel
52 */
53public final class JDepend
54{
55   private static final ClassLoaderRepository REPOSITORY
56         = new ClassLoaderRepository(new ClassLoader());
57
58   private static final char[] PROPELLER_CHARS
59         = new char[]{'\\', '|', '/', '-'};
60   
61   private final List mClassDependencies = new ArrayList();
62   private final List mAllClasses = new ArrayList();
63   private final List mPackageDependency = new ArrayList();
64
65   private String mOutputFilename = "out.dot";
66   private File mClassDir = new File("build/classes");
67   private String mIncludePattern = ".*";
68   private String mExcludePattern = ""; 
69
70   private int mPropellerIndex = 0;
71   
72   private JDepend ()
73   {
74   }
75
76   public static void main (String[] args) 
77         throws ClassNotFoundException, FileNotFoundException
78   {
79      final JDepend main = new JDepend();
80      main.parseArgs(args); 
81      main.findClasses();
82      main.writeDotFile();
83   }
84
85   private void parseArgs (String[] args)
86   {
87      for (int i = 0; i < args.length; i++)
88      {
89         if (args[i].equals("-out"))
90         {
91            mOutputFilename = args[i + 1];
92         }
93         else if (args[i].equals("-classes"))
94         {
95            mClassDir = new File(args[i + 1]);
96            if (! mClassDir.isDirectory())
97            {
98               throw new IllegalArgumentException("The argument '" + mClassDir
99                     + "' for parameter classdir is not a valid directory");
100            }
101         }
102         else if (args[i].equals("-include"))
103         {
104            mIncludePattern = args[i + 1];
105         }
106         else if (args[i].equals("-exclude"))
107         {
108            mExcludePattern = args[i + 1];
109         }
110         else if (args[i].startsWith("-h") 
111                  || args[i].startsWith("--h") 
112                  || args[i].indexOf('?') != -1)
113         {
114            help();
115         }
116         else
117         {
118            throw new IllegalArgumentException(
119                    "Unknown parameter '" + args[i] + "'");
120         } 
121         ++i;
122      }
123   }
124
125   private static void help ()
126   {
127      System.err.println("     _ ____                            _"); 
128      System.err.println("    | |  _ \\  ___ _ __   ___ _ __   __| |");
129      System.err.println(" _  | | | | |/ _ \\ \'_ \\ / _ \\ \'_ \\ / _` |"); 
130      System.err.println("| |_| | |_| |  __/ |_) |  __/ | | | (_| |"); 
131      System.err.println(" \\___/|____/ \\___| .__/ \\___|_| |_|\\__,_|"); 
132      System.err.println("                 |_|");
133      System.err.println("   )=- The Java Dependency Checker -=(");
134      System.err.println();
135      System.err.println("Usage:");
136      System.err.println(
137              " -classes DIR      ... path to class directory [build/classes]");
138      System.err.println(
139              " -out FILE         ... path to the output file [out.dot]");
140      System.err.println(
141              " -include PATTERN  ... regex for include packages [.*]");
142      System.err.println(
143              " -exclude PATTERN  ... regex for exclude packages []");
144      System.err.println("");
145      System.exit(-1);
146   }
147
148   public void writeDotFile () 
149         throws FileNotFoundException
150   {
151      final PrintWriter out
152          = new PrintWriter(new FileOutputStream(mOutputFilename));
153      out.println("digraph G  {\n" 
154            + "  center=\"\"\n" 
155            + "  node[width=.25,hight=.375,fontsize=10,shape=box]");
156      for (final Iterator iterator = mPackageDependency.iterator(); 
157          iterator.hasNext();)
158      {
159         final PackageDependency d = (PackageDependency) iterator.next();
160         out.println("   " + d.toDotString() 
161             + " [fontname=\"verdana\", fontcolor=\"black\", fontsize=10.0];");
162      }
163      out.println("}");
164      out.close();
165   }
166
167   private void visit (String clazz) 
168         throws ClassNotFoundException
169   {
170      final JavaClass javaClass = REPOSITORY.loadClass(clazz);
171      final ConstantPool constantPool = javaClass.getConstantPool();
172      mAllClasses.add(clazz);
173      propeller();
174      final Constant[] constant = constantPool.getConstantPool();
175      for (int i = 0; i < constant.length; i++)
176      {
177         final Constant c = constant[i];
178         if (c instanceof ConstantClass)
179         {
180             final ConstantClass constantClass = (ConstantClass) c;
181           
182            String newClazz
183               = (String) constantClass.getConstantValue(constantPool);
184            newClazz = newClazz.replace('/', '.');
185           
186            if (mAllClasses.contains(newClazz))
187            {
188               continue;
189            }
190           
191   
192            if (!newClazz.matches(mIncludePattern)
193                || newClazz.startsWith("[") 
194                || newClazz.matches(mExcludePattern))
195            {
196               //System.out.println("Skipping '" + newClazz + "'");
197               continue;
198            }
199           
200            final JDepend.ClassDependency x
201                  = new JDepend.ClassDependency(clazz, newClazz);
202            final JDepend.PackageDependency p
203                  = new JDepend.PackageDependency(clazz, newClazz);
204            if (! mPackageDependency.contains(p))
205            {
206               mPackageDependency.add(p);
207            }
208           
209            if (! mClassDependencies.contains(x))
210            {
211               mClassDependencies.add(x);
212               //System.out.println("visit: " + newClazz);
213               visit(newClazz);
214            }
215         }
216      }
217
218   }
219
220   /**
221    *
222    */
223   private void propeller ()
224   {
225      System.out.print("\b");
226      System.out.print(
227              PROPELLER_CHARS[mPropellerIndex++ % PROPELLER_CHARS.length]);
228   }
229
230   public void findClasses ()
231   {
232      System.out.print("Scanning classpath ...  ");
233      findClasses(mClassDir, null);
234      System.out.println();
235      System.out.println("Scanned " + mAllClasses.size() + " classes.");
236   }
237
238   private void findClasses (File dir, String pkg) 
239   {
240      final File[] files = dir.listFiles();
241      for (int i = 0; i < files.length; i++)
242      {
243         if (files[i].isDirectory())
244         {
245            final String newpkg;
246            if (pkg == null)
247            {
248               newpkg = files[i].getName();
249            }
250            else
251            {
252               newpkg = pkg + "." + files[i].getName();
253            }
254            findClasses(files[i], newpkg);
255         }
256         else if (files[i].getName().endsWith(".class") 
257                  && files[i].getName().indexOf('$') == -1)
258         {
259            final String filename = files[i].getName();
260            final String clazz = pkg + "." 
261                  + filename.substring(0, 
262                          filename.length() - ".class".length());
263            try
264            {
265               visit(clazz);
266            }
267            catch (ClassNotFoundException ignore)
268            {
269               // classes that are not on the classpath are ignored.
270            }
271         }
272      }
273   }   
274   
275   
276   public static final class ClassDependency
277   {
278      private final String mClazz;
279      private final String mDependsClazz;
280
281      ClassDependency (String c, String dc)
282      {
283         mClazz = c;
284         mDependsClazz = dc;
285      }
286     
287      public String toString ()
288      {
289         return mClazz + " -> " + mDependsClazz;
290      }
291
292      public String toDotString ()
293      {
294         return "\"" + mClazz + "\"" +  " -> " + "\"" + mDependsClazz + "\"";
295      }
296
297      /** {@inheritDoc} */
298      public boolean equals (Object obj)
299      {
300         if (! (obj instanceof JDepend.ClassDependency))
301         {
302            return false;
303         }
304         final JDepend.ClassDependency o = (JDepend.ClassDependency) obj;
305         
306         return o.mClazz.equals(mClazz) 
307                 && o.mDependsClazz.equals(mDependsClazz);
308      }
309     
310      /** {@inheritDoc} */
311      public int hashCode ()
312      {
313         return mClazz.hashCode() + mDependsClazz.hashCode();
314      }
315   }
316
317   public static final class PackageDependency
318   {
319      private final String mPackage;
320      private final String mDependsPackage;
321
322      PackageDependency (String c, String dc)
323      {
324         mPackage = c.substring(0, c.lastIndexOf('.'));
325         mDependsPackage = dc.substring(0, dc.lastIndexOf('.'));
326      }
327     
328      public String toString ()
329      {
330         return mPackage + " -> " + mDependsPackage;
331      }
332
333      public String toDotString ()
334      {
335         return "\"" + mPackage + "\"" +  " -> " + "\"" 
336                 + mDependsPackage + "\"";
337      }
338
339      /** {@inheritDoc} */
340      public boolean equals (Object obj)
341      {
342         if (! (obj instanceof JDepend.PackageDependency))
343         {
344            return false;
345         }
346         final JDepend.PackageDependency o = (JDepend.PackageDependency) obj;
347         
348         return o.mPackage.equals(mPackage) 
349             && o.mDependsPackage.equals(mDependsPackage);
350      }
351     
352      /** {@inheritDoc} */
353      public int hashCode ()
354      {
355         return mPackage.hashCode() + mDependsPackage.hashCode();
356      }
357   }
358}
Note: See TracBrowser for help on using the browser.