Merge branch 'pr-573'
This commit is contained in:
@ -55,7 +55,7 @@ public final class JavaQualifiedName implements QualifiedName {
|
||||
* @return The qualified name of the node
|
||||
*/
|
||||
/* default */ static JavaQualifiedName ofOperation(ASTConstructorDeclaration node) {
|
||||
ASTClassOrInterfaceDeclaration parent = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
ASTAnyTypeDeclaration parent = node.getFirstParentOfType(ASTAnyTypeDeclaration.class);
|
||||
|
||||
return ofOperation(parent.getQualifiedName(),
|
||||
parent.getImage(),
|
||||
|
@ -23,6 +23,7 @@ import net.sourceforge.pmd.lang.metrics.Metric;
|
||||
*/
|
||||
public abstract class AbstractJavaMetric<N extends Node> implements Metric<N> {
|
||||
|
||||
|
||||
protected List<JavaQualifiedName> findAllCalls(ASTMethodOrConstructorDeclaration node) {
|
||||
List<JavaQualifiedName> result = new ArrayList<>();
|
||||
// TODO:cf findAllCalls
|
||||
@ -32,6 +33,18 @@ public abstract class AbstractJavaMetric<N extends Node> implements Metric<N> {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
return o != null && o.getClass() == this.getClass();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gives access to a signature matcher to metrics. They can use it to perform signature matching.
|
||||
*
|
||||
@ -41,14 +54,4 @@ public abstract class AbstractJavaMetric<N extends Node> implements Metric<N> {
|
||||
return JavaMetrics.getFacade().getTopLevelPackageStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
return o != null && o.getClass() == this.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.AtfdMetric.AtfdClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.LocMetric.LocClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.NcssMetric.NcssClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.NoamMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.NopaMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.WmcMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.WocMetric;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricKey;
|
||||
|
||||
/**
|
||||
@ -42,7 +45,29 @@ public enum JavaClassMetricKey implements MetricKey<ASTAnyTypeDeclaration> {
|
||||
*
|
||||
* @see net.sourceforge.pmd.lang.java.metrics.impl.LocMetric
|
||||
*/
|
||||
LOC(new LocClassMetric());
|
||||
LOC(new LocClassMetric()),
|
||||
|
||||
/**
|
||||
* Number of Public Attributes.
|
||||
*
|
||||
* @see NopaMetric
|
||||
*/
|
||||
NOPA(new NopaMetric()),
|
||||
|
||||
/**
|
||||
* Number of Accessor Methods.
|
||||
*
|
||||
* @see NopaMetric
|
||||
*/
|
||||
NOAM(new NoamMetric()),
|
||||
|
||||
/**
|
||||
* Weight of class.
|
||||
*
|
||||
* @see WocMetric
|
||||
*/
|
||||
WOC(new WocMetric());
|
||||
|
||||
|
||||
private final JavaClassMetric calculator;
|
||||
|
||||
|
@ -4,10 +4,19 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.metrics.AbstractJavaMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.JavaFieldSigMask;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSigMask;
|
||||
|
||||
/**
|
||||
* Base class for class metrics.
|
||||
@ -28,4 +37,89 @@ public abstract class AbstractJavaClassMetric extends AbstractJavaMetric<ASTAnyT
|
||||
public boolean supports(ASTAnyTypeDeclaration node) {
|
||||
return node.getTypeKind() != TypeKind.ANNOTATION && node.getTypeKind() != TypeKind.INTERFACE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Counts the operations matching the signature mask in this class.
|
||||
*
|
||||
* @param classNode The class on which to count
|
||||
* @param mask The mask
|
||||
*
|
||||
* @return The number of operations matching the signature mask
|
||||
*/
|
||||
protected int countMatchingOpSigs(ASTAnyTypeDeclaration classNode, JavaOperationSigMask mask) {
|
||||
int count = 0;
|
||||
List<ASTMethodOrConstructorDeclaration> decls = getMethodsAndConstructors(classNode);
|
||||
|
||||
for (ASTMethodOrConstructorDeclaration decl : decls) {
|
||||
if (mask.covers(decl.getSignature())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Counts the fields matching the signature mask in this class.
|
||||
*
|
||||
* @param classNode The class on which to count
|
||||
* @param mask The mask
|
||||
*
|
||||
* @return The number of fields matching the signature mask
|
||||
*/
|
||||
protected int countMatchingFieldSigs(ASTAnyTypeDeclaration classNode, JavaFieldSigMask mask) {
|
||||
int count = 0;
|
||||
List<ASTFieldDeclaration> decls = getFields(classNode);
|
||||
|
||||
for (ASTFieldDeclaration decl : decls) {
|
||||
if (mask.covers(decl.getSignature())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a list of all methods and constructors declared in the class.
|
||||
*
|
||||
* @param node The class
|
||||
*
|
||||
* @return The list of all methods and constructors
|
||||
*/
|
||||
protected List<ASTMethodOrConstructorDeclaration> getMethodsAndConstructors(ASTAnyTypeDeclaration node) {
|
||||
return getDeclarationsOfType(node, ASTMethodOrConstructorDeclaration.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a list of all fields declared in the class.
|
||||
*
|
||||
* @param node The class
|
||||
*
|
||||
* @return The list of all fields
|
||||
*/
|
||||
protected List<ASTFieldDeclaration> getFields(ASTAnyTypeDeclaration node) {
|
||||
return getDeclarationsOfType(node, ASTFieldDeclaration.class);
|
||||
}
|
||||
|
||||
|
||||
private <T extends Node> List<T> getDeclarationsOfType(ASTAnyTypeDeclaration node, Class<T> tClass) {
|
||||
|
||||
List<T> result = new ArrayList<>();
|
||||
List<ASTAnyTypeBodyDeclaration> decls = node.getDeclarations();
|
||||
|
||||
for (ASTAnyTypeBodyDeclaration decl : decls) {
|
||||
if (tClass.isInstance(decl.jjtGetChild(0))) {
|
||||
result.add(tClass.cast(decl.jjtGetChild(0)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
|
||||
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.metrics.MetricOptions;
|
||||
|
||||
/**
|
||||
* Number of accessor methods.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class NoamMetric extends AbstractJavaClassMetric {
|
||||
|
||||
@Override
|
||||
public boolean supports(ASTAnyTypeDeclaration node) {
|
||||
return node.getTypeKind() == TypeKind.CLASS;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTAnyTypeDeclaration node, MetricOptions options) {
|
||||
JavaOperationSigMask mask = new JavaOperationSigMask();
|
||||
mask.restrictRolesTo(Role.GETTER_OR_SETTER);
|
||||
mask.restrictVisibilitiesTo(Visibility.PUBLIC);
|
||||
|
||||
|
||||
return (double) countMatchingOpSigs(node, mask);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.JavaFieldSigMask;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.JavaSignature.Visibility;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricOptions;
|
||||
|
||||
/**
|
||||
* Number of public attributes.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class NopaMetric extends AbstractJavaClassMetric {
|
||||
|
||||
@Override
|
||||
public boolean supports(ASTAnyTypeDeclaration node) {
|
||||
return node.getTypeKind() == TypeKind.CLASS;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTAnyTypeDeclaration node, MetricOptions options) {
|
||||
|
||||
JavaFieldSigMask mask = new JavaFieldSigMask();
|
||||
mask.restrictVisibilitiesTo(Visibility.PUBLIC);
|
||||
|
||||
return (double) countMatchingFieldSigs(node, mask);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
|
||||
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.metrics.MetricOptions;
|
||||
|
||||
/**
|
||||
* Weight of class.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class WocMetric extends AbstractJavaClassMetric {
|
||||
|
||||
@Override
|
||||
public boolean supports(ASTAnyTypeDeclaration node) {
|
||||
return node.getTypeKind() == TypeKind.CLASS;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTAnyTypeDeclaration node, MetricOptions options) {
|
||||
|
||||
JavaOperationSigMask mask = new JavaOperationSigMask();
|
||||
mask.forbid(Role.CONSTRUCTOR, Role.GETTER_OR_SETTER);
|
||||
mask.restrictVisibilitiesTo(Visibility.PUBLIC);
|
||||
|
||||
int functionalMethods = countMatchingOpSigs(node, mask);
|
||||
|
||||
mask.coverAllRoles();
|
||||
|
||||
int totalPublicMethods = countMatchingOpSigs(node, mask);
|
||||
|
||||
return functionalMethods / (double) totalPublicMethods;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics.rule;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
|
||||
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
|
||||
|
||||
/**
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class DataClassRule extends AbstractJavaMetricsRule {
|
||||
|
||||
// probably not worth using properties
|
||||
private static final int ACCESSOR_OR_FIELD_FEW_LEVEL = 3;
|
||||
private static final int ACCESSOR_OR_FIELD_MANY_LEVEL = 5;
|
||||
private static final double WOC_LEVEL = 1. / 3.;
|
||||
private static final int WMC_HIGH_LEVEL = 31;
|
||||
private static final int WMC_VERY_HIGH_LEVEL = 47;
|
||||
|
||||
|
||||
@Override
|
||||
public Object visit(ASTAnyTypeDeclaration node, Object data) {
|
||||
|
||||
boolean isDataClass = interfaceRevealsData(node) && classRevealsDataAndLacksComplexity(node);
|
||||
|
||||
if (isDataClass) {
|
||||
addViolation(data, node, new String[] {node.getImage()});
|
||||
}
|
||||
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
|
||||
private boolean interfaceRevealsData(ASTAnyTypeDeclaration node) {
|
||||
double woc = JavaMetrics.get(JavaClassMetricKey.WOC, node);
|
||||
return woc < WOC_LEVEL;
|
||||
}
|
||||
|
||||
|
||||
private boolean classRevealsDataAndLacksComplexity(ASTAnyTypeDeclaration node) {
|
||||
int nopa = (int) JavaMetrics.get(JavaClassMetricKey.NOPA, node);
|
||||
int noam = (int) JavaMetrics.get(JavaClassMetricKey.NOAM, node);
|
||||
int wmc = (int) JavaMetrics.get(JavaClassMetricKey.WMC, node);
|
||||
|
||||
return nopa + noam > ACCESSOR_OR_FIELD_FEW_LEVEL && wmc < WMC_HIGH_LEVEL
|
||||
|| nopa + noam > ACCESSOR_OR_FIELD_MANY_LEVEL && wmc < WMC_VERY_HIGH_LEVEL;
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
@ -79,6 +81,8 @@ public final class JavaOperationSignature extends JavaSignature<ASTMethodOrConst
|
||||
public enum Role {
|
||||
GETTER_OR_SETTER, CONSTRUCTOR, METHOD, STATIC;
|
||||
|
||||
private static final Pattern FIELD_NAME_PATTERN = Pattern.compile("(?:m_|_)?(\\w+)");
|
||||
|
||||
|
||||
public static Role get(ASTMethodOrConstructorDeclaration node) {
|
||||
return node instanceof ASTConstructorDeclaration ? CONSTRUCTOR : get((ASTMethodDeclaration) node);
|
||||
@ -110,7 +114,11 @@ public final class JavaOperationSignature extends JavaSignature<ASTMethodOrConst
|
||||
.getNode()
|
||||
.getFirstParentOfType(ASTFieldDeclaration.class);
|
||||
|
||||
fieldNames.put(field.getVariableName(), field.getFirstChildOfType(ASTType.class).getTypeImage());
|
||||
|
||||
Matcher matcher = FIELD_NAME_PATTERN.matcher(field.getVariableName());
|
||||
String varName = matcher.find() ? matcher.group(1) : field.getVariableName();
|
||||
|
||||
fieldNames.put(varName, field.getFirstChildOfType(ASTType.class).getTypeImage());
|
||||
}
|
||||
|
||||
return isGetter(node, fieldNames) || isSetter(node, fieldNames);
|
||||
|
@ -163,4 +163,42 @@ public class Foo {
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="DataClass"
|
||||
since="6.0"
|
||||
message="The class ''{0}'' is suspected to be a Data Class"
|
||||
class="net.sourceforge.pmd.lang.java.metrics.rule.DataClassRule"
|
||||
metrics="true"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_metrics.html#DataClass">
|
||||
<description>
|
||||
Data Classes are simple data holders, which reveal most of their state, and
|
||||
without complex functionality. The lack of functionality may indicate that
|
||||
their behaviour is defined elsewhere, which is a sign of poor data-behaviour
|
||||
proximity. By directly exposing their internals, Data Classes break encapsulation,
|
||||
and therefore reduce the system's maintainability and understandability. Moreover,
|
||||
classes tend to strongly rely on their data representation, which makes for a brittle
|
||||
design.
|
||||
|
||||
Refactoring a Data Class should focus on restoring a good data-behaviour proximity. In
|
||||
most cases, that means moving the operations defined on the data back into the class.
|
||||
In some other cases it may make sense to remove entirely the class and move the data
|
||||
into the former client classes.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class DataClass {
|
||||
|
||||
public int bar = 0;
|
||||
public int na = 0;
|
||||
private int bee = 0;
|
||||
|
||||
public void setBee(int n) {
|
||||
bee = n;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
|
||||
</ruleset>
|
||||
|
@ -126,16 +126,40 @@ public abstract class AbstractMetricTestRule extends AbstractJavaMetricsRule {
|
||||
}
|
||||
|
||||
|
||||
/** Gets a string representation rounded to the nearest half. */
|
||||
private String presentableString(double val) {
|
||||
boolean isInt = Math.floor(val) == val;
|
||||
|
||||
if (!isInt && val >= 0 && val <= 1) { // percentage
|
||||
return roundedString(100 * val) + "%";
|
||||
} else if (!isInt) {
|
||||
return String.valueOf(roundedString(val));
|
||||
} else {
|
||||
return String.valueOf((int) val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String roundedString(double val) {
|
||||
double truncated = Math.floor(100 * val) / 100;
|
||||
if (truncated == Math.floor(truncated)) {
|
||||
return String.valueOf((int) truncated);
|
||||
} else {
|
||||
return String.valueOf(truncated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object visit(ASTAnyTypeDeclaration node, Object data) {
|
||||
if (classKey != null && reportClasses && classKey.supports(node)) {
|
||||
int classValue = (int) JavaMetrics.get(classKey, node, metricOptions);
|
||||
double classValue = JavaMetrics.get(classKey, node, metricOptions);
|
||||
|
||||
String valueReport = String.valueOf(classValue);
|
||||
String valueReport = presentableString(classValue);
|
||||
|
||||
if (opKey != null) {
|
||||
int highest = (int) JavaMetrics.get(opKey, node, metricOptions, ResultOption.HIGHEST);
|
||||
valueReport += " highest " + highest;
|
||||
double highest = JavaMetrics.get(opKey, node, metricOptions, ResultOption.HIGHEST);
|
||||
valueReport += " highest " + presentableString(highest);
|
||||
}
|
||||
if (classValue >= reportLevel) {
|
||||
addViolation(data, node, new String[] {node.getQualifiedName().toString(), valueReport, });
|
||||
@ -148,9 +172,10 @@ public abstract class AbstractMetricTestRule extends AbstractJavaMetricsRule {
|
||||
@Override
|
||||
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
if (opKey != null && reportMethods && opKey.supports(node)) {
|
||||
int methodValue = (int) JavaMetrics.get(opKey, node, metricOptions);
|
||||
double methodValue = JavaMetrics.get(opKey, node, metricOptions);
|
||||
if (methodValue >= reportLevel) {
|
||||
addViolation(data, node, new String[] {node.getQualifiedName().toString(), "" + methodValue, });
|
||||
addViolation(data, node, new String[] {node.getQualifiedName().toString(),
|
||||
"" + presentableString(methodValue), });
|
||||
}
|
||||
}
|
||||
return data;
|
||||
|
@ -33,6 +33,9 @@ public class AllMetricsTest extends SimpleAggregatorTst {
|
||||
addRule(RULESET, "WmcTest");
|
||||
addRule(RULESET, "LocTest");
|
||||
addRule(RULESET, "NPathTest");
|
||||
addRule(RULESET, "NopaTest");
|
||||
addRule(RULESET, "NoamTest");
|
||||
addRule(RULESET, "WocTest");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 NoamTestRule extends AbstractMetricTestRule {
|
||||
|
||||
@Override
|
||||
protected JavaClassMetricKey getClassKey() {
|
||||
return JavaClassMetricKey.NOAM;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected JavaOperationMetricKey getOpKey() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -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 NopaTestRule extends AbstractMetricTestRule {
|
||||
|
||||
@Override
|
||||
protected JavaClassMetricKey getClassKey() {
|
||||
return JavaClassMetricKey.NOPA;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected JavaOperationMetricKey getOpKey() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -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 WocTestRule extends AbstractMetricTestRule {
|
||||
|
||||
@Override
|
||||
protected JavaClassMetricKey getClassKey() {
|
||||
return JavaClassMetricKey.WOC;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected JavaOperationMetricKey getOpKey() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -28,5 +28,6 @@ public class MetricsRulesTest extends SimpleAggregatorTst {
|
||||
addRule(RULESET, "CyclomaticComplexity");
|
||||
addRule(RULESET, "NcssCount");
|
||||
addRule(RULESET, "NPathComplexity");
|
||||
addRule(RULESET, "DataClass");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data>
|
||||
|
||||
<code-fragment id="full-example"><![CDATA[
|
||||
public class Foo {
|
||||
private int value;
|
||||
private double speed;
|
||||
private MutableInt mutX;
|
||||
private boolean bool;
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isBool() {
|
||||
return bool;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
protected int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
boolean isBool() {
|
||||
return bool;
|
||||
}
|
||||
|
||||
private int value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
]]></code-fragment>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Full example</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo' has value 3.</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test empty class</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo' has value 0.</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>NOPA doesn't support enums, interfaces or annotations</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public interface Foo {
|
||||
|
||||
public final int h;
|
||||
|
||||
enum Bar{
|
||||
FOO;
|
||||
public int bel = 0;
|
||||
}
|
||||
|
||||
@interface Tag {
|
||||
public static final int num = 0;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
</test-data>
|
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data>
|
||||
|
||||
<code-fragment id="full-example"><![CDATA[
|
||||
public class Foo {
|
||||
public static int a;
|
||||
public final int b;
|
||||
protected int c;
|
||||
public static int d;
|
||||
public static final int e;
|
||||
int f;
|
||||
int g;
|
||||
public int h;
|
||||
public int i;
|
||||
public int j;
|
||||
|
||||
public static void bar() {
|
||||
|
||||
}
|
||||
}
|
||||
]]></code-fragment>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Full example</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo' has value 7.</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test empty class</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo' has value 0.</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>NOPA doesn't support enums, interfaces or annotations</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public interface Foo {
|
||||
|
||||
public final int h;
|
||||
|
||||
enum Bar{
|
||||
FOO;
|
||||
public int bel = 0;
|
||||
}
|
||||
|
||||
@interface Tag {
|
||||
public static final int num = 0;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
</test-data>
|
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data>
|
||||
|
||||
<code-fragment id="full-example"><![CDATA[
|
||||
|
||||
// Data class from ArgoUML
|
||||
/**
|
||||
* A property that can be displayed and edited within a PropertyTable.
|
||||
*
|
||||
* @author Jeremy Jones
|
||||
**/
|
||||
public class Property implements Comparable {
|
||||
|
||||
private String _name;
|
||||
private Class _valueType;
|
||||
private Object _initialValue;
|
||||
private Object _currentValue;
|
||||
private Object[] _availableValues;
|
||||
|
||||
public Property(String name, Class valueType, Object initialValue) {
|
||||
this(name, valueType, initialValue, null);
|
||||
}
|
||||
|
||||
|
||||
public Property(String name, Class valueType, Object initialValue, Object[] values) {
|
||||
_name = name;
|
||||
_valueType = valueType;
|
||||
_initialValue = initialValue;
|
||||
_availableValues = values;
|
||||
_currentValue = _initialValue;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
public Class getValueType() {
|
||||
return _valueType;
|
||||
}
|
||||
|
||||
public Object getInitialValue() {
|
||||
return _initialValue;
|
||||
}
|
||||
|
||||
public Object[] getAvailableValues() {
|
||||
return _availableValues;
|
||||
}
|
||||
|
||||
public Object getCurrentValue() {
|
||||
return _currentValue;
|
||||
}
|
||||
|
||||
public void setCurrentValue(Object value) {
|
||||
_currentValue = value;
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
return _name.compareTo(((Property) o)._name);
|
||||
}
|
||||
}
|
||||
|
||||
]]></code-fragment>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Full example</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Property' has value 11.11%.</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test empty class</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>NOPA doesn't support enums, interfaces or annotations</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public interface Foo {
|
||||
|
||||
public final int h;
|
||||
|
||||
enum Bar{
|
||||
FOO;
|
||||
public int bel = 0;
|
||||
}
|
||||
|
||||
@interface Tag {
|
||||
public static final int num = 0;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
</test-data>
|
@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data>
|
||||
|
||||
<code-fragment id="argoUML-dataclass">
|
||||
<![CDATA[
|
||||
public class Property implements Comparable { // Woc = 10%, Noam = 6, Nopa = 0, Wmc = 10
|
||||
|
||||
private String _name;
|
||||
private Class _valueType;
|
||||
private Object _initialValue;
|
||||
private Object _currentValue;
|
||||
private Object[] _availableValues;
|
||||
|
||||
public Property(String name, Class valueType, Object initialValue) {
|
||||
this(name, valueType, initialValue, null);
|
||||
}
|
||||
|
||||
|
||||
public Property(String name, Class valueType, Object initialValue, Object[] values) {
|
||||
_name = name;
|
||||
_valueType = valueType;
|
||||
_initialValue = initialValue;
|
||||
_availableValues = values;
|
||||
_currentValue = _initialValue;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
public Class getValueType() {
|
||||
return _valueType;
|
||||
}
|
||||
|
||||
public Object getInitialValue() {
|
||||
return _initialValue;
|
||||
}
|
||||
|
||||
public Object[] getAvailableValues() {
|
||||
return _availableValues;
|
||||
}
|
||||
|
||||
public Object getCurrentValue() {
|
||||
return _currentValue;
|
||||
}
|
||||
|
||||
public void setCurrentValue(Object value) {
|
||||
_currentValue = value;
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
return _name.compareTo(((Property) o)._name);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>ArgoUML data class</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The class 'Property' is suspected to be a Data Class</message>
|
||||
</expected-messages>
|
||||
<code-ref id="argoUML-dataclass"/>
|
||||
</test-code>
|
||||
|
||||
|
||||
<code-fragment id="testDescriptor">
|
||||
<![CDATA[
|
||||
public class TestDescriptor {
|
||||
private Rule rule;
|
||||
private Properties properties;
|
||||
private String description;
|
||||
private int numberOfProblemsExpected;
|
||||
private List<String> expectedMessages = new ArrayList<>();
|
||||
private List<Integer> expectedLineNumbers = new ArrayList<>();
|
||||
private String code;
|
||||
private LanguageVersion languageVersion;
|
||||
// default, avoids unintentional mixing of state between test cases
|
||||
private boolean reinitializeRule = true;
|
||||
private boolean isRegressionTest = true;
|
||||
private boolean useAuxClasspath = true;
|
||||
private int numberInDocument = -1;
|
||||
|
||||
// Empty descriptor added to please mvn surefire plugin
|
||||
public TestDescriptor() {
|
||||
|
||||
}
|
||||
|
||||
public TestDescriptor(String code, String description, int numberOfProblemsExpected, Rule rule) {
|
||||
this(code, description, numberOfProblemsExpected, rule, rule.getLanguage().getDefaultVersion());
|
||||
}
|
||||
|
||||
public TestDescriptor(String code, String description, int numberOfProblemsExpected, Rule rule,
|
||||
LanguageVersion languageVersion) {
|
||||
this.rule = rule;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.numberOfProblemsExpected = numberOfProblemsExpected;
|
||||
this.languageVersion = languageVersion;
|
||||
}
|
||||
|
||||
public int getNumberInDocument() {
|
||||
return numberInDocument;
|
||||
}
|
||||
|
||||
public void setNumberInDocument(int numberInDocument) {
|
||||
this.numberInDocument = numberInDocument;
|
||||
}
|
||||
|
||||
public void setExpectedMessages(List<String> messages) {
|
||||
expectedMessages.clear();
|
||||
expectedMessages.addAll(messages);
|
||||
}
|
||||
|
||||
public List<String> getExpectedMessages() {
|
||||
return expectedMessages;
|
||||
}
|
||||
|
||||
public void setExpectedLineNumbers(List<Integer> expectedLineNumbers) {
|
||||
this.expectedLineNumbers.clear();
|
||||
this.expectedLineNumbers.addAll(expectedLineNumbers);
|
||||
}
|
||||
|
||||
public List<Integer> getExpectedLineNumbers() {
|
||||
return expectedLineNumbers;
|
||||
}
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public LanguageVersion getLanguageVersion() {
|
||||
return languageVersion;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getNumberOfProblemsExpected() {
|
||||
return numberOfProblemsExpected;
|
||||
}
|
||||
|
||||
public Rule getRule() {
|
||||
return rule;
|
||||
}
|
||||
|
||||
public boolean getReinitializeRule() {
|
||||
return reinitializeRule;
|
||||
}
|
||||
|
||||
public void setReinitializeRule(boolean reinitializeRule) {
|
||||
this.reinitializeRule = reinitializeRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we are testing for regression problems only. Return value
|
||||
* is based on the system property "pmd.regress".
|
||||
*
|
||||
* @return <code>false</code> if system property "pmd.regress" is set to
|
||||
* <code>false</code>, <code>true</code> otherwise
|
||||
*/
|
||||
public static boolean inRegressionTestMode() {
|
||||
boolean inRegressionMode = true; // default
|
||||
try {
|
||||
// get the "pmd.regress" System property
|
||||
String property = System.getProperty("pmd.regress");
|
||||
if (property != null) {
|
||||
inRegressionMode = Boolean.parseBoolean(property);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
|
||||
return inRegressionMode;
|
||||
}
|
||||
|
||||
public boolean isRegressionTest() {
|
||||
return isRegressionTest;
|
||||
}
|
||||
|
||||
public void setRegressionTest(boolean isRegressionTest) {
|
||||
this.isRegressionTest = isRegressionTest;
|
||||
}
|
||||
|
||||
public void setUseAuxClasspath(boolean useAuxClasspath) {
|
||||
this.useAuxClasspath = useAuxClasspath;
|
||||
}
|
||||
|
||||
public boolean isUseAuxClasspath() {
|
||||
return useAuxClasspath;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>PMD data class</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The class 'TestDescriptor' is suspected to be a Data Class</message>
|
||||
</expected-messages>
|
||||
<code-ref id="testDescriptor"/>
|
||||
</test-code>
|
||||
|
||||
</test-data>
|
@ -39,4 +39,22 @@
|
||||
metrics="true">
|
||||
</rule>
|
||||
|
||||
<rule name="NopaTest"
|
||||
message = "''{0}'' has value {1}."
|
||||
class="net.sourceforge.pmd.lang.java.metrics.impl.NopaTestRule"
|
||||
metrics="true">
|
||||
</rule>
|
||||
|
||||
<rule name="NoamTest"
|
||||
message = "''{0}'' has value {1}."
|
||||
class="net.sourceforge.pmd.lang.java.metrics.impl.NoamTestRule"
|
||||
metrics="true">
|
||||
</rule>
|
||||
|
||||
<rule name="WocTest"
|
||||
message = "''{0}'' has value {1}."
|
||||
class="net.sourceforge.pmd.lang.java.metrics.impl.WocTestRule"
|
||||
metrics="true">
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
||||
|
Reference in New Issue
Block a user