Merge branch 'saxon-rulechain-excludes'
This commit is contained in:
@ -77,7 +77,7 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
|
||||
/**
|
||||
* Representation of an XPath query, created at {@link #initializeXPathExpression()} using {@link #xpath}.
|
||||
*/
|
||||
private XPathExpression xpathExpression;
|
||||
XPathExpression xpathExpression;
|
||||
|
||||
/**
|
||||
* Holds the static context later used to match the variables in the dynamic context in
|
||||
|
@ -13,8 +13,14 @@ import net.sf.saxon.om.Axis;
|
||||
|
||||
/**
|
||||
* Simple printer for saxon expressions. Might be useful for debugging / during development.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>
|
||||
* ExpressionPrinter printer = new ExpressionPrinter();
|
||||
* printer.visit(query.xpathExpression.getInternalExpression());
|
||||
* </pre>
|
||||
*/
|
||||
public class ExpressionPrinter extends Visitor {
|
||||
public class ExpressionPrinter extends SaxonExprVisitor {
|
||||
private int depth = 0;
|
||||
|
||||
private void print(String s) {
|
||||
|
@ -13,6 +13,7 @@ import net.sf.saxon.Configuration;
|
||||
import net.sf.saxon.expr.AxisExpression;
|
||||
import net.sf.saxon.expr.Expression;
|
||||
import net.sf.saxon.expr.FilterExpression;
|
||||
import net.sf.saxon.expr.LazyExpression;
|
||||
import net.sf.saxon.expr.PathExpression;
|
||||
import net.sf.saxon.expr.RootExpression;
|
||||
import net.sf.saxon.om.Axis;
|
||||
@ -33,17 +34,22 @@ import net.sf.saxon.type.Type;
|
||||
* <p>DocumentSorter expression is removed. The sorting of the resulting nodes needs to be done
|
||||
* after all (sub)expressions have been executed.
|
||||
*/
|
||||
public class RuleChainAnalyzer extends Visitor {
|
||||
public class RuleChainAnalyzer extends SaxonExprVisitor {
|
||||
private final Configuration configuration;
|
||||
private String rootElement;
|
||||
private boolean rootElementReplaced;
|
||||
private boolean insideLazyExpression;
|
||||
private boolean foundPathInsideLazy;
|
||||
|
||||
public RuleChainAnalyzer(Configuration currentConfiguration) {
|
||||
this.configuration = currentConfiguration;
|
||||
}
|
||||
|
||||
public String getRootElement() {
|
||||
return rootElement;
|
||||
if (!foundPathInsideLazy && rootElementReplaced) {
|
||||
return rootElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -55,7 +61,7 @@ public class RuleChainAnalyzer extends Visitor {
|
||||
|
||||
@Override
|
||||
public Expression visit(PathExpression e) {
|
||||
if (rootElement == null) {
|
||||
if (!insideLazyExpression && rootElement == null) {
|
||||
Expression result = super.visit(e);
|
||||
if (rootElement != null && !rootElementReplaced) {
|
||||
if (result instanceof PathExpression) {
|
||||
@ -79,6 +85,9 @@ public class RuleChainAnalyzer extends Visitor {
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (insideLazyExpression) {
|
||||
foundPathInsideLazy = true;
|
||||
}
|
||||
return super.visit(e);
|
||||
}
|
||||
}
|
||||
@ -96,6 +105,15 @@ public class RuleChainAnalyzer extends Visitor {
|
||||
return super.visit(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression visit(LazyExpression e) {
|
||||
boolean prevCtx = insideLazyExpression;
|
||||
insideLazyExpression = true;
|
||||
Expression result = super.visit(e);
|
||||
insideLazyExpression = prevCtx;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Comparator<Node> documentOrderComparator() {
|
||||
return net.sourceforge.pmd.lang.rule.xpath.internal.DocumentSorter.INSTANCE;
|
||||
}
|
||||
|
@ -5,8 +5,10 @@
|
||||
package net.sourceforge.pmd.lang.rule.xpath.internal;
|
||||
|
||||
import net.sf.saxon.expr.AxisExpression;
|
||||
import net.sf.saxon.expr.BooleanExpression;
|
||||
import net.sf.saxon.expr.Expression;
|
||||
import net.sf.saxon.expr.FilterExpression;
|
||||
import net.sf.saxon.expr.LazyExpression;
|
||||
import net.sf.saxon.expr.LetExpression;
|
||||
import net.sf.saxon.expr.PathExpression;
|
||||
import net.sf.saxon.expr.QuantifiedExpression;
|
||||
@ -14,7 +16,7 @@ import net.sf.saxon.expr.RootExpression;
|
||||
import net.sf.saxon.expr.VennExpression;
|
||||
import net.sf.saxon.sort.DocumentSorter;
|
||||
|
||||
abstract class Visitor {
|
||||
abstract class SaxonExprVisitor {
|
||||
public Expression visit(DocumentSorter e) {
|
||||
Expression base = visit(e.getBaseExpression());
|
||||
return new DocumentSorter(base);
|
||||
@ -63,6 +65,18 @@ abstract class Visitor {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Expression visit(LazyExpression e) {
|
||||
Expression base = visit(e.getBaseExpression());
|
||||
return LazyExpression.makeLazyExpression(base);
|
||||
}
|
||||
|
||||
public Expression visit(BooleanExpression e) {
|
||||
final Expression[] operands = e.getOperands();
|
||||
Expression operand0 = visit(operands[0]);
|
||||
Expression operand1 = visit(operands[1]);
|
||||
return new BooleanExpression(operand0, e.getOperator(), operand1);
|
||||
}
|
||||
|
||||
public Expression visit(Expression expr) {
|
||||
Expression result;
|
||||
if (expr instanceof DocumentSorter) {
|
||||
@ -81,6 +95,10 @@ abstract class Visitor {
|
||||
result = visit((QuantifiedExpression) expr);
|
||||
} else if (expr instanceof LetExpression) {
|
||||
result = visit((LetExpression) expr);
|
||||
} else if (expr instanceof LazyExpression) {
|
||||
result = visit((LazyExpression) expr);
|
||||
} else if (expr instanceof BooleanExpression) {
|
||||
result = visit((BooleanExpression) expr);
|
||||
} else {
|
||||
result = expr;
|
||||
}
|
@ -17,7 +17,7 @@ import net.sf.saxon.expr.VennExpression;
|
||||
*
|
||||
* <p>E.g. "//A | //B | //C" will result in 3 expressions "//A", "//B", and "//C".
|
||||
*/
|
||||
class SplitUnions extends Visitor {
|
||||
class SplitUnions extends SaxonExprVisitor {
|
||||
private List<Expression> expressions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
|
@ -19,11 +19,13 @@ import net.sourceforge.pmd.RuleViolation;
|
||||
import net.sourceforge.pmd.lang.ast.DummyNode;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.ast.ParseException;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.AbstractASTXPathHandler;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
|
||||
import net.sourceforge.pmd.lang.rule.AbstractRuleChainVisitor;
|
||||
import net.sourceforge.pmd.lang.rule.AbstractRuleViolationFactory;
|
||||
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
|
||||
|
||||
import net.sf.saxon.expr.XPathContext;
|
||||
import net.sf.saxon.sxpath.IndependentContext;
|
||||
|
||||
/**
|
||||
@ -67,11 +69,18 @@ public class DummyLanguageModule extends BaseLanguageModule {
|
||||
}
|
||||
|
||||
public static class Handler extends AbstractLanguageVersionHandler {
|
||||
public static class TestFunctions {
|
||||
public static boolean typeIs(final XPathContext context, final String fullTypeName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XPathHandler getXPathHandler() {
|
||||
return new XPathHandler() {
|
||||
return new AbstractASTXPathHandler() {
|
||||
@Override
|
||||
public void initialize(IndependentContext context) {
|
||||
super.initialize(context, LanguageRegistry.getLanguage(DummyLanguageModule.NAME), TestFunctions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,7 +18,6 @@ import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.properties.PropertyFactory;
|
||||
|
||||
import net.sf.saxon.expr.Expression;
|
||||
import net.sf.saxon.expr.XPathContext;
|
||||
|
||||
public class SaxonXPathRuleQueryTest {
|
||||
|
||||
@ -64,10 +63,42 @@ public class SaxonXPathRuleQueryTest {
|
||||
assertExpression("((((/)/descendant::element(dummyNode, xs:anyType))[QuantifiedExpression(Atomizer(attribute::attribute(Test2, xs:anyAtomicType)), ($qq:qq1741979653 singleton eq true()))])[QuantifiedExpression(Atomizer(attribute::attribute(Test1, xs:anyAtomicType)), ($qq:qq1529060733 singleton eq false()))])", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0));
|
||||
}
|
||||
|
||||
public static class TestFunctions {
|
||||
public static boolean typeIs(final XPathContext context, final String fullTypeName) {
|
||||
return false;
|
||||
}
|
||||
@Test
|
||||
public void ruleChainVisitsCustomFunctions() {
|
||||
SaxonXPathRuleQuery query = createQuery("//dummyNode[pmd-dummy:typeIs(@Image)]");
|
||||
List<String> ruleChainVisits = query.getRuleChainVisits();
|
||||
Assert.assertEquals(1, ruleChainVisits.size());
|
||||
Assert.assertTrue(ruleChainVisits.contains("dummyNode"));
|
||||
Assert.assertEquals(2, query.nodeNameToXPaths.size());
|
||||
assertExpression("(self::node()[pmd-dummy:typeIs(CardinalityChecker(ItemChecker(UntypedAtomicConverter(Atomizer(attribute::attribute(Image, xs:anyAtomicType))))))])", query.nodeNameToXPaths.get("dummyNode").get(0));
|
||||
assertExpression("DocumentSorter((((/)/descendant-or-self::node())/(child::element(dummyNode, xs:anyType)[pmd-dummy:typeIs(CardinalityChecker(ItemChecker(UntypedAtomicConverter(Atomizer(attribute::attribute(Image, xs:anyAtomicType))))))])))", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a query contains another unbounded path expression other than the first one, it must be
|
||||
* excluded from rule chain execution. Saxon itself optimizes this quite good already.
|
||||
*/
|
||||
@Test
|
||||
public void ruleChainVisitsUnboundedPathExpressions() {
|
||||
SaxonXPathRuleQuery query = createQuery("//dummyNode[//ClassOrInterfaceType]");
|
||||
List<String> ruleChainVisits = query.getRuleChainVisits();
|
||||
Assert.assertEquals(0, ruleChainVisits.size());
|
||||
Assert.assertEquals(1, query.nodeNameToXPaths.size());
|
||||
assertExpression("LetExpression(LazyExpression(((/)/descendant::element(ClassOrInterfaceType, xs:anyType))), (((/)/descendant::element(dummyNode, xs:anyType))[$zz:zz771775563]))", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0));
|
||||
|
||||
// second sample, more complex
|
||||
query = createQuery("//dummyNode[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType]]");
|
||||
ruleChainVisits = query.getRuleChainVisits();
|
||||
Assert.assertEquals(0, ruleChainVisits.size());
|
||||
Assert.assertEquals(1, query.nodeNameToXPaths.size());
|
||||
assertExpression("LetExpression(LazyExpression(((/)/descendant::element(ClassOrInterfaceType, xs:anyType))), (((/)/descendant::element(dummyNode, xs:anyType))[(ancestor::element(ClassOrInterfaceDeclaration, xs:anyType)[$zz:zz106374177])]))", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0));
|
||||
|
||||
// third example, with boolean expr
|
||||
query = createQuery("//dummyNode[//ClassOrInterfaceType or //OtherNode]");
|
||||
ruleChainVisits = query.getRuleChainVisits();
|
||||
Assert.assertEquals(0, ruleChainVisits.size());
|
||||
Assert.assertEquals(1, query.nodeNameToXPaths.size());
|
||||
assertExpression("LetExpression(LazyExpression((((/)/descendant::element(ClassOrInterfaceType, xs:anyType)) or ((/)/descendant::element(OtherNode, xs:anyType)))), (((/)/descendant::element(dummyNode, xs:anyType))[$zz:zz1364913072]))", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user