SignatureMatcher interface

This commit is contained in:
oowekyala
2017-07-29 16:21:22 +02:00
parent 56f3b3ea62
commit 7fc250ad6d
11 changed files with 113 additions and 51 deletions

View File

@ -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();
}
}

View File

@ -33,7 +33,7 @@ public final class JavaMetrics {
*
* @return The project mirror
*/
static JavaProjectMirror getJavaProjectMirror() {
static PackageStats getTopLevelPackageStats() {
return FACADE.getLanguageSpecificProjectMirror();
}

View File

@ -26,7 +26,7 @@ class JavaMetricsFacade extends AbstractMetricsFacade<ASTAnyTypeDeclaration, AST
@Override
public JavaProjectMirror getLanguageSpecificProjectMirror() {
public PackageStats getLanguageSpecificProjectMirror() {
return topLevelPackageStats;
}

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<>();

View File

@ -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++;
}
}

View File

@ -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);
}
}
}

View File

@ -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<>();

View File

@ -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);