SignatureMatcher interface
This commit is contained in:
@ -33,12 +33,12 @@ public abstract class AbstractJavaMetric<N extends Node> implements Metric<N> {
|
||||
|
||||
|
||||
/**
|
||||
* Gives access to the project mirror to metrics. They can use it to perform signature matching.
|
||||
* Gives access to a signature matcher to metrics. They can use it to perform signature matching.
|
||||
*
|
||||
* @return The project mirror (singleton contained within {@link JavaMetricsFacade}).
|
||||
* @return A signature matcher
|
||||
*/
|
||||
protected static JavaProjectMirror getJavaProjectMirror() {
|
||||
return JavaMetrics.getJavaProjectMirror();
|
||||
protected static JavaSignatureMatcher getSignatureMatcher() {
|
||||
return JavaMetrics.getTopLevelPackageStats();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public final class JavaMetrics {
|
||||
*
|
||||
* @return The project mirror
|
||||
*/
|
||||
static JavaProjectMirror getJavaProjectMirror() {
|
||||
static PackageStats getTopLevelPackageStats() {
|
||||
return FACADE.getLanguageSpecificProjectMirror();
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class JavaMetricsFacade extends AbstractMetricsFacade<ASTAnyTypeDeclaration, AST
|
||||
|
||||
|
||||
@Override
|
||||
public JavaProjectMirror getLanguageSpecificProjectMirror() {
|
||||
public PackageStats getLanguageSpecificProjectMirror() {
|
||||
return topLevelPackageStats;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ public class JavaMetricsVisitorFacade extends JavaParserVisitorAdapter {
|
||||
|
||||
public void initializeWith(ASTCompilationUnit rootNode) {
|
||||
JavaMetricsVisitor visitor = new JavaMetricsVisitor();
|
||||
rootNode.jjtAccept(visitor, JavaMetrics.getJavaProjectMirror());
|
||||
rootNode.jjtAccept(visitor, JavaMetrics.getTopLevelPackageStats());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,40 +6,12 @@ package net.sourceforge.pmd.lang.java.metrics;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaQualifiedName;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.FieldSigMask;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
import net.sourceforge.pmd.lang.metrics.ProjectMirror;
|
||||
|
||||
/**
|
||||
* Gathers the methods that the Java project mirror should make available to metrics during the computation.
|
||||
* Shorthand for a project mirror parameterized with Java-specific node types.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public interface JavaProjectMirror extends ProjectMirror<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {
|
||||
|
||||
/**
|
||||
* Returns true if the signature of the operation designated by the qualified name is covered by the mask.
|
||||
*
|
||||
* @param qname The operation to test
|
||||
* @param sigMask The signature mask to use
|
||||
*
|
||||
* @return True if the signature of the operation designated by the qualified name is covered by the mask
|
||||
*/
|
||||
boolean hasMatchingSig(JavaQualifiedName qname, OperationSigMask sigMask);
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the signature of the field designated by its name and the qualified name of its class is covered
|
||||
* by the mask.
|
||||
*
|
||||
* @param qname The class of the field
|
||||
* @param fieldName The name of the field
|
||||
* @param sigMask The signature mask to use
|
||||
*
|
||||
* @return True if the signature of the field is covered by the mask
|
||||
*/
|
||||
boolean hasMatchingSig(JavaQualifiedName qname, String fieldName, FieldSigMask sigMask);
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaQualifiedName;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.FieldSigMask;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
|
||||
/**
|
||||
* Gathers the methods that the Java project mirror should make available to metrics during the computation.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public interface JavaSignatureMatcher {
|
||||
|
||||
/**
|
||||
* Returns true if the signature of the operation designated by the qualified name is covered by the mask.
|
||||
*
|
||||
* @param qname The operation to test
|
||||
* @param sigMask The signature mask to use
|
||||
*
|
||||
* @return True if the signature of the operation designated by the qualified name is covered by the mask
|
||||
*/
|
||||
boolean hasMatchingSig(JavaQualifiedName qname, OperationSigMask sigMask);
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the signature of the field designated by its name and the qualified name of its class is covered
|
||||
* by the mask.
|
||||
*
|
||||
* @param qname The class of the field
|
||||
* @param fieldName The name of the field
|
||||
* @param sigMask The signature mask to use
|
||||
*
|
||||
* @return True if the signature of the field is covered by the mask
|
||||
*/
|
||||
boolean hasMatchingSig(JavaQualifiedName qname, String fieldName, FieldSigMask sigMask);
|
||||
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
|
||||
* @author Clément Fournier
|
||||
* @see ClassStats
|
||||
*/
|
||||
public final class PackageStats implements JavaProjectMirror {
|
||||
public final class PackageStats implements JavaProjectMirror, JavaSignatureMatcher {
|
||||
|
||||
private final Map<String, PackageStats> subPackages = new HashMap<>();
|
||||
private final Map<String, ClassStats> classes = new HashMap<>();
|
||||
|
@ -34,7 +34,7 @@ public final class AtfdMetric {
|
||||
List<JavaQualifiedName> callQNames = findAllCalls(node);
|
||||
int foreignCalls = 0;
|
||||
for (JavaQualifiedName name : callQNames) {
|
||||
if (getJavaProjectMirror().hasMatchingSig(name, targetOps)) {
|
||||
if (getSignatureMatcher().hasMatchingSig(name, targetOps)) {
|
||||
foreignCalls++;
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,14 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor;
|
||||
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
|
||||
@ -24,27 +29,69 @@ import net.sourceforge.pmd.lang.metrics.api.ResultOption;
|
||||
*
|
||||
* <p>The standard version of the metric complies with McCabe's original definition [3]:
|
||||
*
|
||||
* <ul> <li>+1 for every control flow statement ({@code if, case, catch, throw, do, while, for, break, continue}) and
|
||||
* <ul>
|
||||
* <li>+1 for every control flow statement ({@code if, case, catch, throw, do, while, for, break, continue}) and
|
||||
* conditional expression ({@code ? : }). Notice switch cases count as one, but not the switch itself: the point is that
|
||||
* a switch should have the same complexity value as the equivalent series of {@code if} statements. <li>{@code else},
|
||||
* {@code finally} and {@code default} don't count; <li>+1 for every boolean operator ({@code &&, ||}) in the guard
|
||||
* condition of a control flow statement. That's because Java has short-circuit evaluation semantics for boolean
|
||||
* operators, which makes every boolean operator kind of a control flow statement in itself. </ul>
|
||||
* a switch should have the same complexity value as the equivalent series of {@code if} statements.
|
||||
* <li>{@code else}, {@code finally} and {@code default} don't count;
|
||||
* <li>+1 for every boolean operator ({@code &&, ||}) in the guard condition of a control flow statement. That's because
|
||||
* Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a
|
||||
* control flow statement in itself.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Version {@link CycloVersion#IGNORE_BOOLEAN_PATHS}: Boolean operators are not counted, which means that empty
|
||||
* fall-through cases in {@code switch} statements are not counted as well.
|
||||
*
|
||||
* <p>References: <ul> <li> [1] Lanza, Object-Oriented Metrics in Practice, 2005. <li> [2] McCabe, A Complexity Measure,
|
||||
* in Proceedings of the 2nd ICSE (1976). <li> [3] <a href="https://docs.sonarqube.org/display/SONAR/Metrics+-+Complexity">Sonarqube
|
||||
* online documentation</a> </ul>
|
||||
* <p>References:
|
||||
*
|
||||
* <ul>
|
||||
* <li> [1] Lanza, Object-Oriented Metrics in Practice, 2005.
|
||||
* <li> [2] McCabe, A Complexity Measure, in Proceedings of the 2nd ICSE (1976).
|
||||
* <li> [3] <a href="https://docs.sonarqube.org/display/SONAR/Metrics+-+Complexity">Sonarqube online documentation</a>
|
||||
* </ul>
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since June 2017
|
||||
*/
|
||||
public final class CycloMetric {
|
||||
|
||||
private CycloMetric() {
|
||||
|
||||
}
|
||||
|
||||
// TODO:cf Cyclo should develop factorized boolean operators to count them
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates the number of paths through a boolean expression. This is the total number of {@code &&} and {@code ||}
|
||||
* operators appearing in the expression. This is used in the calculation of cyclomatic and n-path complexity.
|
||||
*
|
||||
* @param expr Expression to analyse
|
||||
*
|
||||
* @return The number of paths through the expression
|
||||
*/
|
||||
public static int booleanExpressionComplexity(ASTExpression expr) {
|
||||
if (expr == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<ASTConditionalAndExpression> andNodes = expr.findDescendantsOfType(ASTConditionalAndExpression.class);
|
||||
List<ASTConditionalOrExpression> orNodes = expr.findDescendantsOfType(ASTConditionalOrExpression.class);
|
||||
|
||||
int complexity = 0;
|
||||
|
||||
for (ASTConditionalOrExpression element : orNodes) {
|
||||
complexity += element.jjtGetNumChildren() - 1;
|
||||
}
|
||||
|
||||
for (ASTConditionalAndExpression element : andNodes) {
|
||||
complexity += element.jjtGetNumChildren() - 1;
|
||||
}
|
||||
|
||||
return complexity;
|
||||
}
|
||||
|
||||
|
||||
/** Variants of CYCLO. */
|
||||
public enum CycloVersion implements MetricVersion {
|
||||
/** Do not count the paths in boolean expressions as decision points. */
|
||||
@ -72,4 +119,5 @@ public final class CycloMetric {
|
||||
return 1 + JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, version, ResultOption.AVERAGE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ public class DataStructureTest extends ParserTst {
|
||||
}
|
||||
|
||||
private List<Integer> visitWith(ASTCompilationUnit acu, final boolean force) {
|
||||
final JavaProjectMirror toplevel = JavaMetrics.getJavaProjectMirror();
|
||||
final PackageStats toplevel = JavaMetrics.getTopLevelPackageStats();
|
||||
|
||||
final List<Integer> result = new ArrayList<>();
|
||||
|
||||
|
@ -39,7 +39,7 @@ public class JavaMetricsVisitorTest {
|
||||
|
||||
@Test
|
||||
public void testPackageStatsNotNull() {
|
||||
assertNotNull(JavaMetrics.getJavaProjectMirror());
|
||||
assertNotNull(JavaMetrics.getTopLevelPackageStats());
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ public class JavaMetricsVisitorTest {
|
||||
public void testOperationsAreThere() {
|
||||
ASTCompilationUnit acu = parseAndVisitForClass15(MetricsVisitorTestData.class);
|
||||
|
||||
final JavaProjectMirror toplevel = JavaMetrics.getJavaProjectMirror();
|
||||
final JavaSignatureMatcher toplevel = JavaMetrics.getTopLevelPackageStats();
|
||||
|
||||
final OperationSigMask opMask = new OperationSigMask();
|
||||
|
||||
@ -67,7 +67,7 @@ public class JavaMetricsVisitorTest {
|
||||
parseAndVisitForClass15(MetricsVisitorTestData.class);
|
||||
|
||||
|
||||
final JavaProjectMirror toplevel = JavaMetrics.getJavaProjectMirror();
|
||||
final JavaSignatureMatcher toplevel = JavaMetrics.getTopLevelPackageStats();
|
||||
|
||||
final FieldSigMask fieldSigMask = new FieldSigMask();
|
||||
|
||||
@ -90,7 +90,7 @@ public class JavaMetricsVisitorTest {
|
||||
public void testStaticOperationsSig() {
|
||||
parseAndVisitForClass15(MetricsVisitorTestData.class);
|
||||
|
||||
final JavaProjectMirror toplevel = JavaMetrics.getJavaProjectMirror();
|
||||
final JavaSignatureMatcher toplevel = JavaMetrics.getTopLevelPackageStats();
|
||||
|
||||
final OperationSigMask operationSigMask = new OperationSigMask();
|
||||
operationSigMask.restrictRolesTo(Role.STATIC);
|
||||
|
Reference in New Issue
Block a user