KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jdepend > framework > JDepend


1 package jdepend.framework;
2
3 import java.io.IOException JavaDoc;
4 import java.util.*;
5
6 /**
7  * The <code>JDepend</code> class analyzes directories of Java class files
8  * and generates the following metrics for each Java package.
9  * <p>
10  * <ul>
11  * <li>Afferent Coupling (Ca)
12  * <p>
13  * The number of packages that depend upon the classes within the analyzed
14  * package.
15  * </p>
16  * </li>
17  * <li>Efferent Coupling (Ce)
18  * <p>
19  * The number of packages that the classes in the analyzed package depend upon.
20  * </p>
21  * </li>
22  * <li>Abstractness (A)
23  * <p>
24  * The ratio of the number of abstract classes (and interfaces) in the analyzed
25  * package to the total number of classes in the analyzed package.
26  * </p>
27  * <p>
28  * The range for this metric is 0 to 1, with A=0 indicating a completely
29  * concrete package and A=1 indicating a completely abstract package.
30  * </p>
31  * </li>
32  * <li>Instability (I)
33  * <p>
34  * The ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that I =
35  * Ce / (Ce + Ca).
36  * </p>
37  * <p>
38  * The range for this metric is 0 to 1, with I=0 indicating a completely stable
39  * package and I=1 indicating a completely instable package.
40  * </p>
41  * </li>
42  * <li>Distance from the Main Sequence (D)
43  * <p>
44  * The perpendicular distance of a package from the idealized line A + I = 1. A
45  * package coincident with the main sequence is optimally balanced with respect
46  * to its abstractness and stability. Ideal packages are either completely
47  * abstract and stable (x=0, y=1) or completely concrete and instable (x=1,
48  * y=0).
49  * </p>
50  * <p>
51  * The range for this metric is 0 to 1, with D=0 indicating a package that is
52  * coincident with the main sequence and D=1 indicating a package that is as far
53  * from the main sequence as possible.
54  * </p>
55  * </li>
56  * <li>Package Dependency Cycle
57  * <p>
58  * Package dependency cycles are reported along with the paths of packages
59  * participating in package dependency cycles.
60  * </p>
61  * </li>
62  * </ul>
63  * <p>
64  * These metrics are hereafter referred to as the "Martin Metrics", as they are
65  * credited to Robert Martin (Object Mentor Inc.) and referenced in the book
66  * "Designing Object Oriented C++ Applications using the Booch Method", by
67  * Robert C. Martin, Prentice Hall, 1995.
68  * </p>
69  * <p>
70  * Example API use:
71  * <p>
72  * <blockquote>
73  *
74  * <pre>
75  * JDepend jdepend = new JDepend();
76  * jdepend.addDirectory(&quot;/path/to/classes&quot;);
77  * Collection packages = jdepend.analyze();
78  *
79  * Iterator i = packages.iterator();
80  * while (i.hasNext()) {
81  * JavaPackage jPackage = (JavaPackage) i.next();
82  * String name = jPackage.getName();
83  * int Ca = jPackage.afferentCoupling();
84  * int Ce = jPackage.efferentCoupling();
85  * float A = jPackage.abstractness();
86  * float I = jPackage.instability();
87  * float D = jPackage.distance();
88  * boolean b = jPackage.containsCycle();
89  * }
90  * </pre>
91  *
92  * </blockquote>
93  * </p>
94  * <p>
95  * This class is the data model used by the <code>jdepend.textui.JDepend</code>
96  * and <code>jdepend.swingui.JDepend</code> views.
97  * </p>
98  *
99  * @author <b>Mike Clark</b>
100  * @author Clarkware Consulting, Inc.
101  */

