From 2f678791ccee6d0bebf67e7fa12eb985653df2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 20 Aug 2017 01:40:11 +0200 Subject: [PATCH] ATFD metric --- .../lang/java/metrics/impl/AtfdMetric.java | 32 +-- .../impl/visitors/AtfdBaseVisitor.java | 109 ++++++++++ .../java/metrics/impl/AllMetricsTest.java | 1 + .../lang/java/metrics/impl/AtfdTestRule.java | 26 +++ .../lang/java/metrics/impl/xml/AtfdTest.xml | 186 ++++++++++++++++++ .../resources/rulesets/java/metrics_test.xml | 6 + 6 files changed, 338 insertions(+), 22 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/AtfdBaseVisitor.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdTestRule.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/AtfdTest.xml diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdMetric.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdMetric.java index 9f15a84271..946c13ef78 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdMetric.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdMetric.java @@ -4,15 +4,15 @@ 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.ASTMethodOrConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.JavaQualifiedName; -import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSigMask; -import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSignature.Role; -import net.sourceforge.pmd.lang.java.metrics.signature.JavaSignature.Visibility; +import net.sourceforge.pmd.lang.java.metrics.JavaMetrics; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; +import net.sourceforge.pmd.lang.java.metrics.impl.visitors.AtfdBaseVisitor; import net.sourceforge.pmd.lang.metrics.MetricOptions; +import net.sourceforge.pmd.lang.metrics.ResultOption; /** * Access to Foreign Data. Quantifies the number of foreign fields accessed directly or via accessors. @@ -24,31 +24,19 @@ public final class AtfdMetric { public static final class AtfdOperationMetric extends AbstractJavaOperationMetric { - @Override // TODO:cf + @Override public double computeFor(ASTMethodOrConstructorDeclaration node, MetricOptions options) { - - JavaOperationSigMask targetOps = new JavaOperationSigMask(); - targetOps.restrictVisibilitiesTo(Visibility.PUBLIC); - targetOps.restrictRolesTo(Role.GETTER_OR_SETTER); - - List callQNames = findAllCalls(node); - int foreignCalls = 0; - for (JavaQualifiedName name : callQNames) { - if (getSignatureMatcher().hasMatchingSig(name, targetOps)) { - foreignCalls++; - } - } - - return foreignCalls / callQNames.size(); + return ((MutableInt) node.jjtAccept(new AtfdBaseVisitor(), new MutableInt(0))).getValue(); } + } public static final class AtfdClassMetric extends AbstractJavaClassMetric { @Override public double computeFor(ASTAnyTypeDeclaration node, MetricOptions options) { - // TODO:cf - return 0; + // TODO maybe consider code outside methods + return JavaMetrics.get(JavaOperationMetricKey.ATFD, node, options, ResultOption.SUM); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/AtfdBaseVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/AtfdBaseVisitor.java new file mode 100644 index 0000000000..77b32dd102 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/AtfdBaseVisitor.java @@ -0,0 +1,109 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.metrics.impl.visitors; + +import java.util.List; + +import org.apache.commons.lang3.mutable.MutableInt; + +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter; +import net.sourceforge.pmd.util.StringUtil; + +/** + * Computes Atfd. + * + * @author Clément Fournier + * @since 6.0.0 + */ +public class AtfdBaseVisitor extends JavaParserVisitorAdapter { + + + @Override + public Object visit(ASTPrimaryExpression node, Object data) { + if (isForeignAttributeOrMethod(node) && (isAttributeAccess(node) + || isMethodCall(node) && isForeignGetterSetterCall(node))) { + + ((MutableInt) data).increment(); + } + return super.visit(node, data); + } + + + private boolean isForeignGetterSetterCall(ASTPrimaryExpression node) { + + String methodOrAttributeName = getMethodOrAttributeName(node); + + return methodOrAttributeName != null && StringUtil.startsWithAny(methodOrAttributeName, "get", "is", "set"); + } + + + private boolean isMethodCall(ASTPrimaryExpression node) { + boolean result = false; + List suffixes = node.findDescendantsOfType(ASTPrimarySuffix.class); + if (suffixes.size() == 1) { + result = suffixes.get(0).isArguments(); + } + return result; + } + + + private boolean isForeignAttributeOrMethod(ASTPrimaryExpression node) { + boolean result; + String nameImage = getNameImage(node); + + if (nameImage != null && (!nameImage.contains(".") || nameImage.startsWith("this."))) { + result = false; + } else if (nameImage == null && node.getFirstDescendantOfType(ASTPrimaryPrefix.class).usesThisModifier()) { + result = false; + } else if (nameImage == null && node.hasDecendantOfAnyType(ASTLiteral.class, ASTAllocationExpression.class)) { + result = false; + } else { + result = true; + } + + return result; + } + + + private String getNameImage(ASTPrimaryExpression node) { + ASTPrimaryPrefix prefix = node.getFirstDescendantOfType(ASTPrimaryPrefix.class); + ASTName name = prefix.getFirstDescendantOfType(ASTName.class); + + String image = null; + if (name != null) { + image = name.getImage(); + } + return image; + } + + + private String getMethodOrAttributeName(ASTPrimaryExpression node) { + ASTPrimaryPrefix prefix = node.getFirstDescendantOfType(ASTPrimaryPrefix.class); + ASTName name = prefix.getFirstDescendantOfType(ASTName.class); + + String methodOrAttributeName = null; + + if (name != null) { + int dotIndex = name.getImage().indexOf("."); + if (dotIndex > -1) { + methodOrAttributeName = name.getImage().substring(dotIndex + 1); + } + } + + return methodOrAttributeName; + } + + + private boolean isAttributeAccess(ASTPrimaryExpression node) { + return node.findDescendantsOfType(ASTPrimarySuffix.class).isEmpty(); + } + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AllMetricsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AllMetricsTest.java index aa7756200b..f5d3b6e2a9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AllMetricsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AllMetricsTest.java @@ -37,6 +37,7 @@ public class AllMetricsTest extends SimpleAggregatorTst { addRule(RULESET, "NoamTest"); addRule(RULESET, "WocTest"); addRule(RULESET, "TccTest"); + addRule(RULESET, "AtfdTest"); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdTestRule.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdTestRule.java new file mode 100644 index 0000000000..11a17bb4a4 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/impl/AtfdTestRule.java @@ -0,0 +1,26 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.metrics.impl; + +import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class AtfdTestRule extends AbstractMetricTestRule { + + @Override + protected JavaClassMetricKey getClassKey() { + return JavaClassMetricKey.ATFD; + } + + + @Override + protected JavaOperationMetricKey getOpKey() { + return JavaOperationMetricKey.ATFD; + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/AtfdTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/AtfdTest.xml new file mode 100644 index 0000000000..6a060ba0ce --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/AtfdTest.xml @@ -0,0 +1,186 @@ + + + + + + + + Full example + 1 + + '.StatementAndBraceFinder' has value 10. + + + + + + Test empty class + 1 + false + + '.Foo' has value 0. + + + + + + Test empty class + + 1 + + '.Foo' has value 0. + + + + + + NOPA doesn't support enums, interfaces or annotations + 0 + + + + \ No newline at end of file diff --git a/pmd-java/src/test/resources/rulesets/java/metrics_test.xml b/pmd-java/src/test/resources/rulesets/java/metrics_test.xml index 258f83f764..be4470c5a5 100644 --- a/pmd-java/src/test/resources/rulesets/java/metrics_test.xml +++ b/pmd-java/src/test/resources/rulesets/java/metrics_test.xml @@ -63,4 +63,10 @@ metrics="true"> + + +