MetricsGatherer.java
/*
* Copyright (c) 2001-2024, Jean Tessier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Jean Tessier nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jeantessier.metrics;
import com.jeantessier.classreader.*;
import org.apache.logging.log4j.*;
import org.apache.oro.text.perl.Perl5Util;
import java.util.*;
import java.util.stream.Stream;
/**
* <p>Collects metrics from Classfile instances.</p>
*
* <p>This class can only approximate SLOC based on information provided
* by the compiler.</p>
*/
public class MetricsGatherer extends VisitorBase {
private static final Perl5Util perl = new Perl5Util();
private final MetricsFactory factory;
private Collection<String> scope = null;
private Collection<String> filter = null;
private Metrics currentProject;
private Metrics currentGroup;
private Metrics currentClass;
private Metrics currentMethod;
private int sloc;
private final Collection<MetricsListener> metricsListeners = new HashSet<>();
public MetricsGatherer(MetricsFactory factory) {
this.factory = factory;
setCurrentProject(getMetricsFactory().createProjectMetrics());
}
public MetricsFactory getMetricsFactory() {
return factory;
}
public void setScopeIncludes(Collection<String> scope) {
this.scope = scope;
}
public void setFilterIncludes(Collection<String> filter) {
this.filter = filter;
}
private Metrics getCurrentProject() {
return currentProject;
}
void setCurrentProject(Metrics currentProject) {
this.currentProject = currentProject;
}
private Metrics getCurrentGroup() {
return currentGroup;
}
void setCurrentGroup(Metrics currentGroup) {
this.currentGroup = currentGroup;
}
private Metrics getCurrentClass() {
return currentClass;
}
void setCurrentClass(Metrics currentClass) {
this.currentClass = currentClass;
}
private Metrics getCurrentMethod() {
return currentMethod;
}
void setCurrentMethod(Metrics currentMethod) {
this.currentMethod = currentMethod;
}
private Collection<Metrics> getAllMatchingGroups(String className) {
return Stream.concat(
Stream.of(getCurrentGroup()),
getMetricsFactory().getGroupMetrics(className).stream()
).toList();
}
public void visitClassfiles(Collection<Classfile> classfiles) {
fireBeginSession(classfiles.size());
super.visitClassfiles(classfiles);
fireEndSession();
}
// Classfile
public void visitClassfile(Classfile classfile) {
String className = classfile.getClassName();
LogManager.getLogger(getClass()).debug("visitClassfile():");
LogManager.getLogger(getClass()).debug(" class = \"{}\"", className);
LogManager.getLogger(getClass()).debug(" access flag: {}", () -> classfile.getAccessFlags());
LogManager.getLogger(getClass()).debug(" public: {}", () -> classfile.isPublic());
LogManager.getLogger(getClass()).debug(" final: {}", () -> classfile.isFinal());
LogManager.getLogger(getClass()).debug(" super: {}", () -> classfile.isSuper());
LogManager.getLogger(getClass()).debug(" interface: {}", () -> classfile.isInterface());
LogManager.getLogger(getClass()).debug(" abstract: {}", () -> classfile.isAbstract());
LogManager.getLogger(getClass()).debug(" synthetic: {}", () -> classfile.isSynthetic());
LogManager.getLogger(getClass()).debug(" annotation: {}", () -> classfile.isAnnotation());
LogManager.getLogger(getClass()).debug(" enum: {}", () -> classfile.isEnum());
LogManager.getLogger(getClass()).debug(" module: {}", () -> classfile.isModule());
fireBeginClass(classfile);
setCurrentMethod(null);
setCurrentClass(getMetricsFactory().createClassMetrics(className));
setCurrentGroup(getCurrentClass().getParent());
setCurrentProject(getCurrentGroup().getParent());
getMetricsFactory().includeClassMetrics(getCurrentClass());
getCurrentClass().addToMeasurement(BasicMeasurements.MAJOR_VERSION, classfile.getMajorVersion());
getCurrentClass().addToMeasurement(BasicMeasurements.MINOR_VERSION, classfile.getMinorVersion());
Collection<Metrics> groups = getAllMatchingGroups(className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PACKAGES, getCurrentGroup().getName()));
getCurrentProject().addToMeasurement(BasicMeasurements.CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.CLASSES, className));
if (classfile.isPublic()) {
getCurrentProject().addToMeasurement(BasicMeasurements.PUBLIC_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PUBLIC_CLASSES, className));
} else {
getCurrentProject().addToMeasurement(BasicMeasurements.PACKAGE_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PACKAGE_CLASSES, className));
}
if (classfile.isFinal()) {
getCurrentProject().addToMeasurement(BasicMeasurements.FINAL_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.FINAL_CLASSES, className));
}
if (classfile.isSuper()) {
getCurrentProject().addToMeasurement(BasicMeasurements.SUPER_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.SUPER_CLASSES, className));
}
if (classfile.isInterface()) {
getCurrentProject().addToMeasurement(BasicMeasurements.INTERFACES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.INTERFACES, className));
}
if (classfile.isAbstract()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ABSTRACT_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ABSTRACT_CLASSES, className));
}
if (classfile.isSynthetic()) {
getCurrentProject().addToMeasurement(BasicMeasurements.SYNTHETIC_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.SYNTHETIC_CLASSES, className));
}
if (classfile.isAnnotation()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ANNOTATION_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ANNOTATION_CLASSES, className));
}
if (classfile.isEnum()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ENUM_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ENUM_CLASSES, className));
}
if (classfile.isModule()) {
getCurrentProject().addToMeasurement(BasicMeasurements.MODULE_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.MODULE_CLASSES, className));
}
if (classfile.isDeprecated()) {
getCurrentProject().addToMeasurement(BasicMeasurements.DEPRECATED_CLASSES, className);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.DEPRECATED_CLASSES, className));
}
if (classfile.hasSuperclass()) {
classfile.getRawSuperclass().accept(this);
getMetricsFactory().createClassMetrics(classfile.getSuperclassName()).addToMeasurement(BasicMeasurements.SUBCLASSES);
Classfile superclass = classfile.getLoader().getClassfile(classfile.getSuperclassName());
if (superclass != null) {
getCurrentClass().addToMeasurement(BasicMeasurements.DEPTH_OF_INHERITANCE, computeDepthOfInheritance(superclass));
}
}
classfile.getAllInterfaces().forEach(class_info -> class_info.accept(this));
classfile.getAllFields().forEach(field -> field.accept(this));
classfile.getAllMethods().forEach(method -> method.accept(this));
sloc = 1;
classfile.getAttributes().forEach(attribute -> attribute.accept(this));
if (!classfile.isSynthetic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.CLASS_SLOC, sloc);
}
fireEndClass(classfile, getCurrentClass());
}
// ConstantPool entries
public void visitClass_info(Class_info entry) {
LogManager.getLogger(getClass()).debug("visitClass_info():");
LogManager.getLogger(getClass()).debug(" name = \"{}\"", () -> entry.getName());
if (entry.getName().startsWith("[")) {
addClassDependencies(processDescriptor(entry.getName()));
} else {
addClassDependency(entry.getName());
}
}
public void visitFieldRef_info(FieldRef_info entry) {
LogManager.getLogger(getClass()).debug("visitFieldRef_info():");
LogManager.getLogger(getClass()).debug(" class = \"{}\"", () -> entry.getClassName());
LogManager.getLogger(getClass()).debug(" name = \"{}\"", () -> entry.getRawNameAndType().getName());
LogManager.getLogger(getClass()).debug(" type = \"{}\"", () -> entry.getRawNameAndType().getType());
// Dependencies on attributes are accounted as dependencies on their class
entry.getRawClass().accept(this);
addClassDependencies(processDescriptor(entry.getRawNameAndType().getType()));
}
public void visitMethodRef_info(MethodRef_info entry) {
LogManager.getLogger(getClass()).debug("visitMethodRef_info():");
LogManager.getLogger(getClass()).debug(" class = \"{}\"", () -> entry.getClassName());
LogManager.getLogger(getClass()).debug(" name = \"{}\"", () -> entry.getRawNameAndType().getName());
LogManager.getLogger(getClass()).debug(" type = \"{}\"", () -> entry.getRawNameAndType().getType());
addMethodDependency(entry.getFullUniqueName());
addClassDependencies(processDescriptor(entry.getRawNameAndType().getType()));
}
public void visitInterfaceMethodRef_info(InterfaceMethodRef_info entry) {
LogManager.getLogger(getClass()).debug("visitInterfaceMethodRef_info():");
LogManager.getLogger(getClass()).debug(" class = \"{}\"", () -> entry.getClassName());
LogManager.getLogger(getClass()).debug(" name = \"{}\"", () -> entry.getRawNameAndType().getName());
LogManager.getLogger(getClass()).debug(" type = \"{}\"", () -> entry.getRawNameAndType().getType());
addMethodDependency(entry.getFullUniqueName());
addClassDependencies(processDescriptor(entry.getRawNameAndType().getType()));
}
public void visitField_info(Field_info entry) {
String fullUniqueName = entry.getFullUniqueName();
LogManager.getLogger(getClass()).debug("visitField_info({})", () -> entry.getFullSignature());
LogManager.getLogger(getClass()).debug(" current class: {}", () -> getCurrentClass().getName());
LogManager.getLogger(getClass()).debug(" access flag: {}", () -> entry.getAccessFlags());
LogManager.getLogger(getClass()).debug(" public: {}", () -> entry.isPublic());
LogManager.getLogger(getClass()).debug(" private: {}", () -> entry.isPrivate());
LogManager.getLogger(getClass()).debug(" protected: {}", () -> entry.isProtected());
LogManager.getLogger(getClass()).debug(" static: {}", () -> entry.isStatic());
LogManager.getLogger(getClass()).debug(" final: {}", () -> entry.isFinal());
LogManager.getLogger(getClass()).debug(" volatile: {}", () -> entry.isVolatile());
LogManager.getLogger(getClass()).debug(" transient: {}", () -> entry.isTransient());
LogManager.getLogger(getClass()).debug(" synthetic: {}", () -> entry.isSynthetic());
LogManager.getLogger(getClass()).debug(" enum: {}", () -> entry.isEnum());
getCurrentClass().addToMeasurement(BasicMeasurements.ATTRIBUTES, fullUniqueName);
if (entry.isPublic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_ATTRIBUTES, fullUniqueName);
} else if (entry.isPrivate()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_ATTRIBUTES, fullUniqueName);
} else if (entry.isProtected()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_ATTRIBUTES, fullUniqueName);
} else {
getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_ATTRIBUTES, fullUniqueName);
}
if (entry.isFinal()) {
getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_ATTRIBUTES, fullUniqueName);
}
if (entry.isDeprecated()) {
getCurrentClass().addToMeasurement(BasicMeasurements.DEPRECATED_ATTRIBUTES, fullUniqueName);
}
if (entry.isSynthetic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.SYNTHETIC_ATTRIBUTES, fullUniqueName);
}
if (entry.isStatic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_ATTRIBUTES, fullUniqueName);
}
if (entry.isTransient()) {
getCurrentClass().addToMeasurement(BasicMeasurements.TRANSIENT_ATTRIBUTES, fullUniqueName);
}
if (entry.isVolatile()) {
getCurrentClass().addToMeasurement(BasicMeasurements.VOLATILE_ATTRIBUTES, fullUniqueName);
}
if (entry.isEnum()) {
getCurrentClass().addToMeasurement(BasicMeasurements.ENUM_ATTRIBUTES, fullUniqueName);
}
sloc = 1;
super.visitField_info(entry);
if (!entry.isSynthetic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.CLASS_SLOC, sloc);
}
addClassDependencies(processDescriptor(entry.getDescriptor()));
}
public void visitMethod_info(Method_info entry) {
fireBeginMethod(entry);
setCurrentMethod(getMetricsFactory().createMethodMetrics(entry.getFullUniqueName()));
getMetricsFactory().includeMethodMetrics(getCurrentMethod());
LogManager.getLogger(getClass()).debug("visitMethod_info({})", () -> entry.getFullSignature());
LogManager.getLogger(getClass()).debug(" current class: {}", () -> getCurrentClass().getName());
LogManager.getLogger(getClass()).debug(" access flag: {}", () -> entry.getAccessFlags());
LogManager.getLogger(getClass()).debug(" public: {}", () -> entry.isPublic());
LogManager.getLogger(getClass()).debug(" private: {}", () -> entry.isPrivate());
LogManager.getLogger(getClass()).debug(" protected: {}", () -> entry.isProtected());
LogManager.getLogger(getClass()).debug(" static: {}", () -> entry.isStatic());
LogManager.getLogger(getClass()).debug(" final: {}", () -> entry.isFinal());
LogManager.getLogger(getClass()).debug(" synchronized: {}", () -> entry.isSynchronized());
LogManager.getLogger(getClass()).debug(" bridge: {}", () -> entry.isBridge());
LogManager.getLogger(getClass()).debug(" varars: {}", () -> entry.isVarargs());
LogManager.getLogger(getClass()).debug(" native: {}", () -> entry.isNative());
LogManager.getLogger(getClass()).debug(" abstract: {}", () -> entry.isAbstract());
LogManager.getLogger(getClass()).debug(" strict: {}", () -> entry.isStrict());
LogManager.getLogger(getClass()).debug(" synthetic: {}", () -> entry.isSynthetic());
sloc = 0;
getCurrentClass().addToMeasurement(BasicMeasurements.METHODS, getCurrentMethod().getName());
if (entry.isPublic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_METHODS, getCurrentMethod().getName());
} else if (entry.isPrivate()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_METHODS, getCurrentMethod().getName());
} else if (entry.isProtected()) {
getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_METHODS, getCurrentMethod().getName());
} else {
getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_METHODS, getCurrentMethod().getName());
}
if (entry.isFinal()) {
getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_METHODS, getCurrentMethod().getName());
}
if (entry.isAbstract()) {
getCurrentClass().addToMeasurement(BasicMeasurements.ABSTRACT_METHODS, getCurrentMethod().getName());
sloc = 1;
}
if (entry.isDeprecated()) {
getCurrentClass().addToMeasurement(BasicMeasurements.DEPRECATED_METHODS, getCurrentMethod().getName());
}
if (entry.isSynthetic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.SYNTHETIC_METHODS, getCurrentMethod().getName());
}
if (entry.isStatic()) {
getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_METHODS, getCurrentMethod().getName());
}
if (entry.isSynchronized()) {
getCurrentClass().addToMeasurement(BasicMeasurements.SYNCHRONIZED_METHODS, getCurrentMethod().getName());
}
if (entry.isBridge()) {
getCurrentClass().addToMeasurement(BasicMeasurements.BRIDGE_METHODS, getCurrentMethod().getName());
}
if (entry.isVarargs()) {
getCurrentClass().addToMeasurement(BasicMeasurements.VARARGS_METHODS, getCurrentMethod().getName());
}
if (entry.isNative()) {
getCurrentClass().addToMeasurement(BasicMeasurements.NATIVE_METHODS, getCurrentMethod().getName());
}
if (entry.isStrict()) {
getCurrentClass().addToMeasurement(BasicMeasurements.STRICT_METHODS, getCurrentMethod().getName());
}
getCurrentMethod().addToMeasurement(BasicMeasurements.PARAMETERS, DescriptorHelper.getParameterCount(entry.getDescriptor()));
super.visitMethod_info(entry);
if (!entry.isSynthetic()) {
getCurrentMethod().addToMeasurement(BasicMeasurements.SLOC, sloc);
}
addClassDependencies(processDescriptor(entry.getDescriptor()));
fireEndMethod(entry, getCurrentMethod());
}
//
// Attributes
//
//
// Attribute helpers
//
public void visitInstruction(Instruction helper) {
super.visitInstruction(helper);
/*
* We can skip the "new" (0xbb) instruction as it is always
* followed by a call to the constructor method.
*/
switch (helper.getOpcode()) {
case 0x12: // ldc
case 0x13: // ldc_w
case 0xb2: // getstatic
case 0xb3: // putstatic
case 0xb4: // getfield
case 0xb5: // putfield
case 0xb6: // invokevirtual
case 0xb7: // invokespecial
case 0xb8: // invokestatic
case 0xb9: // invokeinterface
// case 0xbb: // new
case 0xbd: // anewarray
case 0xc0: // checkcast
case 0xc1: // instanceof
case 0xc5: // multianewarray
helper.getIndexedConstantPoolEntry().accept(this);
break;
default:
// Do nothing
break;
}
}
public void visitExceptionHandler(ExceptionHandler helper) {
if (helper.hasCatchType()) {
helper.getRawCatchType().accept(this);
}
}
public void visitInnerClass(InnerClass helper) {
if (isInnerClassOfCurrentClass(helper)) {
String innerClassName = helper.getInnerClassInfo();
Collection<Metrics> groups = getAllMatchingGroups(innerClassName);
getCurrentProject().addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.INNER_CLASSES, innerClassName);
if (helper.isPublic()) {
getCurrentProject().addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.PUBLIC_INNER_CLASSES, innerClassName);
} else if (helper.isPrivate()) {
getCurrentProject().addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.PRIVATE_INNER_CLASSES, innerClassName);
} else if (helper.isProtected()) {
getCurrentProject().addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.PROTECTED_INNER_CLASSES, innerClassName);
} else {
getCurrentProject().addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.PACKAGE_INNER_CLASSES, innerClassName);
}
if (helper.isStatic()) {
getCurrentProject().addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.STATIC_INNER_CLASSES, innerClassName);
}
if (helper.isFinal()) {
getCurrentProject().addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.FINAL_INNER_CLASSES, innerClassName);
}
if (helper.isInterface()) {
getCurrentProject().addToMeasurement(BasicMeasurements.INTERFACE_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.INTERFACE_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.INTERFACE_INNER_CLASSES, innerClassName);
}
if (helper.isAbstract()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.ABSTRACT_INNER_CLASSES, innerClassName);
}
if (helper.isSynthetic()) {
getCurrentProject().addToMeasurement(BasicMeasurements.SYNTHETIC_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.SYNTHETIC_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.SYNTHETIC_INNER_CLASSES, innerClassName);
}
if (helper.isAnnotation()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ANNOTATION_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ANNOTATION_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.ANNOTATION_INNER_CLASSES, innerClassName);
}
if (helper.isEnum()) {
getCurrentProject().addToMeasurement(BasicMeasurements.ENUM_INNER_CLASSES, innerClassName);
groups.forEach(group -> group.addToMeasurement(BasicMeasurements.ENUM_INNER_CLASSES, innerClassName));
getCurrentClass().addToMeasurement(BasicMeasurements.ENUM_INNER_CLASSES, innerClassName);
}
}
}
// Package-level for testing
boolean isInnerClassOfCurrentClass(InnerClass helper) {
boolean result;
if (helper.hasOuterClassInfo()) {
result = helper.getOuterClassInfo().equals(getCurrentClass().getName());
} else {
result = perl.match("/^" + getCurrentClass().getName() + "\\$\\d+$/", helper.getInnerClassInfo());
}
return result;
}
public void visitLineNumber(LineNumber helper) {
sloc++;
}
public void visitLocalVariable(LocalVariable helper) {
LogManager.getLogger(getClass()).debug("visitLocalVariable({})", () -> helper.getName());
getCurrentMethod().addToMeasurement(BasicMeasurements.LOCAL_VARIABLES, helper.getName());
addClassDependencies(processDescriptor(helper.getDescriptor()));
}
private int computeDepthOfInheritance(Classfile classfile) {
int result = 1;
if (classfile != null && classfile.hasSuperclass()) {
Classfile superclass = classfile.getLoader().getClassfile(classfile.getSuperclassName());
result += computeDepthOfInheritance(superclass);
}
return result;
}
private Collection<String> processDescriptor(String str) {
Collection<String> result = new LinkedList<>();
LogManager.getLogger(getClass()).debug("processDescriptor(\"{}\")", str);
int currentPos = 0;
int startPos;
int endPos;
while ((startPos = str.indexOf('L', currentPos)) != -1) {
if ((endPos = str.indexOf(';', startPos)) != -1) {
String classname = ClassNameHelper.path2ClassName(str.substring(startPos + 1, endPos));
result.add(classname);
currentPos = endPos + 1;
} else {
currentPos = startPos + 1;
}
}
LogManager.getLogger(getClass()).debug(" Parses to {}", result);
return result;
}
private void addClassDependencies(Collection<String> classnames) {
classnames.forEach(this::addClassDependency);
}
private void addClassDependency(String name) {
LogManager.getLogger(getClass()).debug("addClassDependency(\"{}\"):", name);
if (!getCurrentClass().getName().equals(name) && isInFilter(name)) {
Metrics other = getMetricsFactory().createClassMetrics(name);
if (getCurrentMethod() != null && isInScope(getCurrentMethod().getName())) {
LogManager.getLogger(getClass()).debug(" add class dependency {} --> {}", getCurrentMethod().getName(), name);
if (getCurrentClass().getParent().equals(other.getParent())) {
LogManager.getLogger(getClass()).debug(" Intra-Package!");
getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_CLASS_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName());
} else {
LogManager.getLogger(getClass()).debug(" Extra-Package!");
getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_CLASS_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName());
}
} else if (isInScope(getCurrentClass().getName())) {
LogManager.getLogger(getClass()).debug(" add class dependency {} --> {}", getCurrentClass().getName(), name);
if (getCurrentClass().getParent().equals(other.getParent())) {
LogManager.getLogger(getClass()).debug(" Intra-Package!");
getCurrentClass().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_DEPENDENCIES, getCurrentClass().getName());
} else {
LogManager.getLogger(getClass()).debug(" Extra-Package!");
getCurrentClass().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_DEPENDENCIES, getCurrentClass().getName());
}
} else {
LogManager.getLogger(getClass()).debug(" skipping class dependency {} --> {}", getCurrentClass().getName(), name);
}
} else {
LogManager.getLogger(getClass()).debug(" skipping class dependency {} --> {}", getCurrentClass().getName(), name);
}
}
private void addMethodDependency(String name) {
LogManager.getLogger(getClass()).debug("addMethodDependency(\"{}\"):", name);
if (!getCurrentMethod().getName().equals(name) && isInScope(getCurrentMethod().getName()) && isInFilter(name)) {
LogManager.getLogger(getClass()).debug(" add method dependency {} --> {}", getCurrentMethod().getName(), name);
Metrics other = getMetricsFactory().createMethodMetrics(name);
if (getCurrentClass().equals(other.getParent())) {
LogManager.getLogger(getClass()).debug(" Intra-Class!");
getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_CLASS_FEATURE_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_CLASS_METHOD_DEPENDENCIES, getCurrentMethod().getName());
} else if (getCurrentGroup().equals(other.getParent().getParent())) {
LogManager.getLogger(getClass()).debug(" Intra-Package!");
getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_INTRA_PACKAGE_FEATURE_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_INTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName());
} else {
LogManager.getLogger(getClass()).debug(" Extra-Package!");
getCurrentMethod().addToMeasurement(BasicMeasurements.OUTBOUND_EXTRA_PACKAGE_FEATURE_DEPENDENCIES, other.getName());
other.addToMeasurement(BasicMeasurements.INBOUND_EXTRA_PACKAGE_METHOD_DEPENDENCIES, getCurrentMethod().getName());
}
} else {
LogManager.getLogger(getClass()).debug(" skipping method dependency {} --> {}", getCurrentMethod().getName(), name);
}
}
private boolean isInScope(String name) {
boolean result = true;
if (scope != null) {
result = scope.contains(name);
}
return result;
}
private boolean isInFilter(String name) {
boolean result = true;
if (filter != null) {
result = filter.contains(name);
}
return result;
}
public void addMetricsListener(MetricsListener listener) {
metricsListeners.add(listener);
}
public void removeMetricsListener(MetricsListener listener) {
metricsListeners.remove(listener);
}
protected void fireBeginSession(int size) {
MetricsEvent event = new MetricsEvent(this, size);
metricsListeners.forEach(listener -> listener.beginSession(event));
}
protected void fireBeginClass(Classfile classfile) {
MetricsEvent event = new MetricsEvent(this, classfile);
metricsListeners.forEach(listener -> listener.beginClass(event));
}
protected void fireBeginMethod(Method_info method) {
MetricsEvent event = new MetricsEvent(this, method);
metricsListeners.forEach(listener -> listener.beginMethod(event));
}
protected void fireEndMethod(Method_info method, Metrics metrics) {
MetricsEvent event = new MetricsEvent(this, method, metrics);
metricsListeners.forEach(listener -> listener.endMethod(event));
}
protected void fireEndClass(Classfile classfile, Metrics metrics) {
MetricsEvent event = new MetricsEvent(this, classfile, metrics);
metricsListeners.forEach(listener -> listener.endClass(event));
}
protected void fireEndSession() {
MetricsEvent event = new MetricsEvent(this);
metricsListeners.forEach(listener -> listener.endSession(event));
}
}