Abstracted Computer, Memoizer, ProjectMirror
This commit is contained in:
@ -9,11 +9,13 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
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.JavaFieldSignature;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSignature;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
|
||||
|
||||
/**
|
||||
* Statistics about a class, enum, interface, or annotation. Stores information about the contained members and their
|
||||
@ -26,7 +28,7 @@ import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
/* default */ class ClassStats extends Memoizer {
|
||||
/* default */ class ClassStats extends MetricMemoizer<ASTAnyTypeDeclaration> {
|
||||
|
||||
private Map<JavaOperationSignature, Map<String, OperationStats>> operations = new HashMap<>();
|
||||
private Map<JavaFieldSignature, Set<String>> fields = new HashMap<>();
|
||||
|
@ -10,131 +10,25 @@ import java.util.List;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.metrics.ParameterizedMetricKey;
|
||||
import net.sourceforge.pmd.lang.metrics.api.MetricKey;
|
||||
import net.sourceforge.pmd.lang.metrics.api.MetricVersion;
|
||||
import net.sourceforge.pmd.lang.metrics.api.ResultOption;
|
||||
import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer;
|
||||
|
||||
/**
|
||||
* Computes a metric. This relieves ClassStats and OperationStats from that responsibility.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public class JavaMetricsComputer {
|
||||
public class JavaMetricsComputer extends AbstractMetricsComputer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {
|
||||
|
||||
static final JavaMetricsComputer INSTANCE = new JavaMetricsComputer();
|
||||
|
||||
|
||||
private JavaMetricsComputer() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the value of a metric for a class and stores the result in the ClassStats object.
|
||||
*
|
||||
* @param key The class metric to compute
|
||||
* @param node The AST node of the class
|
||||
* @param force Force the recomputation; if unset, we'll first check for a memoized result
|
||||
* @param version The version of the metric to compute
|
||||
* @param memoizer The object memoizing the results
|
||||
*
|
||||
* @return The result of the computation, or {@code Double.NaN} if it couldn't be performed
|
||||
*/
|
||||
/* default */ double compute(MetricKey<ASTAnyTypeDeclaration> key, ASTAnyTypeDeclaration node, boolean force,
|
||||
MetricVersion version, ClassStats memoizer) {
|
||||
|
||||
ParameterizedMetricKey paramKey = ParameterizedMetricKey.getInstance(key, version);
|
||||
// if memo.get(key) == null then the metric has never been computed. NaN is a valid value.
|
||||
Double prev = memoizer.getMemo(paramKey);
|
||||
if (!force && prev != null) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
double val = key.getCalculator().computeFor(node, version);
|
||||
memoizer.memoize(paramKey, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the value of a metric for an operation and stores the result in the OperationStats object.
|
||||
*
|
||||
* @param key The operation metric to compute
|
||||
* @param node The AST node of the operation
|
||||
* @param force Force the recomputation; if unset, we'll first check for a memoized result
|
||||
* @param version The version of the metric to compute
|
||||
* @param memoizer The object memoizing the results
|
||||
*
|
||||
* @return The result of the computation, or {@code Double.NaN} if it couldn't be performed
|
||||
*/
|
||||
/* default */ double compute(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTMethodOrConstructorDeclaration node,
|
||||
boolean force, MetricVersion version, OperationStats memoizer) {
|
||||
|
||||
ParameterizedMetricKey paramKey = ParameterizedMetricKey.getInstance(key, version);
|
||||
Double prev = memoizer.getMemo(paramKey);
|
||||
if (!force && prev != null) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
double val = key.getCalculator().computeFor(node, version);
|
||||
memoizer.memoize(paramKey, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes an aggregate result using a ResultOption.
|
||||
*
|
||||
* @param key The class metric to compute
|
||||
* @param node The AST node of the class
|
||||
* @param force Force the recomputation; if unset, we'll first check for a memoized result
|
||||
* @param version The version of the metric
|
||||
* @param option The type of result to compute
|
||||
* @param stats The ClassStats storing info about the class
|
||||
*
|
||||
* @return The result of the computation, or {@code Double.NaN} if it couldn't be performed
|
||||
*/
|
||||
/* default */ double computeWithResultOption(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTAnyTypeDeclaration node,
|
||||
boolean force, MetricVersion version, ResultOption option, ClassStats stats) {
|
||||
|
||||
List<ASTMethodOrConstructorDeclaration> ops = findOperations(node);
|
||||
|
||||
List<Double> values = new ArrayList<>();
|
||||
for (ASTMethodOrConstructorDeclaration op : ops) {
|
||||
if (key.supports(op)) {
|
||||
OperationStats opStats = stats.getOperationStats(op.getQualifiedName().getOperation(),
|
||||
op.getSignature());
|
||||
double val = this.compute(key, op, force, version, opStats);
|
||||
if (val != Double.NaN) {
|
||||
values.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FUTURE use streams to do that when we upgrade the compiler to 1.8
|
||||
switch (option) {
|
||||
case SUM:
|
||||
return sum(values);
|
||||
case HIGHEST:
|
||||
return highest(values);
|
||||
case AVERAGE:
|
||||
return average(values);
|
||||
default:
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the declaration nodes of all methods or constructors that are declared inside a class.
|
||||
*
|
||||
* @param node The class in which to look for.
|
||||
*
|
||||
* @return The list of all operations declared inside the specified class.
|
||||
*
|
||||
* TODO:cf this one is computed every time
|
||||
*/
|
||||
private static List<ASTMethodOrConstructorDeclaration> findOperations(ASTAnyTypeDeclaration node) {
|
||||
@Override
|
||||
protected List<ASTMethodOrConstructorDeclaration> findOperations(ASTAnyTypeDeclaration node) {
|
||||
|
||||
List<ASTMethodOrConstructorDeclaration> operations = new ArrayList<>();
|
||||
|
||||
@ -146,30 +40,4 @@ public class JavaMetricsComputer {
|
||||
return operations;
|
||||
}
|
||||
|
||||
|
||||
private static double sum(List<Double> values) {
|
||||
double sum = 0;
|
||||
for (double val : values) {
|
||||
sum += val;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
private static double highest(List<Double> values) {
|
||||
double highest = Double.NEGATIVE_INFINITY;
|
||||
for (double val : values) {
|
||||
if (val > highest) {
|
||||
highest = val;
|
||||
}
|
||||
}
|
||||
return highest == Double.NEGATIVE_INFINITY ? 0 : highest;
|
||||
}
|
||||
|
||||
|
||||
private static double average(List<Double> values) {
|
||||
return sum(values) / values.size();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import net.sourceforge.pmd.lang.metrics.api.MetricVersion;
|
||||
import net.sourceforge.pmd.lang.metrics.api.ResultOption;
|
||||
|
||||
/**
|
||||
* Facade of the Java metrics framework.
|
||||
* Inner façade of the Java metrics framework. The static façade delegates to an instance of this class.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
@ -48,7 +48,9 @@ class JavaMetricsFacade {
|
||||
*
|
||||
* @return The value of the metric, or {@code Double.NaN} if the value couln't be computed
|
||||
*/
|
||||
public double computeForType(MetricKey<ASTAnyTypeDeclaration> key, ASTAnyTypeDeclaration node, MetricVersion version) {
|
||||
double computeForType(MetricKey<ASTAnyTypeDeclaration> key, ASTAnyTypeDeclaration node, MetricVersion version) {
|
||||
|
||||
checkKeyNotNull(key);
|
||||
|
||||
if (!key.supports(node)) {
|
||||
return Double.NaN;
|
||||
@ -58,7 +60,7 @@ class JavaMetricsFacade {
|
||||
ClassStats memoizer = topLevelPackageStats.getClassStats(node.getQualifiedName(), false);
|
||||
|
||||
return memoizer == null ? Double.NaN
|
||||
: JavaMetricsComputer.INSTANCE.compute(key, node, false, safeVersion, memoizer);
|
||||
: JavaMetricsComputer.INSTANCE.computeForType(key, node, false, safeVersion, memoizer);
|
||||
}
|
||||
|
||||
|
||||
@ -71,8 +73,10 @@ class JavaMetricsFacade {
|
||||
*
|
||||
* @return The value of the metric, or {@code Double.NaN} if the value couln't be computed
|
||||
*/
|
||||
public double computeForOperation(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTMethodOrConstructorDeclaration node,
|
||||
MetricVersion version) {
|
||||
double computeForOperation(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTMethodOrConstructorDeclaration node,
|
||||
MetricVersion version) {
|
||||
|
||||
checkKeyNotNull(key);
|
||||
|
||||
if (!key.supports(node)) {
|
||||
return Double.NaN;
|
||||
@ -86,7 +90,8 @@ class JavaMetricsFacade {
|
||||
: container.getOperationStats(qname.getOperation(), node.getSignature());
|
||||
|
||||
return memoizer == null ? Double.NaN
|
||||
: JavaMetricsComputer.INSTANCE.compute(key, node, false, safeVersion, memoizer);
|
||||
: JavaMetricsComputer.INSTANCE.computeForOperation(key, node, false, safeVersion,
|
||||
memoizer);
|
||||
|
||||
}
|
||||
|
||||
@ -103,18 +108,25 @@ class JavaMetricsFacade {
|
||||
* @return The value of the metric, or {@code Double.NaN} if the value couln't be computed or {@code option} is
|
||||
* {@code null}
|
||||
*/
|
||||
public double computeWithResultOption(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTAnyTypeDeclaration node,
|
||||
MetricVersion version, ResultOption option) {
|
||||
double computeWithResultOption(MetricKey<ASTMethodOrConstructorDeclaration> key, ASTAnyTypeDeclaration node,
|
||||
MetricVersion version, ResultOption option) {
|
||||
|
||||
checkKeyNotNull(key);
|
||||
|
||||
if (option == null) {
|
||||
throw new IllegalArgumentException("The result option may not be null");
|
||||
}
|
||||
|
||||
MetricVersion safeVersion = (version == null) ? Version.STANDARD : version;
|
||||
ClassStats memoizer = topLevelPackageStats.getClassStats(node.getQualifiedName(), false);
|
||||
|
||||
return memoizer == null ? Double.NaN
|
||||
: JavaMetricsComputer.INSTANCE.computeWithResultOption(key, node, false, safeVersion,
|
||||
option, memoizer);
|
||||
return JavaMetricsComputer.INSTANCE.computeWithResultOption(key, node, false, safeVersion,
|
||||
option, topLevelPackageStats);
|
||||
}
|
||||
|
||||
|
||||
private void checkKeyNotNull(MetricKey<?> key) {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("The metric key may not be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.metrics.ParameterizedMetricKey;
|
||||
|
||||
/**
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public abstract class Memoizer {
|
||||
|
||||
|
||||
private final Map<ParameterizedMetricKey, Double> memo = new HashMap<>();
|
||||
|
||||
|
||||
Double getMemo(ParameterizedMetricKey key) {
|
||||
return memo.get(key);
|
||||
}
|
||||
|
||||
|
||||
void memoize(ParameterizedMetricKey key, double value) {
|
||||
memo.put(key, value);
|
||||
}
|
||||
}
|
@ -4,12 +4,15 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
|
||||
|
||||
/**
|
||||
* Statistics for an operation. Keeps a map of all memoized metrics results.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
class OperationStats extends Memoizer {
|
||||
class OperationStats extends MetricMemoizer<ASTMethodOrConstructorDeclaration> {
|
||||
|
||||
private final String name;
|
||||
|
||||
|
@ -7,10 +7,15 @@ package net.sourceforge.pmd.lang.java.metrics;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.QualifiedName;
|
||||
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.JavaOperationSignature;
|
||||
import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricMemoizer;
|
||||
import net.sourceforge.pmd.lang.metrics.ProjectMirror;
|
||||
|
||||
|
||||
/**
|
||||
@ -20,12 +25,13 @@ import net.sourceforge.pmd.lang.java.metrics.signature.OperationSigMask;
|
||||
* @author Clément Fournier
|
||||
* @see ClassStats
|
||||
*/
|
||||
public final class PackageStats {
|
||||
public final class PackageStats implements ProjectMirror<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {
|
||||
|
||||
private final Map<String, PackageStats> subPackages = new HashMap<>();
|
||||
private final Map<String, ClassStats> classes = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@ -138,6 +144,7 @@ public final class PackageStats {
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the signature of the operation designated by the qualified name is covered by the mask.
|
||||
*
|
||||
@ -152,6 +159,7 @@ public final class PackageStats {
|
||||
return clazz != null && clazz.hasMatchingSig(qname.getOperation(), 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.
|
||||
@ -167,4 +175,16 @@ public final class PackageStats {
|
||||
|
||||
return clazz != null && clazz.hasMatchingSig(fieldName, sigMask);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MetricMemoizer<ASTMethodOrConstructorDeclaration> getOperationStats(QualifiedName qname) {
|
||||
return getOperationStats((JavaQualifiedName) qname, null, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MetricMemoizer<ASTAnyTypeDeclaration> getClassStats(QualifiedName qname) {
|
||||
return getClassStats((JavaQualifiedName) qname, false);
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ public class DataStructureTest extends ParserTst {
|
||||
@Override
|
||||
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
OperationStats op = toplevel.getOperationStats(node.getQualifiedName(), node.getSignature(), false);
|
||||
result.add((int) JavaMetricsComputer.INSTANCE.compute(opMetricKey, node, force, Version.STANDARD, op));
|
||||
result.add((int) JavaMetricsComputer.INSTANCE.computeForOperation(opMetricKey, node, force, Version.STANDARD, op));
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ public class DataStructureTest extends ParserTst {
|
||||
@Override
|
||||
public Object visit(ASTAnyTypeDeclaration node, Object data) {
|
||||
ClassStats clazz = toplevel.getClassStats(node.getQualifiedName(), false);
|
||||
result.add((int) JavaMetricsComputer.INSTANCE.compute(classMetricKey, node, force, Version.STANDARD, clazz));
|
||||
result.add((int) JavaMetricsComputer.INSTANCE.computeForType(classMetricKey, node, force, Version.STANDARD, clazz));
|
||||
return super.visit(node, data);
|
||||
}
|
||||
}, null);
|
||||
|
Reference in New Issue
Block a user