102
103 public class JDepend {
104
105     private HashMap packages;
106     private FileManager fileManager;
107     private PackageFilter filter;
108     private ClassFileParser parser;
109     private JavaClassBuilder builder;
110     private Collection components;
111
112     public JDepend() {
113         this(new PackageFilter());
114     }
115
116     public JDepend(PackageFilter filter) {
117
118         setFilter(filter);
119
120         this.packages = new HashMap();
121         this.fileManager = new FileManager();
122
123         this.parser = new ClassFileParser(filter);
124         this.builder = new JavaClassBuilder(parser, fileManager);
125
126         PropertyConfigurator config = new PropertyConfigurator();
127         addPackages(config.getConfiguredPackages());
128         analyzeInnerClasses(config.getAnalyzeInnerClasses());
129     }
130     
131     /**
132      * Analyzes the registered directories and returns the collection of
133      * analyzed packages.
134      *
135      * @return Collection of analyzed packages.
136      */

137     public Collection analyze() {
138
139         Collection classes = builder.build();
140         
141         for (Iterator i = classes.iterator(); i.hasNext();) {
142             analyzeClass((JavaClass)i.next());
143         }
144
145         return getPackages();
146     }
147
148     /**
149      * Adds the specified directory name to the collection of directories to be
150      * analyzed.
151      *
152      * @param name Directory name.
153      * @throws IOException If the directory is invalid.
154      */

155     public void addDirectory(String JavaDoc name) throws IOException JavaDoc {
156         fileManager.addDirectory(name);
157     }
158     
159     /**
160      * Sets the list of components.
161      *
162      * @param components Comma-separated list of components.
163      */

164     public void setComponents(String JavaDoc components) {
165         this.components = new ArrayList();
166         StringTokenizer st = new StringTokenizer(components, ",");
167         while (st.hasMoreTokens()) {
168             String JavaDoc component = st.nextToken();
169             this.components.add(component);
170         }
171     }
172
173     /**
174      * Determines whether inner classes are analyzed.
175      *
176      * @param b <code>true</code> to analyze inner classes;
177      * <code>false</code> otherwise.
178      */

179     public void analyzeInnerClasses(boolean b) {
180         fileManager.acceptInnerClasses(b);
181     }
182
183     /**
184      * Returns the collection of analyzed packages.
185      *
186      * @return Collection of analyzed packages.
187      */

188     public Collection getPackages() {
189         return packages.values();
190     }
191
192     /**
193      * Returns the analyzed package of the specified name.
194      *
195      * @param name Package name.
196      * @return Package, or <code>null</code> if the package was not analyzed.
197      */

198     public JavaPackage getPackage(String JavaDoc name) {
199         return (JavaPackage)packages.get(name);
200     }
201
202     /**
203      * Returns the number of analyzed Java packages.
204      *
205      * @return Number of Java packages.
206      */

207     public int countPackages() {
208         return getPackages().size();
209     }
210
211     /**
212      * Returns the number of registered Java classes to be analyzed.
213      *
214      * @return Number of classes.
215      */

216     public int countClasses() {
217         return builder.countClasses();
218     }
219
220     /**
221      * Indicates whether the packages contain one or more dependency cycles.
222      *
223      * @return <code>true</code> if one or more dependency cycles exist.
224      */

225     public boolean containsCycles() {
226         for (Iterator i = getPackages().iterator(); i.hasNext();) {
227             JavaPackage jPackage = (JavaPackage)i.next();
228             if (jPackage.containsCycle()) {
229                 return true;
230             }
231         }
232
233         return false;
234     }
235
236     /**
237      * Indicates whether the analyzed packages match the specified
238      * dependency constraint.
239      *
240      * @return <code>true</code> if the packages match the dependency
241      * constraint
242      */

243     public boolean dependencyMatch(DependencyConstraint constraint) {
244         return constraint.match(getPackages());
245     }
246
247     /**
248      * Registers the specified parser listener.
249      *
250      * @param listener Parser listener.
251      */

252     public void addParseListener(ParserListener listener) {
253         parser.addParseListener(listener);
254     }
255
256     /**
257      * Adds the specified Java package name to the collection of analyzed
258      * packages.
259      *
260      * @param name Java package name.
261      * @return Added Java package.
262      */

263     public JavaPackage addPackage(String JavaDoc name) {
264         name = toComponent(name);
265         JavaPackage pkg = (JavaPackage)packages.get(name);
266         if (pkg == null) {
267             pkg = new JavaPackage(name);
268             addPackage(pkg);
269         }
270
271         return pkg;
272     }
273
274     private String JavaDoc toComponent(String JavaDoc packageName) {
275         if (components != null) {
276             for (Iterator i = components.iterator(); i.hasNext();) {
277                 String JavaDoc component = (String JavaDoc)i.next();
278                 if (packageName.startsWith(component + ".")) {
279                     return component;
280                 }
281             }
282         }
283         return packageName;
284     }
285
286     /**
287      * Adds the specified collection of packages to the collection
288      * of analyzed packages.
289      *
290      * @param packages Collection of packages.
291      */

292     public void addPackages(Collection packages) {
293         for (Iterator i = packages.iterator(); i.hasNext();) {
294             JavaPackage pkg = (JavaPackage)i.next();
295             addPackage(pkg);
296         }
297     }
298
299     /**
300      * Adds the specified Java package to the collection of
301      * analyzed packages.
302      *
303      * @param pkg Java package.
304      */

305     public void addPackage(JavaPackage pkg) {
306         if (!packages.containsValue(pkg)) {
307             packages.put(pkg.getName(), pkg);
308         }
309     }
310
311     public PackageFilter getFilter() {
312         if (filter == null) {
313             filter = new PackageFilter();
314         }
315
316         return filter;
317     }
318
319     public void setFilter(PackageFilter filter) {
320         if (parser != null) {
321             parser.setFilter(filter);
322         }
323         this.filter = filter;
324     }
325
326     private void analyzeClass(JavaClass clazz) {
327
328         String JavaDoc packageName = clazz.getPackageName();
329
330         if (!getFilter().accept(packageName)) {
331             return;
332         }
333
334         JavaPackage clazzPackage = addPackage(packageName);
335         clazzPackage.addClass(clazz);
336
337         Collection imports = clazz.getImportedPackages();
338         for (Iterator i = imports.iterator(); i.hasNext();) {
339             JavaPackage importedPackage = (JavaPackage)i.next();
340             importedPackage = addPackage(importedPackage.getName());
341             clazzPackage.dependsUpon(importedPackage);
342         }
343     }
344 }
345
Popular Tags