forked from phoedos/pmd
Handle XPath errors gracefully
This commit is contained in:
parent
00fbcdfc9f
commit
fc48f93e92
@ -165,7 +165,7 @@ public class MainDesignerController extends AbstractController<AbstractControlle
|
||||
if (root.isPresent()) {
|
||||
xpathPanelController.evaluateXPath(root.get(), getLanguageVersion());
|
||||
} else {
|
||||
xpathPanelController.invalidateResults(true);
|
||||
xpathPanelController.invalidateResultsExternal(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ public class MainDesignerController extends AbstractController<AbstractControlle
|
||||
*/
|
||||
public void invalidateAst() {
|
||||
nodeInfoPanelController.setFocusNode(null);
|
||||
xpathPanelController.invalidateResults(false);
|
||||
xpathPanelController.invalidateResultsExternal(false);
|
||||
getDesignerRoot().getNodeSelectionChannel().pushEvent(this, null);
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,14 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.validation.ValidationSupport;
|
||||
import org.controlsfx.validation.Validator;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.SuspendableEventStream;
|
||||
import org.reactfx.collection.LiveArrayList;
|
||||
@ -55,6 +58,7 @@ import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.MenuButton;
|
||||
import javafx.scene.control.MenuItem;
|
||||
@ -78,6 +82,8 @@ import javafx.stage.StageStyle;
|
||||
*/
|
||||
public class XPathPanelController extends AbstractController<MainDesignerController> implements NodeSelectionSource {
|
||||
|
||||
private static final Pattern EXCEPTION_PREFIX_PATTERN = Pattern.compile("(?:(?:\\w+\\.)*\\w+:\\s*)*\\s*(.*)$", Pattern.DOTALL);
|
||||
private static final String NO_MATCH_MESSAGE = "No match in text";
|
||||
private static final Duration XPATH_REFRESH_DELAY = Duration.ofMillis(100);
|
||||
private final XPathEvaluator xpathEvaluator = new XPathEvaluator();
|
||||
private final ObservableXPathRuleBuilder ruleBuilder = new ObservableXPathRuleBuilder();
|
||||
@ -135,9 +141,12 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
|
||||
initNodeSelectionHandling(getDesignerRoot(),
|
||||
selectionEvents.filter(Objects::nonNull).map(TextAwareNodeWrapper::getNode), false);
|
||||
|
||||
violationsTitledPane.titleProperty().bind(currentResults.map(List::size).map(n -> "Matched nodes (" + n + ")"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void afterParentInit() {
|
||||
bindToParent();
|
||||
@ -252,7 +261,7 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
try {
|
||||
String xpath = getXpathExpression();
|
||||
if (StringUtils.isBlank(xpath)) {
|
||||
invalidateResults(false);
|
||||
updateResults(false, false, Collections.emptyList(), "Type an XPath expression to show results");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -262,13 +271,12 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
getXpathVersion(),
|
||||
xpath,
|
||||
ruleBuilder.getRuleProperties()));
|
||||
xpathResultListView.setItems(results.stream().map(parent::wrapNode).collect(Collectors.toCollection(LiveArrayList::new)));
|
||||
this.currentResults.setValue(results);
|
||||
violationsTitledPane.setTitle("Matched nodes (" + results.size() + ")");
|
||||
|
||||
updateResults(false, false, results, NO_MATCH_MESSAGE);
|
||||
// Notify that everything went OK so we can avoid logging very recent exceptions
|
||||
raiseParsableXPathFlag();
|
||||
} catch (XPathEvaluationException e) {
|
||||
invalidateResults(true);
|
||||
updateResults(true, false, Collections.emptyList(), sanitizeExceptionMessage(e));
|
||||
logUserException(e, Category.XPATH_EVALUATION_EXCEPTION);
|
||||
}
|
||||
|
||||
@ -280,10 +288,12 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
}
|
||||
|
||||
|
||||
public void invalidateResults(boolean error) {
|
||||
xpathResultListView.getItems().clear();
|
||||
this.currentResults.setValue(Collections.emptyList());
|
||||
violationsTitledPane.setTitle("Matched nodes" + (error ? "\t(error)" : ""));
|
||||
/**
|
||||
* Called by the rest of the app.
|
||||
*/
|
||||
public void invalidateResultsExternal(boolean error) {
|
||||
String placeholder = error ? "Compilation unit is invalid" : NO_MATCH_MESSAGE;
|
||||
updateResults(false, true, Collections.emptyList(), placeholder);
|
||||
}
|
||||
|
||||
|
||||
@ -356,4 +366,27 @@ public class XPathPanelController extends AbstractController<MainDesignerControl
|
||||
}
|
||||
|
||||
|
||||
private void updateResults(boolean xpathError,
|
||||
boolean otherError,
|
||||
List<Node> results,
|
||||
String emptyResultsPlaceholder) {
|
||||
|
||||
Label emptyLabel = xpathError || otherError
|
||||
? new Label(emptyResultsPlaceholder, new FontIcon("fas-exclamation"))
|
||||
: new Label(emptyResultsPlaceholder);
|
||||
|
||||
xpathResultListView.setPlaceholder(emptyLabel);
|
||||
|
||||
xpathResultListView.setItems(results.stream().map(parent::wrapNode).collect(Collectors.toCollection(LiveArrayList::new)));
|
||||
this.currentResults.setValue(results);
|
||||
// only show the error label here when it's an xpath error
|
||||
expressionTitledPane.errorMessageProperty().setValue(xpathError ? emptyResultsPlaceholder : "");
|
||||
}
|
||||
|
||||
|
||||
private static String sanitizeExceptionMessage(Throwable exception) {
|
||||
Matcher matcher = EXCEPTION_PREFIX_PATTERN.matcher(exception.getMessage());
|
||||
return matcher.matches() ? matcher.group(1) : exception.getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,10 @@
|
||||
|
||||
package net.sourceforge.pmd.util.fxdesigner.util.controls;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
import org.reactfx.value.Val;
|
||||
import org.reactfx.value.Var;
|
||||
|
||||
@ -18,6 +19,7 @@ import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.control.ToolBar;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
|
||||
@ -33,10 +35,22 @@ public final class ToolbarTitledPane extends TitledPane {
|
||||
|
||||
private final ToolBar toolBar = new ToolBar();
|
||||
private final Var<String> title = Var.newSimpleVar("Title");
|
||||
private final Var<String> errorMessage = Var.newSimpleVar("");
|
||||
|
||||
|
||||
public ToolbarTitledPane() {
|
||||
|
||||
Label errorLabel = new Label();
|
||||
|
||||
FontIcon errorIcon = new FontIcon("fas-exclamation-triangle");
|
||||
errorLabel.setGraphic(errorIcon);
|
||||
errorLabel.tooltipProperty().bind(errorMessage.map(message -> StringUtils.isBlank(message) ? null : new Tooltip(message)));
|
||||
errorLabel.visibleProperty().bind(errorMessage.map(StringUtils::isNotBlank));
|
||||
// makes the label zero-width when it's not visible
|
||||
errorLabel.managedProperty().bind(errorLabel.visibleProperty());
|
||||
|
||||
toolBar.getItems().add(errorLabel);
|
||||
|
||||
getStyleClass().add("tool-bar-title");
|
||||
|
||||
// change the default
|
||||
@ -79,11 +93,6 @@ public final class ToolbarTitledPane extends TitledPane {
|
||||
}
|
||||
|
||||
|
||||
public void setToolbarItems(Collection<? extends Node> nodes) {
|
||||
toolBar.getItems().setAll(nodes);
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title.getValue();
|
||||
}
|
||||
@ -94,6 +103,10 @@ public final class ToolbarTitledPane extends TitledPane {
|
||||
}
|
||||
|
||||
|
||||
public Var<String> errorMessageProperty() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
/** Title of the pane, not equivalent to {@link #textProperty()}. */
|
||||
public Var<String> titleProperty() {
|
||||
return title;
|
||||
|
Loading…
x
Reference in New Issue
Block a user