diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 8a2bae25df..1576779ef2 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -54,8 +54,9 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues -* all +* core * [#1196](https://github.com/pmd/pmd/issues/1196): \[core] CPD results not consistent between runs + * [#1496](https://github.com/pmd/pmd/issues/1496) \[core] Refactor metrics to be dealt with generically from pmd-core * apex * [#1542](https://github.com/pmd/pmd/pull/1542): \[apex] Include the documentation category * [#1546](https://github.com/pmd/pmd/issues/1546): \[apex] PMD parsing exception for Apex classes using 'inherited sharing' keyword diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java index 6984ff1a36..3a8663871d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java @@ -5,21 +5,34 @@ package net.sourceforge.pmd.lang.apex; import java.io.Writer; +import java.util.Arrays; +import java.util.List; import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; import net.sourceforge.pmd.lang.Parser; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.VisitorStarter; import net.sourceforge.pmd.lang.XPathHandler; +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.ast.DumpFacade; +import net.sourceforge.pmd.lang.apex.metrics.ApexMetricsComputer; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey; import net.sourceforge.pmd.lang.apex.multifile.ApexMultifileVisitorFacade; import net.sourceforge.pmd.lang.apex.rule.ApexRuleViolationFactory; import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; + public class ApexHandler extends AbstractLanguageVersionHandler { + private final ApexMetricsProvider myMetricsProvider = new ApexMetricsProvider(); + + @Override public VisitorStarter getMultifileFacade() { return rootNode -> new ApexMultifileVisitorFacade().initializeWith((ApexNode) rootNode); @@ -51,4 +64,31 @@ public class ApexHandler extends AbstractLanguageVersionHandler { return rootNode -> new DumpFacade().initializeWith(writer, prefix, recurse, (ApexNode) rootNode); } + + @Override + public LanguageMetricsProvider, ASTMethod> getLanguageMetricsProvider() { + return myMetricsProvider; + } + + + private static class ApexMetricsProvider extends AbstractLanguageMetricsProvider, ASTMethod> { + + @SuppressWarnings("unchecked") + ApexMetricsProvider() { + // a wild double cast + super((Class>) (Object) ASTUserClassOrInterface.class, ASTMethod.class, ApexMetricsComputer.getInstance()); + } + + + @Override + public List getAvailableTypeMetrics() { + return Arrays.asList(ApexClassMetricKey.values()); + } + + + @Override + public List getAvailableOperationMetrics() { + return Arrays.asList(ApexOperationMetricKey.values()); + } + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java index d0d82c3d63..63ccc76f74 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.apex.metrics; import java.util.ArrayList; import java.util.List; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; @@ -18,7 +19,13 @@ import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; */ public class ApexMetricsComputer extends AbstractMetricsComputer, ASTMethod> { - static final ApexMetricsComputer INSTANCE = new ApexMetricsComputer(); + private static final ApexMetricsComputer INSTANCE = new ApexMetricsComputer(); + + + @InternalApi + public static ApexMetricsComputer getInstance() { + return INSTANCE; + } @Override diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java index 0bcd271ab9..4ed4fa23d3 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java @@ -27,7 +27,7 @@ public class ApexMetricsFacade extends AbstractMetricsFacade, ASTMethod> getLanguageSpecificComputer() { - return ApexMetricsComputer.INSTANCE; + return ApexMetricsComputer.getInstance(); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java index 9c78229005..49d077318b 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java @@ -91,7 +91,7 @@ public class ApexProjectMirrorTest { @Override public Object visit(ASTMethod node, Object data) { MetricMemoizer op = toplevel.getOperationMemoizer(node.getQualifiedName()); - result.add((int) ApexMetricsComputer.INSTANCE.computeForOperation(opMetricKey, node, force, + result.add((int) ApexMetricsComputer.getInstance().computeForOperation(opMetricKey, node, force, MetricOptions.emptyOptions(), op)); return super.visit(node, data); } @@ -100,7 +100,7 @@ public class ApexProjectMirrorTest { @Override public Object visit(ASTUserClass node, Object data) { MetricMemoizer> clazz = toplevel.getClassMemoizer(node.getQualifiedName()); - result.add((int) ApexMetricsComputer.INSTANCE.computeForType(classMetricKey, node, force, + result.add((int) ApexMetricsComputer.getInstance().computeForType(classMetricKey, node, force, MetricOptions.emptyOptions(), clazz)); return super.visit(node, data); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java index 400be33c40..c00f38a05f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang; import java.io.Writer; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; + /** * This is a generic implementation of the LanguageVersionHandler interface. @@ -71,4 +73,10 @@ public abstract class AbstractLanguageVersionHandler implements LanguageVersionH public DFAGraphRule getDFAGraphRule() { return null; } + + + @Override + public LanguageMetricsProvider getLanguageMetricsProvider() { + return null; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java index 5037c9f456..5474b1758a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java @@ -6,8 +6,10 @@ package net.sourceforge.pmd.lang; import java.io.Writer; +import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; /** @@ -135,4 +137,17 @@ public interface LanguageVersionHandler { @Deprecated @InternalApi DFAGraphRule getDFAGraphRule(); + + + /** + * Returns the metrics provider for this language version, + * or null if it has none. + * + * Note: this is experimental, ie unstable until 7.0.0, after + * which it will probably be promoted to a stable API. For + * instance the return type will probably be changed to an Optional. + */ + @Experimental + LanguageMetricsProvider getLanguageMetricsProvider(); + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java new file mode 100644 index 0000000000..ec57cceff9 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java @@ -0,0 +1,92 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics; + +import java.util.List; +import java.util.Map; + +import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.lang.LanguageVersionHandler; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.QualifiableNode; + + +/** + * Language-specific provider for metrics. Knows about all the metrics + * defined for a language. Can be used e.g. to build GUI applications + * like the designer, in a language independent way. Accessible through + * {@link LanguageVersionHandler#getLanguageMetricsProvider()}. + * + * Note: this is experimental, ie unstable until 7.0.0, after which it will probably + * be promoted to a real API. + * + * @param Type of type declaration nodes of the language + * @param Type of operation declaration nodes of the language + * + * @author Clément Fournier + * @since 6.11.0 + */ +@Experimental +public interface LanguageMetricsProvider { + + /** + * Returns a list of all supported type metric keys + * for the language. + */ + List> getAvailableTypeMetrics(); + + + /** + * Returns a list of all supported operation metric keys + * for the language. + */ + List> getAvailableOperationMetrics(); + + + /** + * Returns the given node casted to {@link T} if it's of the correct + * type, otherwise returns null. + */ + T asTypeNode(Node anyNode); + + + /** + * Returns the given node casted to {@link O} if it's of the correct + * type, otherwise returns null. + */ + O asOperationNode(Node anyNode); + + + /** + * Like {@link MetricsComputer#computeForType(MetricKey, QualifiableNode, boolean, MetricOptions, MetricMemoizer)}, + * but performs no memoisation. + */ + double computeForType(MetricKey key, T node, MetricOptions options); + + + /** + * Like {@link MetricsComputer#computeForOperation(MetricKey, QualifiableNode, boolean, MetricOptions, MetricMemoizer)} + * but performs no memoisation. + */ + double computeForOperation(MetricKey key, O node, MetricOptions options); + + + /** + * Like {@link MetricsComputer#computeWithResultOption(MetricKey, QualifiableNode, boolean, MetricOptions, ResultOption, ProjectMemoizer)} + * but performs no memoisation. + */ + double computeWithResultOption(MetricKey key, T node, MetricOptions options, ResultOption option); + + + /** + * Computes all metrics available on the given node. + * The returned results may contain Double.NaN as a value. + * + * @param node Node to inspect + * + * @return A map of metric key to their result, possibly empty, but with no null value + */ + Map, Double> computeAllMetricsFor(Node node); +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java index 4f7521df0d..2259ed8956 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java @@ -41,5 +41,6 @@ public interface MetricKey { */ boolean supports(N node); + // TODO the metric key should know about supported options } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java new file mode 100644 index 0000000000..d6f46e0d7a --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java @@ -0,0 +1,90 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import java.util.HashMap; +import java.util.Map; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.QualifiableNode; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; +import net.sourceforge.pmd.lang.metrics.MetricOptions; +import net.sourceforge.pmd.lang.metrics.MetricsComputer; +import net.sourceforge.pmd.lang.metrics.ResultOption; + + +/** + * Base implementation for {@link LanguageMetricsProvider}. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public abstract class AbstractLanguageMetricsProvider implements LanguageMetricsProvider { + + private final Class tClass; + private final Class oClass; + private final MetricsComputer myComputer; + + + protected AbstractLanguageMetricsProvider(Class tClass, + Class oClass, + MetricsComputer computer) { + this.tClass = tClass; + this.oClass = oClass; + this.myComputer = computer; + } + + + @Override + public T asTypeNode(Node anyNode) { + return tClass.isInstance(anyNode) ? tClass.cast(anyNode) : null; + } + + + @Override + public O asOperationNode(Node anyNode) { + return oClass.isInstance(anyNode) ? oClass.cast(anyNode) : null; + } + + + @Override + public double computeForType(MetricKey key, T node, MetricOptions options) { + return myComputer.computeForType(key, node, true, options, DummyMetricMemoizer.getInstance()); + } + + + @Override + public double computeForOperation(MetricKey key, O node, MetricOptions options) { + return myComputer.computeForOperation(key, node, true, options, DummyMetricMemoizer.getInstance()); + } + + + @Override + public double computeWithResultOption(MetricKey key, T node, MetricOptions options, ResultOption option) { + return myComputer.computeWithResultOption(key, node, true, options, option, DummyProjectMemoizer.getInstance()); + } + + + @Override + public Map, Double> computeAllMetricsFor(Node node) { + Map, Double> results = new HashMap<>(); + T t = asTypeNode(node); + if (t != null) { + for (MetricKey tkey : getAvailableTypeMetrics()) { + results.put(tkey, computeForType(tkey, t, MetricOptions.emptyOptions())); + } + } + O o = asOperationNode(node); + if (o != null) { + for (MetricKey okey : getAvailableOperationMetrics()) { + results.put(okey, computeForOperation(okey, o, MetricOptions.emptyOptions())); + } + } + + return results; + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java new file mode 100644 index 0000000000..1d6cae45a1 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.metrics.MetricMemoizer; +import net.sourceforge.pmd.lang.metrics.ParameterizedMetricKey; + + +/** + * Memoizes nothing. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public final class DummyMetricMemoizer implements MetricMemoizer { + + private static final DummyMetricMemoizer INSTANCE = new DummyMetricMemoizer<>(); + + + private DummyMetricMemoizer() { + + } + + + @Override + public Double getMemo(ParameterizedMetricKey key) { + return null; + } + + + @Override + public void memoize(ParameterizedMetricKey key, double value) { + // do nothing + } + + + @SuppressWarnings("unchecked") + public static DummyMetricMemoizer getInstance() { + return (DummyMetricMemoizer) INSTANCE; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java new file mode 100644 index 0000000000..10abe75a9e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java @@ -0,0 +1,45 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import net.sourceforge.pmd.lang.ast.QualifiableNode; +import net.sourceforge.pmd.lang.ast.QualifiedName; +import net.sourceforge.pmd.lang.metrics.MetricMemoizer; +import net.sourceforge.pmd.lang.metrics.ProjectMemoizer; + + +/** + * Memoizes nothing. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public final class DummyProjectMemoizer implements ProjectMemoizer { + + private static final DummyProjectMemoizer INSTANCE = new DummyProjectMemoizer<>(); + + + private DummyProjectMemoizer() { + + } + + + @Override + public MetricMemoizer getOperationMemoizer(QualifiedName qname) { + return DummyMetricMemoizer.getInstance(); + } + + + @Override + public MetricMemoizer getClassMemoizer(QualifiedName qname) { + return DummyMetricMemoizer.getInstance(); + } + + + @SuppressWarnings("unchecked") + public static DummyProjectMemoizer getInstance() { + return (DummyProjectMemoizer) INSTANCE; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java index 725a4aae03..179ac6a6f1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.java; import java.io.Writer; +import java.util.Arrays; +import java.util.List; import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; import net.sourceforge.pmd.lang.DataFlowHandler; @@ -14,11 +16,16 @@ import net.sourceforge.pmd.lang.XPathHandler; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.DumpFacade; import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade; import net.sourceforge.pmd.lang.java.dfa.JavaDFAGraphRule; +import net.sourceforge.pmd.lang.java.metrics.JavaMetricsComputer; +import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.java.multifile.MultifileVisitorFacade; import net.sourceforge.pmd.lang.java.qname.QualifiedNameResolver; import net.sourceforge.pmd.lang.java.rule.JavaRuleViolationFactory; @@ -30,6 +37,9 @@ import net.sourceforge.pmd.lang.java.xpath.MetricFunction; import net.sourceforge.pmd.lang.java.xpath.TypeIsExactlyFunction; import net.sourceforge.pmd.lang.java.xpath.TypeIsFunction; import net.sourceforge.pmd.lang.java.xpath.TypeOfFunction; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; +import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sf.saxon.sxpath.IndependentContext; @@ -42,6 +52,8 @@ import net.sf.saxon.sxpath.IndependentContext; */ public abstract class AbstractJavaHandler extends AbstractLanguageVersionHandler { + private final LanguageMetricsProvider myMetricsProvider = new JavaMetricsProvider(); + @Override public DataFlowHandler getDataFlowHandler() { return new JavaDataFlowHandler(); @@ -147,4 +159,31 @@ public abstract class AbstractJavaHandler extends AbstractLanguageVersionHandler public DFAGraphRule getDFAGraphRule() { return new JavaDFAGraphRule(); } + + + @Override + public LanguageMetricsProvider getLanguageMetricsProvider() { + return myMetricsProvider; + } + + + private static class JavaMetricsProvider extends AbstractLanguageMetricsProvider { + + + JavaMetricsProvider() { + super(ASTAnyTypeDeclaration.class, MethodLikeNode.class, JavaMetricsComputer.getInstance()); + } + + + @Override + public List> getAvailableTypeMetrics() { + return Arrays.asList(JavaClassMetricKey.values()); + } + + + @Override + public List> getAvailableOperationMetrics() { + return Arrays.asList(JavaOperationMetricKey.values()); + } + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java index b96d66b93a..9d95cb4eda 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.metrics; import java.util.ArrayList; import java.util.List; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; @@ -20,12 +21,19 @@ import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; */ public final class JavaMetricsComputer extends AbstractMetricsComputer { - static final JavaMetricsComputer INSTANCE = new JavaMetricsComputer(); + private static final JavaMetricsComputer INSTANCE = new JavaMetricsComputer(); private JavaMetricsComputer() { } + + @InternalApi + public static JavaMetricsComputer getInstance() { + return INSTANCE; + } + + // TODO: doesn't consider lambdas @Override protected List findOperations(ASTAnyTypeDeclaration node) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java index 6ddd2e1df9..f239407db7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java @@ -33,7 +33,7 @@ class JavaMetricsFacade extends AbstractMetricsFacade getLanguageSpecificComputer() { - return JavaMetricsComputer.INSTANCE; + return JavaMetricsComputer.getInstance(); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java index 2d51621aa1..06ff9ed917 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java @@ -96,5 +96,4 @@ public enum JavaClassMetricKey implements MetricKey { return calculator.supports(node); } - } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java index cd3064565d..15352e90d1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java @@ -205,12 +205,12 @@ public class ParserTstUtil { } - public static LanguageVersionHandler getLanguageVersionHandler(String version) { - return LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(version).getLanguageVersionHandler(); + public static AbstractJavaHandler getLanguageVersionHandler(String version) { + return (AbstractJavaHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(version).getLanguageVersionHandler(); } - public static LanguageVersionHandler getDefaultLanguageVersionHandler() { - return LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion().getLanguageVersionHandler(); + public static AbstractJavaHandler getDefaultLanguageVersionHandler() { + return (AbstractJavaHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion().getLanguageVersionHandler(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java new file mode 100644 index 0000000000..919253837e --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java @@ -0,0 +1,77 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.metrics; + + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.java.ParserTstUtil; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; +import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; + + +/** + * @author Clément Fournier + */ +public class JavaMetricsProviderTest { + + @Test + public void testComputeAllMetrics() { + + LanguageMetricsProvider provider = ParserTstUtil.getLanguageVersionHandler("1.8").getLanguageMetricsProvider(); + + ASTCompilationUnit acu = ParserTstUtil.parseAndTypeResolveJava("1.8", + "class Foo { void bar() { System.out.println(1); } }"); + + ASTAnyTypeDeclaration type = acu.getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> results = provider.computeAllMetricsFor(type); + + for (JavaClassMetricKey key : JavaClassMetricKey.values()) { + assertTrue(results.containsKey(key)); + } + + MethodLikeNode op = acu.getFirstDescendantOfType(MethodLikeNode.class); + + Map, Double> opResults = provider.computeAllMetricsFor(op); + + for (JavaOperationMetricKey key : JavaOperationMetricKey.values()) { + assertTrue(opResults.containsKey(key)); + } + } + + + @Test + public void testThereIsNoMemoisation() { + + LanguageMetricsProvider provider = ParserTstUtil.getLanguageVersionHandler("1.8").getLanguageMetricsProvider(); + + ASTAnyTypeDeclaration tdecl1 = ParserTstUtil.parseAndTypeResolveJava("1.8", + "class Foo { void bar() { System.out.println(1); } }").getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> reference = provider.computeAllMetricsFor(tdecl1); + + ASTAnyTypeDeclaration tdecl2 = ParserTstUtil.parseAndTypeResolveJava("1.8", + // same name, different characteristics + "class Foo { void bar(){} \npublic void hey() { System.out.println(1); } }").getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> secondTest = provider.computeAllMetricsFor(tdecl2); + + assertNotEquals(reference, secondTest); + + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java index eee8a7152b..b40fa44b5f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java @@ -74,7 +74,7 @@ public class ProjectMemoizerTest { @Override public Object visit(ASTMethodOrConstructorDeclaration node, Object data) { MetricMemoizer op = toplevel.getOperationMemoizer(node.getQualifiedName()); - result.add((int) JavaMetricsComputer.INSTANCE.computeForOperation(opMetricKey, node, force, + result.add((int) JavaMetricsComputer.getInstance().computeForOperation(opMetricKey, node, force, MetricOptions.emptyOptions(), op)); return super.visit(node, data); } @@ -83,7 +83,7 @@ public class ProjectMemoizerTest { @Override public Object visit(ASTAnyTypeDeclaration node, Object data) { MetricMemoizer clazz = toplevel.getClassMemoizer(node.getQualifiedName()); - result.add((int) JavaMetricsComputer.INSTANCE.computeForType(classMetricKey, node, force, + result.add((int) JavaMetricsComputer.getInstance().computeForType(classMetricKey, node, force, MetricOptions.emptyOptions(), clazz)); return super.visit(node, data); } diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml index 7a682c1d2e..e79926fc8c 100644 --- a/pmd-ui/pom.xml +++ b/pmd-ui/pom.xml @@ -143,48 +143,56 @@ pmd-apex ${project.version} true + runtime net.sourceforge.pmd pmd-java ${project.version} true + runtime net.sourceforge.pmd pmd-javascript ${project.version} true + runtime net.sourceforge.pmd pmd-jsp ${project.version} true + runtime net.sourceforge.pmd pmd-plsql ${project.version} true + runtime net.sourceforge.pmd pmd-visualforce ${project.version} true + runtime net.sourceforge.pmd pmd-vm ${project.version} true + runtime net.sourceforge.pmd pmd-xml ${project.version} true + runtime junit diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java index 46b6b11882..30ddf85b04 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java @@ -6,15 +6,16 @@ package net.sourceforge.pmd.util.fxdesigner; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import org.reactfx.EventStreams; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.Attribute; -import net.sourceforge.pmd.lang.java.ast.TypeNode; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; -import net.sourceforge.pmd.util.fxdesigner.model.MetricEvaluator; import net.sourceforge.pmd.util.fxdesigner.model.MetricResult; import net.sourceforge.pmd.util.fxdesigner.util.AbstractController; import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeCell; @@ -56,7 +57,7 @@ public class NodeInfoPanelController extends AbstractController { private Label metricsTitleLabel; @FXML private TreeView scopeHierarchyTreeView; - private final MetricEvaluator metricEvaluator = new MetricEvaluator(); + private Node selectedNode; public NodeInfoPanelController(MainDesignerController mainController) { @@ -122,11 +123,17 @@ public class NodeInfoPanelController extends AbstractController { private ObservableList evaluateAllMetrics(Node n) { - try { - return FXCollections.observableArrayList(metricEvaluator.evaluateAllMetrics(n)); - } catch (UnsupportedOperationException e) { + LanguageMetricsProvider provider = parent.getLanguageVersion().getLanguageVersionHandler().getLanguageMetricsProvider(); + if (provider == null) { return FXCollections.emptyObservableList(); } + List resultList = + provider.computeAllMetricsFor(n) + .entrySet() + .stream() + .map(e -> new MetricResult(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + return FXCollections.observableArrayList(resultList); } @@ -143,9 +150,10 @@ public class NodeInfoPanelController extends AbstractController { + ((attribute.getValue() != null) ? attribute.getStringValue() : "null")); } - if (node instanceof TypeNode) { - result.add("typeIs() = " + ((TypeNode) node).getType()); - } + // TODO maybe put some equivalent to TypeNode inside pmd-core + // if (node instanceof TypeNode) { + // result.add("typeIs() = " + ((TypeNode) node).getType()); + // } Collections.sort(result); return result; } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricEvaluator.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricEvaluator.java deleted file mode 100644 index d590f97ddd..0000000000 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricEvaluator.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.util.fxdesigner.model; - -import java.util.ArrayList; -import java.util.List; - -import net.sourceforge.pmd.lang.apex.ast.ASTMethod; -import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; -import net.sourceforge.pmd.lang.apex.metrics.ApexMetrics; -import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey; -import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; -import net.sourceforge.pmd.lang.java.metrics.JavaMetrics; -import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; -import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; - -/** - * Evaluates metrics. - * - * @author Clément Fournier - * @since 6.0.0 - */ -public class MetricEvaluator { - - /** - * Evaluates all available metrics and returns a list of results. - * - * @param node Node - * - * @return List of all metric results (metric key + result), including NaN results - * - * @throws UnsupportedOperationException If no metrics are available for this node - */ - public List evaluateAllMetrics(Node node) throws UnsupportedOperationException { - if (ASTAnyTypeDeclaration.class.isInstance(node)) { - return evaluateAllMetrics((ASTAnyTypeDeclaration) node); - } else if (ASTMethodOrConstructorDeclaration.class.isInstance(node)) { - return evaluateAllMetrics((ASTMethodOrConstructorDeclaration) node); - } else if (ASTMethod.class.isInstance(node)) { - return evaluateAllMetrics((ASTMethod) node); - } else if (ASTUserClass.class.isInstance(node)) { - return evaluateAllMetrics((ASTUserClass) node); - } - throw new UnsupportedOperationException("That language does not support metrics"); - } - - - private List evaluateAllMetrics(ASTMethodOrConstructorDeclaration node) { - List metricResults = new ArrayList<>(); - for (JavaOperationMetricKey key : JavaOperationMetricKey.values()) { - metricResults.add(new MetricResult(key, JavaMetrics.get(key, node))); - } - - return metricResults; - } - - - private List evaluateAllMetrics(ASTAnyTypeDeclaration node) { - List metricResults = new ArrayList<>(); - for (JavaClassMetricKey key : JavaClassMetricKey.values()) { - metricResults.add(new MetricResult(key, JavaMetrics.get(key, node))); - } - - return metricResults; - } - - - private List evaluateAllMetrics(ASTMethod node) { - List metricResults = new ArrayList<>(); - for (ApexOperationMetricKey key : ApexOperationMetricKey.values()) { - metricResults.add(new MetricResult(key, ApexMetrics.get(key, node))); - } - - return metricResults; - } - - - private List evaluateAllMetrics(ASTUserClass node) { - List metricResults = new ArrayList<>(); - for (ApexClassMetricKey key : ApexClassMetricKey.values()) { - metricResults.add(new MetricResult(key, ApexMetrics.get(key, node))); - } - - return metricResults; - } - -} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java index 49958bd6e9..be51cc7c41 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java @@ -4,10 +4,6 @@ package net.sourceforge.pmd.util.fxdesigner.util.controls; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.symboltable.ClassNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.Scope; @@ -42,31 +38,7 @@ public class ScopeHierarchyTreeCell extends TreeCell { private String getTextForDeclaration(NameDeclaration declaration) { - - StringBuilder sb = new StringBuilder(); - if (declaration instanceof MethodNameDeclaration - || declaration instanceof net.sourceforge.pmd.lang.plsql.symboltable.MethodNameDeclaration) { - sb.append("Method "); - } else if (declaration instanceof VariableNameDeclaration - || declaration instanceof net.sourceforge.pmd.lang.plsql.symboltable.VariableNameDeclaration) { - sb.append("Variable "); - } else if (declaration instanceof ClassNameDeclaration - || declaration instanceof net.sourceforge.pmd.lang.plsql.symboltable.ClassNameDeclaration) { - sb.append("Class "); - } - - Class type = declaration.getNode() instanceof TypeNode ? ((TypeNode) declaration.getNode()).getType() - : null; - - sb.append(declaration.getName()); - - if (type != null) { - sb.append(" : ").append(type.getSimpleName()); - } - - sb.append(" (l. ").append(declaration.getNode().getBeginLine()).append(")"); - - return sb.toString(); + return declaration.toString(); // that's nice enough for now } }