diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml
index a55967c8ff..c1a331ca7e 100644
--- a/pmd-ui/pom.xml
+++ b/pmd-ui/pom.xml
@@ -31,5 +31,10 @@
richtextfx
0.6.10
+
+ net.sourceforge.pmd
+ pmd-apex
+ 6.0.0-SNAPSHOT
+
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/DesignerWindowPresenter.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/DesignerWindowPresenter.java
index a89c1eac81..f776aefc49 100644
--- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/DesignerWindowPresenter.java
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/DesignerWindowPresenter.java
@@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
import net.sourceforge.pmd.util.fxdesigner.model.ASTManager;
+import net.sourceforge.pmd.util.fxdesigner.model.MetricResult;
import net.sourceforge.pmd.util.fxdesigner.model.ParseTimeException;
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
@@ -28,6 +29,7 @@ import net.sourceforge.pmd.util.fxdesigner.view.DesignerWindow;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
+import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.Alert;
@@ -38,6 +40,7 @@ import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleGroup;
+import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
/**
@@ -66,6 +69,7 @@ public class DesignerWindowPresenter {
initializeLanguageVersionMenu();
initializeASTTreeView();
initializeXPath();
+ initialiseNodeInfoSection();
bindModelToView();
try {
@@ -109,6 +113,11 @@ public class DesignerWindowPresenter {
}
+ private void initialiseNodeInfoSection() {
+ view.getMetricResultsListView().setCellFactory(param -> new MetricResultListCell());
+ }
+
+
private void initializeXPath() {
ToggleGroup xpathVersionToggleGroup = view.getXpathVersionToggleGroup();
@@ -136,13 +145,20 @@ public class DesignerWindowPresenter {
TreeView astTreeView = view.getAstTreeView();
astTreeView.setCellFactory(param -> new ASTTreeCell());
- astTreeView.getSelectionModel()
- .selectedItemProperty()
- .addListener((observable, oldValue, newValue) -> {
- if (newValue != null) {
- onNodeItemSelected(newValue.getValue());
- }
- });
+
+ ReadOnlyObjectProperty> selectedItemProperty
+ = astTreeView.getSelectionModel().selectedItemProperty();
+
+ selectedItemProperty.addListener(observable -> {
+ view.getMetricResultsListView().getItems().clear();
+ view.getXpathAttributesListView().getItems().clear();
+ });
+
+ selectedItemProperty.addListener((observable, oldValue, newValue) -> {
+ if (newValue != null) {
+ onNodeItemSelected(newValue.getValue());
+ }
+ });
}
@@ -152,6 +168,14 @@ public class DesignerWindowPresenter {
ObservableList atts = DesignerUtil.getAttributes(selectedValue);
view.getXpathAttributesListView().setItems(atts);
+ ObservableList metrics = model.evaluateAllMetrics(selectedValue);
+ view.getMetricResultsListView().setItems(metrics);
+ view.notifyMetricsAvailable(metrics.stream()
+ .map(MetricResult::getValue)
+ .filter(result -> !result.isNaN())
+ .count());
+
+
DesignerUtil.highlightNode(view.getCodeEditorArea(), selectedValue);
}
}
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MetricResultListCell.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MetricResultListCell.java
new file mode 100644
index 0000000000..f04d9f31a5
--- /dev/null
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MetricResultListCell.java
@@ -0,0 +1,45 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.fxdesigner;
+
+import java.util.Locale;
+
+import net.sourceforge.pmd.util.fxdesigner.model.MetricResult;
+
+import javafx.scene.control.ListCell;
+
+/**
+ * List cell for a metric result.
+ *
+ * @author Clément Fournier
+ * @since 6.0.0
+ */
+public class MetricResultListCell extends ListCell {
+
+
+ @Override
+ protected void updateItem(MetricResult item, boolean empty) {
+ super.updateItem(item, empty);
+
+ if (empty || item == null) {
+ setText(null);
+ setGraphic(null);
+ } else {
+ setText(item.getKey().name() + " = " + niceDoubleString(item.getValue()));
+ }
+ }
+
+
+ /** Gets a nice string representation of a double. */
+ private String niceDoubleString(double val) {
+ if (val == (int) val) {
+ return String.valueOf((int) val);
+ } else {
+ return String.format(Locale.ROOT, "%.4f", val);
+ }
+ }
+
+
+}
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java
index 57205edf25..8f6d8129b6 100644
--- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java
@@ -38,9 +38,12 @@ public class ASTManager {
/** Selected language version. */
private ObjectProperty languageVersion = new SimpleObjectProperty<>();
- /** Evaluates XPath queries and stores the results. */
+ /** Evaluates XPath queries. */
private XPathEvaluator xpathEvaluator = new XPathEvaluator();
+ /** Evaluates metrics on a node. */
+ private MetricEvaluator metricEvaluator = new MetricEvaluator();
+
public LanguageVersion getLanguageVersion() {
return languageVersion.get();
@@ -62,6 +65,22 @@ public class ASTManager {
}
+ /**
+ * Evaluates all available metrics for that node.
+ *
+ * @param n Node
+ *
+ * @return A list of all the metric results that could be computed, possibly with some Double.NaN results
+ */
+ public ObservableList evaluateAllMetrics(Node n) {
+ try {
+ return FXCollections.observableArrayList(metricEvaluator.evaluateAllMetrics(n));
+ } catch (UnsupportedOperationException e) {
+ return FXCollections.emptyObservableList();
+ }
+ }
+
+
/**
* Evaluates an XPath request, returns the matching nodes.
*
@@ -94,6 +113,7 @@ public class ASTManager {
Node node = parser.parse(null, new StringReader(source));
languageVersionHandler.getSymbolFacade().start(node);
languageVersionHandler.getTypeResolutionFacade(ASTManager.class.getClassLoader()).start(node);
+ languageVersionHandler.getMetricsVisitorFacade().start(node);
compilationUnit = node;
lastValidSource = source;
lastLanguageVersion = languageVersion.get();
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
new file mode 100644
index 0000000000..2bbc5ead86
--- /dev/null
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricEvaluator.java
@@ -0,0 +1,92 @@
+/**
+ * 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
+ */
+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
+ */
+ 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/model/MetricResult.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricResult.java
new file mode 100644
index 0000000000..34b86828e1
--- /dev/null
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/MetricResult.java
@@ -0,0 +1,39 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.fxdesigner.model;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+
+import net.sourceforge.pmd.lang.metrics.MetricKey;
+
+/**
+ * @author Clément Fournier
+ * @since 6.0.0
+ */
+public class MetricResult {
+
+ private final SimpleEntry, Double> simpleEntry;
+
+
+ public MetricResult(MetricKey> key, Double value) {
+ simpleEntry = new SimpleEntry<>(key, value);
+ }
+
+
+ MetricResult(Entry extends MetricKey>, ? extends Double> entry) {
+ simpleEntry = new SimpleEntry<>(entry);
+ }
+
+
+ public MetricKey> getKey() {
+ return simpleEntry.getKey();
+ }
+
+
+ public Double getValue() {
+ return simpleEntry.getValue();
+ }
+}
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java
index a6a7cd1569..6669296e49 100644
--- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java
@@ -69,6 +69,10 @@ public class DesignerUtil {
*/
public static void highlightNode(CodeArea codeArea, Node node) {
+ if (node.getBeginLine() == node.getEndLine()
+ && node.getBeginColumn() == node.getEndColumn()) {
+ return;
+ }
StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>();
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/view/DesignerWindow.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/view/DesignerWindow.java
index 8394a287a2..3dd34519e4 100644
--- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/view/DesignerWindow.java
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/view/DesignerWindow.java
@@ -12,6 +12,7 @@ import org.fxmisc.richtext.LineNumberFactory;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.fxdesigner.DesignerWindowPresenter;
+import net.sourceforge.pmd.util.fxdesigner.model.MetricResult;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
@@ -40,6 +41,8 @@ import javafx.util.Duration;
*/
public class DesignerWindow implements Initializable {
+ @FXML
+ public TitledPane metricResultsTitledPane;
@FXML
private CodeArea codeEditorArea;
@FXML
@@ -72,6 +75,9 @@ public class DesignerWindow implements Initializable {
private TitledPane astTitledPane;
@FXML
private SplitPane mainVerticalSplitPane;
+ @FXML
+ private ListView metricResultsListView;
+
/* */
private StringProperty sourceCodeProperty;
@@ -125,6 +131,12 @@ public class DesignerWindow implements Initializable {
}
+ public void notifyMetricsAvailable(long numMetrics) {
+ metricResultsTitledPane.setText("Metrics\t(" + (numMetrics == 0 ? "none" : numMetrics) + " available)");
+ metricResultsTitledPane.setDisable(numMetrics == 0);
+ }
+
+
public void notifyOutdatedAST() {
astTitledPane.setText("Abstract syntax tree (outdated)");
}
@@ -214,4 +226,9 @@ public class DesignerWindow implements Initializable {
public SplitPane getMainVerticalSplitPane() {
return mainVerticalSplitPane;
}
+
+
+ public ListView getMetricResultsListView() {
+ return metricResultsListView;
+ }
}
diff --git a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/designer.fxml b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/designer.fxml
index 3590bd8b06..842c153429 100644
--- a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/designer.fxml
+++ b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/designer.fxml
@@ -1,5 +1,10 @@
+
+
+
+
+
@@ -21,82 +26,67 @@
-
+
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
+
-
+
@@ -105,42 +95,26 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -151,51 +125,37 @@
-
-
+
-
+
-
+
-
+