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.metrics; |
34 | |
35 | import java.util.*; |
36 | |
37 | import org.apache.log4j.*; |
38 | import org.apache.oro.text.perl.*; |
39 | |
40 | import com.jeantessier.classreader.*; |
41 | |
42 | /** |
43 | * Collects metrics from Classfile instances. |
44 | * |
45 | * This class can only approximate SLOC based on information provided |
46 | * by the compiler. |
47 | */ |
48 | public class MetricsGatherer extends VisitorBase { |
49 | private static final Perl5Util perl = new Perl5Util(); |
50 | |
51 | private MetricsFactory factory; |
52 | |
53 | private Collection<String> scope = null; |
54 | private Collection<String> filter = null; |
55 | |
56 | private Metrics currentProject; |
57 | private Metrics currentGroup; |
58 | private Metrics currentClass; |
59 | private Metrics currentMethod; |
60 | |
61 | private int sloc; |
62 | private boolean isSynthetic; |
63 | |
64 | private HashSet<MetricsListener> metricsListeners = new HashSet<MetricsListener>(); |
65 | |
66 | public MetricsGatherer(MetricsFactory factory) { |
67 | this.factory = factory; |
68 | |
69 | setCurrentProject(getMetricsFactory().createProjectMetrics()); |
70 | } |
71 | |
72 | public MetricsFactory getMetricsFactory() { |
73 | return factory; |
74 | } |
75 | |
76 | public void setScopeIncludes(Collection<String> scope) { |
77 | this.scope = scope; |
78 | } |
79 | |
80 | public void setFilterIncludes(Collection<String> filter) { |
81 | this.filter = filter; |
82 | } |
83 | |
84 | private Metrics getCurrentProject() { |
85 | return currentProject; |
86 | } |
87 | |
88 | void setCurrentProject(Metrics currentProject) { |
89 | this.currentProject = currentProject; |
90 | } |
91 | |
92 | private Metrics getCurrentGroup() { |
93 | return currentGroup; |
94 | } |
95 | |
96 | void setCurrentGroup(Metrics currentGroup) { |
97 | this.currentGroup = currentGroup; |
98 | } |
99 | |
100 | private Metrics getCurrentClass() { |
101 | return currentClass; |
102 | } |
103 | |
104 | void setCurrentClass(Metrics currentClass) { |
105 | this.currentClass = currentClass; |
106 | } |
107 | |
108 | private Metrics getCurrentMethod() { |
109 | return currentMethod; |
110 | } |
111 | |
112 | void setCurrentMethod(Metrics currentMethod) { |
113 | this.currentMethod = currentMethod; |
114 | } |
115 | |
116 | public void visitClassfiles(Collection<Classfile> classfiles) { |
117 | fireBeginSession(classfiles.size()); |
118 | |
119 | super.visitClassfiles(classfiles); |
120 | |
121 | fireEndSession(); |
122 | } |
123 | |
124 | // Classfile |
125 | public void visitClassfile(Classfile classfile) { |
126 | String className = classfile.getClassName(); |
127 | Logger.getLogger(getClass()).debug("VisitClassfile():"); |
128 | Logger.getLogger(getClass()).debug(" class = \"" + className + "\""); |
129 | |
130 | fireBeginClass(classfile); |
131 | |
132 | setCurrentMethod(null); |
133 | setCurrentClass(getMetricsFactory().createClassMetrics(className)); |
134 | setCurrentGroup(getCurrentClass().getParent()); |
135 | setCurrentProject(getCurrentGroup().getParent()); |
136 | |
137 | getMetricsFactory().includeClassMetrics(getCurrentClass()); |
138 | |
139 | getCurrentProject().addToMeasurement(BasicMeasurements.PACKAGES, getCurrentGroup().getName()); |
140 | |
141 | if (classfile.isPublic()) { |
142 | getCurrentProject().addToMeasurement(BasicMeasurements.PUBLIC_CLASSES, className); |
143 | getCurrentGroup().addToMeasurement(BasicMeasurements.PUBLIC_CLASSES, className); |
144 | } else { |
145 | getCurrentProject().addToMeasurement(BasicMeasurements.PACKAGE_CLASSES, className); |
146 | getCurrentGroup().addToMeasurement(BasicMeasurements.PACKAGE_CLASSES, className); |
147 | } |
148 | |
149 | if (classfile.isFinal()) { |
150 | getCurrentProject().addToMeasurement(BasicMeasurements.FINAL_CLASSES, className); |
151 | getCurrentGroup().addToMeasurement(BasicMeasurements.FINAL_CLASSES, className); |
152 | } |
153 | |
154 | if (classfile.isSuper()) { |
155 | getCurrentProject().addToMeasurement(BasicMeasurements.SUPER_CLASSES, className); |
156 | getCurrentGroup().addToMeasurement(BasicMeasurements.SUPER_CLASSES, className); |
157 | } |
158 | |
159 | if (classfile.isInterface()) { |
160 | getCurrentProject().addToMeasurement(BasicMeasurements.INTERFACES, className); |
161 | getCurrentGroup().addToMeasurement(BasicMeasurements.INTERFACES, className); |
162 | } |
163 | |
164 | if (classfile.isAbstract()) { |
165 | getCurrentProject().addToMeasurement(BasicMeasurements.ABSTRACT_CLASSES, className); |
166 | getCurrentGroup().addToMeasurement(BasicMeasurements.ABSTRACT_CLASSES, className); |
167 | } |
168 | |
169 | if (classfile.isSynthetic()) { |
170 | getCurrentProject().addToMeasurement(BasicMeasurements.SYNTHETIC_CLASSES, className); |
171 | getCurrentGroup().addToMeasurement(BasicMeasurements.SYNTHETIC_CLASSES, className); |
172 | } |
173 | |
174 | if (classfile.getSuperclassIndex() != 0) { |
175 | classfile.getRawSuperclass().accept(this); |
176 | |
177 | getMetricsFactory().createClassMetrics(classfile.getSuperclassName()).addToMeasurement(BasicMeasurements.SUBCLASSES); |
178 | |
179 | Classfile superclass = classfile.getLoader().getClassfile(classfile.getSuperclassName()); |
180 | if (superclass != null) { |
181 | getCurrentClass().addToMeasurement(BasicMeasurements.DEPTH_OF_INHERITANCE, computeDepthOfInheritance(superclass)); |
182 | } |
183 | } |
184 | |
185 | for (Class_info class_info : classfile.getAllInterfaces()) { |
186 | class_info.accept(this); |
187 | } |
188 | |
189 | for (Field_info field : classfile.getAllFields()) { |
190 | field.accept(this); |
191 | } |
192 | |
193 | for (Method_info method : classfile.getAllMethods()) { |
194 | method.accept(this); |
195 | } |
196 | |
197 | sloc = 1; |
198 | |
199 | for (Attribute_info attribute : classfile.getAttributes()) { |
200 | attribute.accept(this); |
201 | } |
202 | |
203 | if (!classfile.isSynthetic()) { |
204 | getCurrentClass().addToMeasurement(BasicMeasurements.CLASS_SLOC, sloc); |
205 | } |
206 | |
207 | fireEndClass(classfile, getCurrentClass()); |
208 | } |
209 | |
210 | // ConstantPool entries |
211 | public void visitClass_info(Class_info entry) { |
212 | Logger.getLogger(getClass()).debug("VisitClass_info():"); |
213 | Logger.getLogger(getClass()).debug(" name = \"" + entry.getName() + "\""); |
214 | if (entry.getName().startsWith("[")) { |
215 | addClassDependencies(processDescriptor(entry.getName())); |
216 | } else { |
217 | addClassDependency(entry.getName()); |
218 | } |
219 | } |
220 | |
221 | public void visitFieldRef_info(FieldRef_info entry) { |
222 | Logger.getLogger(getClass()).debug("VisitFieldRef_info():"); |
223 | Logger.getLogger(getClass()).debug(" class = \"" + entry.getClassName() + "\""); |
224 | Logger.getLogger(getClass()).debug(" name = \"" + entry.getRawNameAndType().getName() + "\""); |
225 | Logger.getLogger(getClass()).debug(" type = \"" + entry.getRawNameAndType().getType() + "\""); |
226 | |
227 | // Dependencies on attributes are accounted as dependencies on their class |
228 | entry.getRawClass().accept(this); |
229 | addClassDependencies(processDescriptor(entry.getRawNameAndType().getType())); |
230 | } |
231 | |
232 | public void visitMethodRef_info(MethodRef_info entry) { |
233 | Logger.getLogger(getClass()).debug("VisitMethodRef_info():"); |
234 | Logger.getLogger(getClass()).debug(" class = \"" + entry.getClassName() + "\""); |
235 | Logger.getLogger(getClass()).debug(" name = \"" + entry.getRawNameAndType().getName() + "\""); |
236 | Logger.getLogger(getClass()).debug(" type = \"" + entry.getRawNameAndType().getType() + "\""); |
237 | addMethodDependency(entry.getFullSignature()); |
238 | addClassDependencies(processDescriptor(entry.getRawNameAndType().getType())); |
239 | } |
240 | |
241 | public void visitInterfaceMethodRef_info(InterfaceMethodRef_info entry) { |
242 | Logger.getLogger(getClass()).debug("VisitInterfaceMethodRef_info():"); |
243 | Logger.getLogger(getClass()).debug(" class = \"" + entry.getClassName() + "\""); |
244 | Logger.getLogger(getClass()).debug(" name = \"" + entry.getRawNameAndType().getName() + "\""); |
245 | Logger.getLogger(getClass()).debug(" type = \"" + entry.getRawNameAndType().getType() + "\""); |
246 | addMethodDependency(entry.getFullSignature()); |
247 | addClassDependencies(processDescriptor(entry.getRawNameAndType().getType())); |
248 | } |
249 | |
250 | public void visitField_info(Field_info entry) { |
251 | String fullName = entry.getFullName(); |
252 | getCurrentClass().addToMeasurement(BasicMeasurements.ATTRIBUTES, fullName); |
253 | |
254 | Logger.getLogger(getClass()).debug("VisitField_info(" + entry.getFullSignature() + ")"); |
255 | Logger.getLogger(getClass()).debug("Current class: " + getCurrentClass().getName()); |
256 | Logger.getLogger(getClass()).debug("Access flag: " + entry.getAccessFlag()); |
257 | Logger.getLogger(getClass()).debug("Public: " + entry.isPublic()); |
258 | Logger.getLogger(getClass()).debug("Private: " + entry.isPrivate()); |
259 | Logger.getLogger(getClass()).debug("Protected: " + entry.isProtected()); |
260 | Logger.getLogger(getClass()).debug("Static: " + entry.isStatic()); |
261 | |
262 | if (entry.isPublic()) { |
263 | getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_ATTRIBUTES, fullName); |
264 | } else if (entry.isPrivate()) { |
265 | getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_ATTRIBUTES, fullName); |
266 | } else if (entry.isProtected()) { |
267 | getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_ATTRIBUTES, fullName); |
268 | } else { |
269 | getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_ATTRIBUTES, fullName); |
270 | } |
271 | |
272 | if (entry.isStatic()) { |
273 | getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_ATTRIBUTES, fullName); |
274 | } |
275 | |
276 | if (entry.isFinal()) { |
277 | getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_ATTRIBUTES, fullName); |
278 | } |
279 | |
280 | if (entry.isVolatile()) { |
281 | getCurrentClass().addToMeasurement(BasicMeasurements.VOLATILE_ATTRIBUTES, fullName); |
282 | } |
283 | |
284 | if (entry.isTransient()) { |
285 | getCurrentClass().addToMeasurement(BasicMeasurements.TRANSIENT_ATTRIBUTES, fullName); |
286 | } |
287 | |
288 | sloc = 1; |
289 | isSynthetic = entry.isSynthetic(); |
290 | |
291 | super.visitField_info(entry); |
292 | |
293 | if (!isSynthetic) { |
294 | getCurrentClass().addToMeasurement(BasicMeasurements.CLASS_SLOC, sloc); |
295 | } |
296 | |
297 | addClassDependencies(processDescriptor(entry.getDescriptor())); |
298 | } |
299 | |
300 | public void visitMethod_info(Method_info entry) { |
301 | fireBeginMethod(entry); |
302 | |
303 | String fullSignature = entry.getFullSignature(); |
304 | setCurrentMethod(getMetricsFactory().createMethodMetrics(fullSignature)); |
305 | getMetricsFactory().includeMethodMetrics(getCurrentMethod()); |
306 | |
307 | Logger.getLogger(getClass()).debug("VisitMethod_info(" + fullSignature + ")"); |
308 | Logger.getLogger(getClass()).debug("Current class: " + getCurrentClass().getName()); |
309 | Logger.getLogger(getClass()).debug("Access flag: " + entry.getAccessFlag()); |
310 | Logger.getLogger(getClass()).debug("Public: " + entry.isPublic()); |
311 | Logger.getLogger(getClass()).debug("Private: " + entry.isPrivate()); |
312 | Logger.getLogger(getClass()).debug("Protected: " + entry.isProtected()); |
313 | Logger.getLogger(getClass()).debug("Static: " + entry.isStatic()); |
314 | |
315 | sloc = 0; |
316 | isSynthetic = entry.isSynthetic(); |
317 | |
318 | if (entry.isPublic()) { |
319 | getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_METHODS, fullSignature); |
320 | } else if (entry.isPrivate()) { |
321 | getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_METHODS, fullSignature); |
322 | } else if (entry.isProtected()) { |
323 | getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_METHODS, fullSignature); |
324 | } else { |
325 | getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_METHODS, fullSignature); |
326 | } |
327 | |
328 | if (entry.isStatic()) { |
329 | getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_METHODS, fullSignature); |
330 | } |
331 | |
332 | if (entry.isFinal()) { |
333 | getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_METHODS, fullSignature); |
334 | } |
335 | |
336 | if (entry.isSynchronized()) { |
337 | getCurrentClass().addToMeasurement(BasicMeasurements.SYNCHRONIZED_METHODS, fullSignature); |
338 | } |
339 | |
340 | if (entry.isNative()) { |
341 | getCurrentClass().addToMeasurement(BasicMeasurements.NATIVE_METHODS, fullSignature); |
342 | } |
343 | |
344 | if (entry.isAbstract()) { |
345 | getCurrentClass().addToMeasurement(BasicMeasurements.ABSTRACT_METHODS, fullSignature); |
346 | sloc = 1; |
347 | } |
348 | |
349 | getCurrentMethod().addToMeasurement(BasicMeasurements.PARAMETERS, DescriptorHelper.getParameterCount(entry.getDescriptor())); |
350 | |
351 | super.visitMethod_info(entry); |
352 | |
353 | if (!isSynthetic) { |
354 | getCurrentMethod().addToMeasurement(BasicMeasurements.SLOC, sloc); |
355 | } |
356 | |
357 | addClassDependencies(processDescriptor(entry.getDescriptor())); |
358 | |
359 | fireEndMethod(entry, getCurrentMethod()); |
360 | } |
361 | |
362 | // |
363 | // Attributes |
364 | // |
365 | |
366 | public void visitSynthetic_attribute(Synthetic_attribute attribute) { |
367 | Object owner = attribute.getOwner(); |
368 | |
369 | isSynthetic = true; |
370 | |
371 | if (owner instanceof Field_info) { |
372 | getCurrentClass().addToMeasurement(BasicMeasurements.SYNTHETIC_ATTRIBUTES, ((Field_info) owner).getFullName()); |
373 | } else if (owner instanceof Method_info) { |
374 | getCurrentClass().addToMeasurement(BasicMeasurements.SYNTHETIC_METHODS, ((Method_info) owner).getFullSignature()); |
375 | } else { |
376 | Logger.getLogger(getClass()).warn("Synthetic attribute on unknown Visitable: " + owner.getClass().getName()); |
377 | } |
378 | } |
379 | |
380 | public void visitDeprecated_attribute(Deprecated_attribute attribute) { |
381 | Object owner = attribute.getOwner(); |
382 | |
383 | if (owner instanceof Classfile) { |
384 | String className = ((Classfile) owner).getClassName(); |
385 | getCurrentProject().addToMeasurement(BasicMeasurements.DEPRECATED_CLASSES, className); |
386 | getCurrentGroup().addToMeasurement(BasicMeasurements.DEPRECATED_CLASSES, className); |
387 | } else if (owner instanceof Field_info) { |
388 | getCurrentClass().addToMeasurement(BasicMeasurements.DEPRECATED_ATTRIBUTES, ((Field_info) owner).getFullName()); |
389 | } else if (owner instanceof Method_info) { |
390 | getCurrentClass().addToMeasurement(BasicMeasurements.DEPRECATED_METHODS, ((Method_info) owner).getFullSignature()); |
391 | } else { |
392 | Logger.getLogger(getClass()).warn("Deprecated attribute on unknown Visitable: " + owner.getClass().getName()); |
393 | } |
394 | } |
395 | |
396 | // |
397 | // Attribute helpers |
398 | // |
399 | |
400 | public void visitInstruction(Instruction helper) { |
401 | super.visitInstruction(helper); |
402 | |
403 | /* |
404 | * We can skip the "new" (0xbb) instruction as it is always |
405 | * followed by a call to the constructor method. |
406 | */ |
407 | |
408 | switch (helper.getOpcode()) { |
409 | case 0x12: // ldc |
410 | case 0x13: // ldc_w |
411 | case 0xb2: // getstatic |
412 | case 0xb3: // putstatic |
413 | case 0xb4: // getfield |
414 | case 0xb5: // putfield |
415 | case 0xb6: // invokevirtual |
416 | case 0xb7: // invokespecial |
417 | case 0xb8: // invokestatic |
418 | case 0xb9: // invokeinterface |
419 | // case 0xbb: // new |
420 | case 0xbd: // anewarray |
421 | case 0xc0: // checkcast |
422 | case 0xc1: // instanceof |
423 | case 0xc5: // multianewarray |
424 | helper.getIndexedConstantPoolEntry().accept(this); |
425 | break; |
426 | default: |
427 | // Do nothing |
428 | break; |
429 | } |
430 | } |
431 | |
432 | public void visitExceptionHandler(ExceptionHandler helper) { |
433 | if (helper.getCatchTypeIndex() != 0) { |
434 | helper.getRawCatchType().accept(this); |
435 | } |
436 | } |
437 | |
438 | public void visitInnerClass(InnerClass helper) { |
439 | if (isInnerClassOfCurrentClass(helper)) { |
440 | String innerClassName = helper.getInnerClassInfo(); |
441 | |
442 | getCurrentProject().addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName); |
443 | getCurrentGroup().addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName); |
444 | getCurrentClass().addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName); |
445 | |
446 | if (helper.isPublic()) { |
447 | getCurrentProject().addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName); |
448 | getCurrentGroup().addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName); |
449 | getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName); |
450 | } else if (helper.isPrivate()) { |
451 | getCurrentProject().addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName); |
452 | getCurrentGroup().addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName); |
453 | getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName); |
454 | } else if (helper.isProtected()) { |
455 | getCurrentProject().addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName); |
456 | getCurrentGroup().addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName); |
457 | getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName); |
458 | } else { |
459 | getCurrentProject().addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName); |
460 | getCurrentGroup().addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName); |
461 | getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName); |
462 | } |
463 | |
464 | if (helper.isStatic()) { |
465 | getCurrentProject().addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName); |
466 | getCurrentGroup().addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName); |
467 | getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName); |
468 | } |
469 | |
470 | if (helper.isFinal()) { |
471 | getCurrentProject().addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName); |
472 | getCurrentGroup().addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName); |
473 | getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName); |
474 | } |
475 | |
476 | if (helper.isAbstract()) { |
477 | getCurrentProject().addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName); |
478 | getCurrentGroup().addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName); |
479 | getCurrentClass().addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName); |
480 | } |
481 | } |
482 | } |
483 | |
484 | // Package-level for testing |
485 | boolean isInnerClassOfCurrentClass(InnerClass helper) { |
486 | boolean result; |
487 | |
488 | if (helper.getOuterClassInfo().equals("")) { |
489 | result = perl.match("/^" + getCurrentClass().getName() + "\\$\\d+$/", helper.getInnerClassInfo()); |
490 | } else { |
491 | result = helper.getOuterClassInfo().equals(getCurrentClass().getName()); |
492 | } |
493 | |
494 | return result; |
495 | } |
496 | |
497 | public void visitLineNumber(LineNumber helper) { |
498 | sloc++; |
499 | } |
500 | |
501 | public void visitLocalVariable(LocalVariable helper) { |
502 | getCurrentMethod().addToMeasurement(BasicMeasurements.LOCAL_VARIABLES, helper.getName()); |
503 | |
504 | addClassDependencies(processDescriptor(helper.getDescriptor())); |
505 | } |
506 | |
507 | private int computeDepthOfInheritance(Classfile classfile) { |
508 | int result = 1; |
509 | |
510 | if (classfile != null && classfile.getSuperclassIndex() != 0) { |
511 | Classfile superclass = classfile.getLoader().getClassfile(classfile.getSuperclassName()); |
512 | result += computeDepthOfInheritance(superclass); |
513 | } |
514 | |
515 | return result; |
516 | } |
517 | |
518 | private Collection<String> processDescriptor(String str) { |
519 | Collection<String> result = new LinkedList<String>(); |
520 | |
521 | Logger.getLogger(getClass()).debug("ProcessDescriptor: " + str); |
522 | |
523 | int currentPos = 0; |
524 | int startPos; |
525 | int endPos; |
526 | |
527 | while ((startPos = str.indexOf('L', currentPos)) != -1) { |
528 | if ((endPos = str.indexOf(';', startPos)) != -1) { |
529 | String classname = ClassNameHelper.path2ClassName(str.substring(startPos + 1, endPos)); |
530 | result.add(classname); |
531 | currentPos = endPos + 1; |
532 | } else { |
533 | currentPos = startPos + 1; |
534 | } |
535 | } |
536 | |
537 | Logger.getLogger(getClass()).debug("ProcessDescriptor: " + result); |
538 | |
539 | return result; |
540 | } |
541 | |
542 | private void addClassDependencies(Collection<String> classnames) { |
543 | for (String classname : classnames) { |
544 | addClassDependency(classname); |
545 | } |
546 | } |
547 | |
548 | private void addClassDependency(String name) { |
549 | Logger.getLogger(getClass()).debug("AddClassDependency(\"" + name + "\") ..."); |
550 | |
551 | if (!getCurrentClass().getName().equals(name) && isInFilter(name)) { |
552 | Metrics other = getMetricsFactory().createClassMetrics(name); |
553 | |
554 | if (getCurrentMethod() != null && isInScope(getCurrentMethod().getName())) { |
555 | Logger.getLogger(getClass()).debug("AddClassDependency " + getCurrentMethod().getName() + " -> " + name + " ..."); |
556 | |
557 | if (getCurrentClass().getParent().equals(other.getParent())) { |
558 | Logger.getLogger(getClass()).debug("Intra-Package ..."); |
559 | getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_CLASS_DEPENDENCIES, other.getName()); |
560 | other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName()); |
561 | } else { |
562 | Logger.getLogger(getClass()).debug("Extra-Package ..."); |
563 | getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_CLASS_DEPENDENCIES, other.getName()); |
564 | other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName()); |
565 | } |
566 | } else if (isInScope(getCurrentClass().getName())) { |
567 | Logger.getLogger(getClass()).debug("AddClassDependency " + getCurrentClass().getName() + " -> " + name + " ..."); |
568 | |
569 | if (getCurrentClass().getParent().equals(other.getParent())) { |
570 | Logger.getLogger(getClass()).debug("Intra-Package ..."); |
571 | getCurrentClass().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_DEPENDENCIES, other.getName()); |
572 | other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_DEPENDENCIES, getCurrentClass().getName()); |
573 | } else { |
574 | Logger.getLogger(getClass()).debug("Extra-Package ..."); |
575 | getCurrentClass().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_DEPENDENCIES, other.getName()); |
576 | other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_DEPENDENCIES, getCurrentClass().getName()); |
577 | } |
578 | } |
579 | } |
580 | } |
581 | |
582 | private void addMethodDependency(String name) { |
583 | Logger.getLogger(getClass()).debug("AddMethodDependency " + getCurrentMethod().getName() + " -> " + name + " ..."); |
584 | |
585 | if (!getCurrentMethod().getName().equals(name) && isInScope(getCurrentMethod().getName()) && isInFilter(name)) { |
586 | Metrics other = getMetricsFactory().createMethodMetrics(name); |
587 | |
588 | if (getCurrentClass().equals(other.getParent())) { |
589 | Logger.getLogger(getClass()).debug("Intra-Class ..."); |
590 | getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_CLASS_FEATURE_DEPENDENCIES, other.getName()); |
591 | other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_CLASS_METHOD_DEPENDENCIES, getCurrentMethod().getName()); |
592 | } else if (getCurrentGroup().equals(other.getParent().getParent())) { |
593 | Logger.getLogger(getClass()).debug("Intra-Package ..."); |
594 | getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_FEATURE_DEPENDENCIES, other.getName()); |
595 | other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName()); |
596 | } else { |
597 | Logger.getLogger(getClass()).debug("Extra-Package ..."); |
598 | getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_FEATURE_DEPENDENCIES, other.getName()); |
599 | other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName()); |
600 | } |
601 | } |
602 | } |
603 | |
604 | private boolean isInScope(String name) { |
605 | boolean result = true; |
606 | |
607 | if (scope != null) { |
608 | result = scope.contains(name); |
609 | } |
610 | |
611 | return result; |
612 | } |
613 | |
614 | private boolean isInFilter(String name) { |
615 | boolean result = true; |
616 | |
617 | if (filter != null) { |
618 | result = filter.contains(name); |
619 | } |
620 | |
621 | return result; |
622 | } |
623 | |
624 | public void addMetricsListener(MetricsListener listener) { |
625 | synchronized(metricsListeners) { |
626 | metricsListeners.add(listener); |
627 | } |
628 | } |
629 | |
630 | public void removeMetricsListener(MetricsListener listener) { |
631 | synchronized(metricsListeners) { |
632 | metricsListeners.remove(listener); |
633 | } |
634 | } |
635 | |
636 | protected void fireBeginSession(int size) { |
637 | MetricsEvent event = new MetricsEvent(this, size); |
638 | for (MetricsListener listener : cloneListeners()) { |
639 | listener.beginSession(event); |
640 | } |
641 | } |
642 | |
643 | protected void fireBeginClass(Classfile classfile) { |
644 | MetricsEvent event = new MetricsEvent(this, classfile); |
645 | for (MetricsListener listener : cloneListeners()) { |
646 | listener.beginClass(event); |
647 | } |
648 | } |
649 | |
650 | protected void fireBeginMethod(Method_info method) { |
651 | MetricsEvent event = new MetricsEvent(this, method); |
652 | for (MetricsListener listener : cloneListeners()) { |
653 | listener.beginMethod(event); |
654 | } |
655 | } |
656 | |
657 | protected void fireEndMethod(Method_info method, Metrics metrics) { |
658 | MetricsEvent event = new MetricsEvent(this, method, metrics); |
659 | for (MetricsListener listener : cloneListeners()) { |
660 | listener.endMethod(event); |
661 | } |
662 | } |
663 | |
664 | protected void fireEndClass(Classfile classfile, Metrics metrics) { |
665 | MetricsEvent event = new MetricsEvent(this, classfile, metrics); |
666 | for (MetricsListener listener : cloneListeners()) { |
667 | listener.endClass(event); |
668 | } |
669 | } |
670 | |
671 | protected void fireEndSession() { |
672 | MetricsEvent event = new MetricsEvent(this); |
673 | for (MetricsListener listener : cloneListeners()) { |
674 | listener.endSession(event); |
675 | } |
676 | } |
677 | |
678 | private Collection<MetricsListener> cloneListeners() { |
679 | Collection<MetricsListener> result; |
680 | synchronized(metricsListeners) { |
681 | result = (Collection<MetricsListener>) metricsListeners.clone(); |
682 | } |
683 | return result; |
684 | } |
685 | } |