diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index f83648ab1e..1ae4fe9ed5 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -54,13 +54,6 @@ commons-io - - - net.sourceforge.saxon - saxon - - - junit junit diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index bff2176537..2fcb896080 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -126,8 +126,8 @@ javacc - net.sourceforge.saxon - saxon + net.sf.saxon + Saxon-HE org.apache.commons @@ -149,14 +149,6 @@ 2.5.2 - - - net.sourceforge.saxon - saxon - dom - runtime - - com.github.tomakehurst wiremock diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java index 99c192d018..4ae902b6cb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -20,7 +20,6 @@ import net.sourceforge.pmd.lang.Parser; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.xpath.Initializer; public class SourceCodeProcessor { @@ -80,9 +79,6 @@ public class SourceCodeProcessor { public void processSourceCode(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) throws PMDException { determineLanguage(ctx); - // make sure custom XPath functions are initialized - Initializer.initialize(); - // Coarse check to see if any RuleSet applies to file, will need to do a finer RuleSet specific check later if (ruleSets.applies(ctx.getSourceCodeFile())) { if (isCacheUpToDate(ctx)) { 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 fada9e28eb..be6317e076 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 @@ -9,7 +9,6 @@ import java.util.List; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.AstProcessingStage; -import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; @@ -30,7 +29,7 @@ public interface LanguageVersionHandler { * Get the XPathHandler. */ default XPathHandler getXPathHandler() { - return new DefaultASTXPathHandler(); + return XPathHandler.noFunctionDefinitions(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java index 064a65b215..71326d08b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java @@ -4,40 +4,33 @@ package net.sourceforge.pmd.lang; -import org.jaxen.Navigator; +import java.util.Collections; +import java.util.Set; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.xpath.Initializer; +import net.sourceforge.pmd.util.CollectionUtil; + +import net.sf.saxon.lib.ExtensionFunctionDefinition; -import net.sf.saxon.sxpath.IndependentContext; /** * Interface for performing Language specific XPath handling, such as * initialization and navigation. */ -@InternalApi -@Deprecated public interface XPathHandler { - /** - * Initialize. This is intended to be called by {@link Initializer} to - * perform Language specific initialization. - */ - void initialize(); + Set getRegisteredExtensionFunctions(); + + + static XPathHandler noFunctionDefinitions() { + return Collections::emptySet; + } + /** - * Initialize. This is intended to be called by {@link Initializer} to - * perform Language specific initialization for Saxon. + * Returns a default XPath handler. */ - void initialize(IndependentContext context); - - /** - * Get a Jaxen Navigator for this Language. May return null if - * there is no Jaxen Navigation for this language. - * - * @deprecated Support for Jaxen will be removed come 7.0.0. This isn't used - * anymore - */ - @Deprecated - Navigator getNavigator(); + static XPathHandler getHandlerForFunctionDefs(ExtensionFunctionDefinition first, ExtensionFunctionDefinition... defs) { + Set set = CollectionUtil.setOf(first, defs); + return () -> set; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java deleted file mode 100644 index eb470c94bd..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath; - -import org.jaxen.Navigator; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.XPathHandler; - -import net.sf.saxon.sxpath.IndependentContext; - - -@Deprecated -@InternalApi -public abstract class AbstractASTXPathHandler implements XPathHandler { - - @Override - public Navigator getNavigator() { - return new DocumentNavigator(); - } - - public void initialize(IndependentContext context, Language language, Class functionsClass) { - context.declareNamespace("pmd-" + language.getTerseName(), "java:" + functionsClass.getName()); - } - - @Override - public void initialize() { - // override if needed - } - - @Override - public void initialize(IndependentContext context) { - // override if needed - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java index 7f4573ce97..e1d5805330 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -50,6 +51,18 @@ public class Attribute { } + + /** + * Gets the generic type of the value of this attribute. + */ + public Type getType() { + return method == null ? String.class : method.getGenericReturnType(); + } + + public Class getErasedType() { + return method == null ? String.class : method.getReturnType(); + } + public String getName() { return name; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java deleted file mode 100644 index 3fa844f28a..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath; - -import net.sourceforge.pmd.annotation.InternalApi; - -import net.sf.saxon.sxpath.IndependentContext; - - -@Deprecated -@InternalApi -public class DefaultASTXPathHandler extends AbstractASTXPathHandler { - - @Override - public void initialize() { - // override if needed - } - - @Override - public void initialize(IndependentContext context) { - // override if needed - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DocumentNavigator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DocumentNavigator.java index b6fe8eef08..b33ffed931 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DocumentNavigator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DocumentNavigator.java @@ -180,7 +180,7 @@ public class DocumentNavigator extends DefaultNavigator { while (baseIterator.hasNext() && result == null) { Attribute candidate = baseIterator.next(); // Calling getValue() here would break laziness - if (!List.class.isAssignableFrom(candidate.getType())) { + if (!List.class.isAssignableFrom(candidate.getErasedType())) { result = candidate; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AbstractXPathFunctionDef.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AbstractXPathFunctionDef.java new file mode 100644 index 0000000000..fa1fb51d89 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AbstractXPathFunctionDef.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import net.sf.saxon.lib.ExtensionFunctionDefinition; +import net.sf.saxon.om.StructuredQName; + + +/** + * Base impl for an XPath function definition. + * + * @author Clément Fournier + * @since 7.0.0 + */ +public abstract class AbstractXPathFunctionDef extends ExtensionFunctionDefinition { + + private static final String PMD_URI_PREFIX = "http://pmd.sourceforge.net/"; + private final StructuredQName qname; + + protected AbstractXPathFunctionDef(String localName, String languageTerseName) { + String namespacePrefix = "pmd-" + languageTerseName; + String uri = PMD_URI_PREFIX + namespacePrefix; + this.qname = new StructuredQName(namespacePrefix, uri, localName); + } + + @Override + public final StructuredQName getFunctionQName() { + return qname; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstAttributeWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstAttributeWrapper.java new file mode 100644 index 0000000000..20411169ec --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstAttributeWrapper.java @@ -0,0 +1,120 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import net.sourceforge.pmd.lang.ast.xpath.Attribute; + +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.pattern.NodeTest; +import net.sf.saxon.tree.iter.AxisIterator; +import net.sf.saxon.tree.util.FastStringBuffer; +import net.sf.saxon.tree.wrapper.AbstractNodeWrapper; +import net.sf.saxon.type.SchemaType; +import net.sf.saxon.type.Type; + + +/** + * @author Clément Fournier + * @since 7.0.0 + */ +public class AstAttributeWrapper extends AbstractNodeWrapper { + + + private final AstNodeWrapper parent; + private final Attribute attribute; + private final SchemaType schemaType; + + + AstAttributeWrapper(AstNodeWrapper parent, Attribute attribute) { + this.parent = parent; + this.attribute = attribute; + this.schemaType = DomainConversion.buildType(attribute.getType()); + } + + + @Override + protected AxisIterator iterateAttributes(NodeTest nodeTest) { + return null; + } + + + @Override + protected AxisIterator iterateChildren(NodeTest nodeTest) { + return null; + } + + + @Override + protected AxisIterator iterateSiblings(NodeTest nodeTest, boolean forwards) { + return null; + } + + + @Override + protected AxisIterator iterateDescendants(NodeTest nodeTest, boolean includeSelf) { + return null; + } + + + @Override + public SchemaType getSchemaType() { + return schemaType; + } + + + @Override + public Attribute getUnderlyingNode() { + return attribute; + } + + + @Override + public int getNodeKind() { + return Type.ATTRIBUTE; + } + + + @Override + public int compareOrder(NodeInfo other) { + // attributes have no order in the xdm + return 0; + } + + + @Override + public String getLocalPart() { + return attribute.getName(); + } + + + @Override + public String getURI() { + return ""; + } + + + @Override + public String getPrefix() { + return ""; + } + + + @Override + public NodeInfo getParent() { + return parent; + } + + + @Override + public void generateId(FastStringBuffer buffer) { + buffer.append(Integer.toString(hashCode())); + } + + + @Override + public CharSequence getStringValueCS() { + return attribute.getStringValue(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstDocument.java new file mode 100644 index 0000000000..77d47b90c8 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstDocument.java @@ -0,0 +1,46 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import net.sourceforge.pmd.lang.ast.Node; + +import net.sf.saxon.Configuration; +import net.sf.saxon.om.GenericTreeInfo; + + +/** + * A wrapper around the root node of an AST, implementing {@link net.sf.saxon.om.TreeInfo}. + */ +public final class AstDocument extends GenericTreeInfo { + + private DeprecatedAttrLogger logger; + + /** + * Builds an AstDocument, with the given node as the root. + * + * @param node The root AST Node. + * @param configuration Configuration of the run + * + * @see AstNodeWrapper + */ + public AstDocument(Node node, Configuration configuration) { + super(configuration); + setRootNode(new AstNodeWrapper(this, new IdGenerator(), null, node)); + } + + + @Override + public AstNodeWrapper getRootNode() { + return (AstNodeWrapper) super.getRootNode(); + } + + public void setAttrCtx(DeprecatedAttrLogger attrCtx) { + this.logger = attrCtx; + } + + public DeprecatedAttrLogger getLogger() { + return logger == null ? DeprecatedAttrLogger.noop() : logger; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstNodeWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstNodeWrapper.java new file mode 100644 index 0000000000..b351fe450e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/AstNodeWrapper.java @@ -0,0 +1,280 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.Attribute; + +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.pattern.NodeTest; +import net.sf.saxon.tree.iter.AxisIterator; +import net.sf.saxon.tree.util.FastStringBuffer; +import net.sf.saxon.tree.util.Navigator.AxisFilter; +import net.sf.saxon.tree.wrapper.AbstractNodeWrapper; +import net.sf.saxon.type.Type; + + +/** + * A wrapper for Saxon around a Node. + */ +public final class AstNodeWrapper extends AbstractNodeWrapper { + + private final AstNodeWrapper parent; + private final Node wrappedNode; + private final int id; + + private final List children; + private final Map attributes; + + + public AstNodeWrapper(AstDocument document, + IdGenerator idGenerator, + AstNodeWrapper parent, + Node wrappedNode) { + this.treeInfo = document; + this.parent = parent; + this.wrappedNode = wrappedNode; + this.id = idGenerator.getNextId(); + + this.children = new ArrayList<>(wrappedNode.jjtGetNumChildren()); + + for (int i = 0; i < wrappedNode.jjtGetNumChildren(); i++) { + children.add(new AstNodeWrapper(document, idGenerator, this, wrappedNode.jjtGetChild(i))); + } + + Map atts = new HashMap<>(); + Iterator it = wrappedNode.getXPathAttributesIterator(); + + while (it.hasNext()) { + Attribute next = it.next(); + atts.put(next.getName(), new AstAttributeWrapper(this, next)); + } + + this.attributes = atts; + } + + + @Override + public AstDocument getTreeInfo() { + return (AstDocument) super.getTreeInfo(); + } + + + @Override + public Node getUnderlyingNode() { + return wrappedNode; + } + + + @Override + public int getColumnNumber() { + return wrappedNode.getBeginColumn(); + } + + + @Override + public int compareOrder(NodeInfo other) { + return Integer.compare(id, ((AstNodeWrapper) other).id); + } + + + private AxisIterator mapIterator(Iterator it, Function map, NodeTest nodeTest) { + AxisIterator axisIterator = new AxisIterator() { + @Override + public NodeInfo next() { + return it.hasNext() ? map.apply(it.next()) : null; + } + + + @Override + public void close() { + // nothing to do + } + + + @Override + public int getProperties() { + return 0; + } + }; + + return nodeTest != null ? new AxisFilter(axisIterator, nodeTest) : axisIterator; + } + + + @Override + protected AxisIterator iterateAttributes(NodeTest nodeTest) { + return mapIterator(attributes.values().iterator(), Function.identity(), nodeTest); + } + + + @Override + protected AxisIterator iterateChildren(NodeTest nodeTest) { + return mapIterator(children.iterator(), Function.identity(), nodeTest); + } + + + private AxisIterator empty() { + return new AxisIterator() { + @Override + public NodeInfo next() { + return null; + } + + + @Override + public void close() { + // nothing to do + } + + + @Override + public int getProperties() { + return 0; + } + }; + } + + + @Override + protected AxisIterator iterateSiblings(NodeTest nodeTest, boolean forwards) { + int startIdx = wrappedNode.getIndexInParent() + (forwards ? +1 : -1); + + if (parent == null) { + return empty(); + } + + AxisIterator siblings = new AxisIterator() { + + int curIdx = startIdx; + + + @Override + public NodeInfo next() { + if (!forwards && startIdx < 0 + || forwards && startIdx > parent.wrappedNode.jjtGetNumChildren()) { + return null; + } + + AstNodeWrapper next = parent.children.get(curIdx); + curIdx += forwards ? +1 : -1; + return next; + } + + + @Override + public void close() { + // nothing to do + } + + + @Override + public int getProperties() { + return 0; + } + }; + + return nodeTest != null ? new AxisFilter(siblings, nodeTest) : siblings; + } + + + private Stream streamDescendants() { + return Stream.concat(Stream.of(this), children.stream().flatMap(AstNodeWrapper::streamDescendants)); + } + + + @Override + public String getAttributeValue(String uri, String local) { + AstAttributeWrapper attributeWrapper = attributes.get(local); + + return attributeWrapper == null ? null : attributeWrapper.getStringValue(); + } + + + @Override + protected AxisIterator iterateDescendants(NodeTest nodeTest, boolean includeSelf) { + AxisIterator descendants = mapIterator(streamDescendants().iterator(), Function.identity(), null); + + if (!includeSelf) { + // skip one + descendants.next(); + } + + return nodeTest != null ? new AxisFilter(descendants, nodeTest) : descendants; + } + + + @Override + public int getLineNumber() { + return wrappedNode.getBeginLine(); + } + + + @Override + public int getNodeKind() { + return parent == null ? Type.DOCUMENT : Type.ELEMENT; + } + + + @Override + public NodeInfo getRoot() { + return getTreeInfo().getRootNode(); + } + + + @Override + public void generateId(FastStringBuffer buffer) { + buffer.append(Integer.toString(hashCode())); + } + + + public int getId() { + return id; + } + + + @Override + public String getLocalPart() { + return wrappedNode.getXPathNodeName(); + } + + + @Override + public String getURI() { + return ""; + } + + + @Override + public String getPrefix() { + return ""; + } + + + @Override + public NodeInfo getParent() { + return parent; + } + + + @Override + public CharSequence getStringValueCS() { + return getStringValue(); + } + + + @Override + public String toString() { + return "Wrapper[" + getLocalPart() + "]@" + hashCode(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DomainConversion.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DomainConversion.java new file mode 100644 index 0000000000..c87e0caa9f --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DomainConversion.java @@ -0,0 +1,89 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import java.util.regex.Pattern; + +import net.sf.saxon.type.BuiltInAtomicType; +import net.sf.saxon.type.SchemaType; +import net.sf.saxon.value.AtomicValue; +import net.sf.saxon.value.BigIntegerValue; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.DoubleValue; +import net.sf.saxon.value.FloatValue; +import net.sf.saxon.value.Int64Value; +import net.sf.saxon.value.StringValue; +import net.sf.saxon.value.UntypedAtomicValue; + + +/** + * @author Clément Fournier + * @since 7.0.0 + */ +public final class DomainConversion { + + private DomainConversion() { + + } + + + public static SchemaType buildType(java.lang.reflect.Type type) { + switch (type.getTypeName()) { + case "java.lang.Integer": + case "java.lang.Long": + return BuiltInAtomicType.INTEGER; + case "java.lang.Double": + case "java.lang.Float": + return BuiltInAtomicType.DOUBLE; + case "java.lang.String": + case "java.lang.Character": + case "java.lang.Class": + case "java.util.regex.Pattern": + return BuiltInAtomicType.STRING; + default: + return BuiltInAtomicType.UNTYPED_ATOMIC; + } + } + + + /** + * Gets the Saxon representation of the parameter, if its type corresponds + * to an XPath 2.0 atomic datatype. + * + * @param value The value to convert + * + * @return The converted AtomicValue + */ + public static AtomicValue getAtomicRepresentation(final Object value) { + + /* + FUTURE When supported, we should consider refactor this implementation to use Pattern Matching + (see http://openjdk.java.net/jeps/305) so that it looks clearer. + */ + if (value == null) { + return UntypedAtomicValue.ZERO_LENGTH_UNTYPED; + + } else if (value instanceof String) { + return new StringValue((String) value); + } else if (value instanceof Boolean) { + return BooleanValue.get((Boolean) value); + } else if (value instanceof Integer) { + return Int64Value.makeIntegerValue((Integer) value); + } else if (value instanceof Long) { + return new BigIntegerValue((Long) value); + } else if (value instanceof Double) { + return new DoubleValue((Double) value); + } else if (value instanceof Character) { + return new StringValue(value.toString()); + } else if (value instanceof Float) { + return new FloatValue((Float) value); + } else if (value instanceof Pattern) { + return new StringValue(String.valueOf(value)); + } else { + // We could maybe use UntypedAtomicValue + throw new RuntimeException("Unable to create ValueRepresentation for value of type: " + value.getClass()); + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/IdGenerator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/IdGenerator.java similarity index 51% rename from pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/IdGenerator.java rename to pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/IdGenerator.java index afb0886811..d04dff01bc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/IdGenerator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/IdGenerator.java @@ -1,21 +1,17 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import net.sourceforge.pmd.annotation.InternalApi; - +package net.sourceforge.pmd.lang.ast.xpath.internal; /** * This class is used to generate unique IDs for nodes. */ -@Deprecated -@InternalApi -public class IdGenerator { +class IdGenerator { private int id; - public int getNextId() { + + int getNextId() { return id++; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java deleted file mode 100644 index 822bb9d797..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java +++ /dev/null @@ -1,283 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import net.sourceforge.pmd.annotation.InternalApi; - -import net.sf.saxon.Configuration; -import net.sf.saxon.event.Receiver; -import net.sf.saxon.om.Axis; -import net.sf.saxon.om.AxisIterator; -import net.sf.saxon.om.DocumentInfo; -import net.sf.saxon.om.FastStringBuffer; -import net.sf.saxon.om.NamePool; -import net.sf.saxon.om.Navigator.AxisFilter; -import net.sf.saxon.om.NodeInfo; -import net.sf.saxon.om.SequenceIterator; -import net.sf.saxon.om.SiblingCountingNode; -import net.sf.saxon.om.VirtualNode; -import net.sf.saxon.pattern.NodeTest; -import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.Value; - -/** - * This is a basic implementation of the Saxon NodeInfo and related interfaces. - * Most methods are trivial implementations which immediately throw - * {@link UnsupportedOperationException}. A few of the methods actually have - * useful implementations, such as {@link #iterateAxis(byte, NodeTest)} and - * {@link #isSameNodeInfo(NodeInfo)}. - */ -@Deprecated -@InternalApi -public class AbstractNodeInfo implements VirtualNode, SiblingCountingNode { - @Override - public String getSystemId() { - throw createUnsupportedOperationException("Source.getSystemId()"); - } - - @Override - public void setSystemId(String systemId) { - throw createUnsupportedOperationException("Source.setSystemId(String)"); - } - - @Override - public String getStringValue() { - throw createUnsupportedOperationException("ValueRepresentation.getStringValue()"); - } - - @Override - public CharSequence getStringValueCS() { - throw createUnsupportedOperationException("ValueRepresentation.getStringValueCS()"); - } - - @Override - public SequenceIterator getTypedValue() throws XPathException { - throw createUnsupportedOperationException("Item.getTypedValue()"); - } - - @Override - public Object getUnderlyingNode() { - throw createUnsupportedOperationException("VirtualNode.getUnderlyingNode()"); - } - - @Override - public int getSiblingPosition() { - throw createUnsupportedOperationException("SiblingCountingNode.getSiblingPosition()"); - } - - @Override - public Value atomize() throws XPathException { - throw createUnsupportedOperationException("NodeInfo.atomize()"); - } - - @Override - public int compareOrder(NodeInfo other) { - throw createUnsupportedOperationException("NodeInfo.compareOrder(NodeInfo)"); - } - - @Override - public void copy(Receiver receiver, int whichNamespaces, boolean copyAnnotations, int locationId) - throws XPathException { - throw createUnsupportedOperationException("ValueRepresentation.copy(Receiver, int, boolean, int)"); - } - - /** - * This implementation considers to NodeInfo objects to be equal, if their - * underlying nodes are equal. - * - * {@inheritDoc} - */ - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other instanceof ElementNode) { - return this.getUnderlyingNode() == ((ElementNode) other).getUnderlyingNode(); - } - return false; - } - - @Override - public int hashCode() { - if (this.getUnderlyingNode() != null) { - return super.hashCode() + 31 * this.getUnderlyingNode().hashCode(); - } else { - return super.hashCode(); - } - } - - @Override - public void generateId(FastStringBuffer buffer) { - throw createUnsupportedOperationException("NodeInfo.generateId(FastStringBuffer)"); - } - - @Override - public String getAttributeValue(int fingerprint) { - throw createUnsupportedOperationException("NodeInfo.getAttributeValue(int)"); - } - - @Override - public String getBaseURI() { - throw createUnsupportedOperationException("NodeInfo.getBaseURI()"); - } - - @Override - public int getColumnNumber() { - throw createUnsupportedOperationException("NodeInfo.getColumnNumber()"); - } - - @Override - public Configuration getConfiguration() { - throw createUnsupportedOperationException("NodeInfo.getConfiguration()"); - } - - @Override - public int[] getDeclaredNamespaces(int[] buffer) { - throw createUnsupportedOperationException("NodeInfo.getDeclaredNamespaces(int[])"); - } - - @Override - public String getDisplayName() { - throw createUnsupportedOperationException("NodeInfo.getDisplayName()"); - } - - /** - * This implementation always returns 0. - * - * {@inheritDoc} - */ - @Override - public int getDocumentNumber() { - return 0; - } - - @Override - public DocumentInfo getDocumentRoot() { - throw createUnsupportedOperationException("NodeInfo.getDocumentRoot()"); - } - - @Override - public int getFingerprint() { - throw createUnsupportedOperationException("NodeInfo.getFingerprint()"); - } - - @Override - public int getLineNumber() { - throw createUnsupportedOperationException("NodeInfo.getLineNumber()"); - } - - @Override - public String getLocalPart() { - throw createUnsupportedOperationException("NodeInfo.getLocalPart()"); - } - - @Override - public int getNameCode() { - throw createUnsupportedOperationException("NodeInfo.getNameCode()"); - } - - @Override - public NamePool getNamePool() { - throw createUnsupportedOperationException("NodeInfo.getNamePool()"); - } - - @Override - public int getNodeKind() { - throw createUnsupportedOperationException("NodeInfo.getNodeKind()"); - } - - @Override - public NodeInfo getParent() { - throw createUnsupportedOperationException("NodeInfo.getParent()"); - } - - @Override - public String getPrefix() { - throw createUnsupportedOperationException("NodeInfo.getPrefix()"); - } - - @Override - public NodeInfo getRoot() { - throw createUnsupportedOperationException("NodeInfo.getRoot()"); - } - - @Override - public int getTypeAnnotation() { - throw createUnsupportedOperationException("NodeInfo.getTypeAnnotation()"); - } - - @Override - public String getURI() { - throw createUnsupportedOperationException("NodeInfo.getURI()"); - } - - @Override - public boolean hasChildNodes() { - throw createUnsupportedOperationException("NodeInfo.hasChildNodes()"); - } - - @Override - public boolean isId() { - throw createUnsupportedOperationException("NodeInfo.isId()"); - } - - @Override - public boolean isIdref() { - throw createUnsupportedOperationException("NodeInfo.isIdref()"); - } - - @Override - public boolean isNilled() { - throw createUnsupportedOperationException("NodeInfo.isNilled()"); - } - - /** - * This implementation delegates to {@link #equals(Object)}, per the Saxon - * documentation's description of this method's behavior. - * - * {@inheritDoc} - */ - @Override - public boolean isSameNodeInfo(NodeInfo other) { - return this.equals(other); - } - - @Override - public AxisIterator iterateAxis(byte axisNumber) { - throw createUnsupportedOperationException( - "NodeInfo.iterateAxis(byte) for axis '" + Axis.axisName[axisNumber] + "'"); - } - - /** - * This implementation calls {@link #iterateAxis(byte)} to get an - * {@link AxisIterator} which is then optionally filtered using - * {@link AxisFilter}. - * - * {@inheritDoc} - */ - @Override - public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) { - return filter(iterateAxis(axisNumber), nodeTest); - } - - protected static AxisIterator filter(AxisIterator axisIterator, NodeTest nodeTest) { - return nodeTest != null ? new AxisFilter(axisIterator, nodeTest) : axisIterator; - } - - /** - * Used to create a customized instance of UnsupportedOperationException. - * The caller of this method is intended to throw the - * exception. - * - * @param name - * Method name that is not supported. - * @return A UnsupportedOperationException indicated the method is not - * supported by the implementation class. - */ - protected UnsupportedOperationException createUnsupportedOperationException(String name) { - return new UnsupportedOperationException(name + " is not implemented by " + this.getClass().getName()); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java deleted file mode 100644 index d22adb33ab..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import java.util.Iterator; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.xpath.Attribute; - -import net.sf.saxon.om.Navigator; -import net.sf.saxon.om.SequenceIterator; - -/** - * An adapter over our {@link net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator} - * for the Saxon model. - */ -@Deprecated -@InternalApi -public class AttributeAxisIterator extends Navigator.BaseEnumeration { - - protected final ElementNode startNodeInfo; - protected final Iterator iterator; - - /** - * Create an iterator over the Attribute axis for the given ElementNode. - * - * @see net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator - */ - public AttributeAxisIterator(ElementNode startNodeInfo) { - this.startNodeInfo = startNodeInfo; - this.iterator = startNodeInfo.node.getXPathAttributesIterator(); - } - - @Override - public SequenceIterator getAnother() { - return new AttributeAxisIterator(startNodeInfo); - } - - @Override - public void advance() { - if (this.iterator.hasNext()) { - Attribute attribute = this.iterator.next(); - super.current = new AttributeNode(startNodeInfo, attribute, super.position()); - } else { - super.current = null; - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java deleted file mode 100644 index 7a919967ef..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.xpath.Attribute; -import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger; -import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; - -import net.sf.saxon.om.NodeInfo; -import net.sf.saxon.om.SequenceIterator; -import net.sf.saxon.trans.XPathException; -import net.sf.saxon.type.Type; -import net.sf.saxon.value.Value; - -/** - * A Saxon OM Attribute node for an AST Node Attribute. - * Belongs to an {@link ElementNode}, and wraps an - * {@link Attribute}. - */ -@Deprecated -@InternalApi -public class AttributeNode extends BaseNodeInfo { - - protected final Attribute attribute; - protected final int id; - protected Value value; - - - /** - * Creates a new AttributeNode from a PMD Attribute. - * - * @param parent Parent elemtn - * @param id The index within the attribute order - */ - public AttributeNode(ElementNode parent, Attribute attribute, int id) { - super(Type.ATTRIBUTE, parent.getNamePool(), attribute.getName(), parent); - this.attribute = attribute; - this.id = id; - } - - @Override - public String getLocalPart() { - return attribute.getName(); - } - - private DeprecatedAttrLogger getAttrCtx() { - return parent == null ? DeprecatedAttrLogger.noop() - : parent.document.getAttrCtx(); - } - - @Override - public Value atomize() { - getAttrCtx().recordUsageOf(attribute); - if (value == null) { - Object data = attribute.getValue(); - if (data instanceof List) { - value = SaxonXPathRuleQuery.getSequenceRepresentation((List) data); - } else { - value = SaxonXPathRuleQuery.getAtomicRepresentation(attribute.getValue()); - } - } - return value; - } - - @Override - public CharSequence getStringValueCS() { - return attribute.getStringValue(); - } - - @Override - public SequenceIterator getTypedValue() throws XPathException { - return atomize().iterate(); - } - - @Override - public int compareOrder(NodeInfo other) { - - return Integer.signum(this.id - ((AttributeNode) other).id); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java deleted file mode 100644 index ea6e4477ac..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - - -import net.sf.saxon.om.FingerprintedNode; -import net.sf.saxon.om.NamePool; -import net.sf.saxon.om.NodeInfo; -import net.sf.saxon.om.SiblingCountingNode; -import net.sf.saxon.om.VirtualNode; - -abstract class BaseNodeInfo extends AbstractNodeInfo implements VirtualNode, SiblingCountingNode, FingerprintedNode { - - // It's important that all our NodeInfo implementations share the - // same getNodeKind implementation, otherwise NameTest spends a lot - // of time in virtual dispatch - private final int nodeKind; - private final NamePool namePool; - private final int fingerprint; - - protected final ElementNode parent; - - BaseNodeInfo(int nodeKind, NamePool namePool, String localName, ElementNode parent) { - this.nodeKind = nodeKind; - this.namePool = namePool; - this.fingerprint = namePool.allocate("", "", localName) & NamePool.FP_MASK; - this.parent = parent; - } - - @Override - public final String getURI() { - return ""; - } - - @Override - public final String getBaseURI() { - return ""; - } - - @Override - public String getPrefix() { - return ""; - } - - @Override - public final NodeInfo getParent() { - return parent; - } - - @Override - public int getNameCode() { - // note: the nameCode is only equal to the fingerprint because - // this implementation does not use namespace prefixes - // if we change that (eg for embedded language support) then - // we'll need to worry about this. See NamePool.FP_MASK - return fingerprint; - } - - @Override - public final int getFingerprint() { - return fingerprint; - } - - @Override - public final NamePool getNamePool() { - return namePool; - } - - @Override - public final int getNodeKind() { - return nodeKind; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java deleted file mode 100644 index d91f1828a6..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger; -import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; - -import net.sf.saxon.om.Axis; -import net.sf.saxon.om.AxisIterator; -import net.sf.saxon.om.DocumentInfo; -import net.sf.saxon.om.NamePool; -import net.sf.saxon.om.Navigator; -import net.sf.saxon.om.NodeInfo; -import net.sf.saxon.om.SingleNodeIterator; -import net.sf.saxon.type.Type; - -/** - * A Saxon OM Document node for an AST Node. - */ -@Deprecated -@InternalApi -public class DocumentNode extends BaseNodeInfo implements DocumentInfo { - - /** - * The root ElementNode of the DocumentNode. - */ - protected final ElementNode rootNode; - - /** - * Mapping from AST Node to corresponding ElementNode. - */ - public final Map nodeToElementNode = new HashMap<>(); - - private DeprecatedAttrLogger attrCtx; - - /** - * Construct a DocumentNode, with the given AST Node serving as the root - * ElementNode. - * - * @param node The root AST Node. - * @param namePool Pool to share names - * - * @see ElementNode - */ - public DocumentNode(Node node, NamePool namePool) { - super(Type.DOCUMENT, namePool, "", null); - this.rootNode = new ElementNode(this, new IdGenerator(), null, node, -1, namePool); - } - - @Deprecated - public DocumentNode(Node node) { - this(node, SaxonXPathRuleQuery.getNamePool()); - } - - @Override - public String[] getUnparsedEntity(String name) { - throw createUnsupportedOperationException("DocumentInfo.getUnparsedEntity(String)"); - } - - @Override - public Iterator getUnparsedEntityNames() { - throw createUnsupportedOperationException("DocumentInfo.getUnparsedEntityNames()"); - } - - @Override - public NodeInfo selectID(String id) { - throw createUnsupportedOperationException("DocumentInfo.selectID(String)"); - } - - @Override - public DocumentInfo getDocumentRoot() { - return this; - } - - @Override - public boolean hasChildNodes() { - return true; - } - - @Override - public AxisIterator iterateAxis(byte axisNumber) { - switch (axisNumber) { - case Axis.DESCENDANT: - return new Navigator.DescendantEnumeration(this, false, true); - case Axis.DESCENDANT_OR_SELF: - return new Navigator.DescendantEnumeration(this, true, true); - case Axis.CHILD: - return SingleNodeIterator.makeIterator(rootNode); - default: - return super.iterateAxis(axisNumber); - } - } - - public DeprecatedAttrLogger getAttrCtx() { - return attrCtx == null ? DeprecatedAttrLogger.noop() : attrCtx; - } - - public void setAttrCtx(DeprecatedAttrLogger attrCtx) { - this.attrCtx = attrCtx; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java deleted file mode 100644 index 6db157eac4..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java +++ /dev/null @@ -1,267 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.xpath.saxon; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.xpath.Attribute; -import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; - -import net.sf.saxon.om.Axis; -import net.sf.saxon.om.AxisIterator; -import net.sf.saxon.om.DocumentInfo; -import net.sf.saxon.om.EmptyIterator; -import net.sf.saxon.om.NamePool; -import net.sf.saxon.om.Navigator; -import net.sf.saxon.om.Navigator.BaseEnumeration; -import net.sf.saxon.om.NodeArrayIterator; -import net.sf.saxon.om.NodeInfo; -import net.sf.saxon.om.SequenceIterator; -import net.sf.saxon.om.SingleNodeIterator; -import net.sf.saxon.om.SingletonIterator; -import net.sf.saxon.pattern.NameTest; -import net.sf.saxon.pattern.NodeTest; -import net.sf.saxon.type.Type; -import net.sf.saxon.value.AtomicValue; -import net.sf.saxon.value.StringValue; -import net.sf.saxon.value.UntypedAtomicValue; -import net.sf.saxon.value.Value; - -/** - * A Saxon OM Element type node for an AST Node. - */ -@Deprecated -@InternalApi -public class ElementNode extends BaseNodeInfo { - - protected final DocumentNode document; - protected final ElementNode parent; - protected final Node node; - protected final int id; - protected final int siblingPosition; - protected final NodeInfo[] children; - - private Map attributes; - - @Deprecated - public ElementNode(DocumentNode document, IdGenerator idGenerator, ElementNode parent, Node node, int siblingPosition) { - this(document, idGenerator, parent, node, siblingPosition, SaxonXPathRuleQuery.getNamePool()); - } - - public ElementNode(DocumentNode document, - IdGenerator idGenerator, - ElementNode parent, - Node node, - int siblingPosition, - NamePool namePool) { - super(Type.ELEMENT, namePool, node.getXPathNodeName(), parent); - - this.document = document; - this.parent = parent; - this.node = node; - this.id = idGenerator.getNextId(); - this.siblingPosition = siblingPosition; - - if (node.getNumChildren() > 0) { - this.children = new NodeInfo[node.getNumChildren()]; - for (int i = 0; i < children.length; i++) { - children[i] = new ElementNode(document, idGenerator, this, node.getChild(i), i, namePool); - } - } else { - this.children = null; - } - document.nodeToElementNode.put(node, this); - } - - private Map getAttributes() { - if (attributes == null) { - attributes = new HashMap<>(); - Iterator iter = node.getXPathAttributesIterator(); - int idx = 0; - while (iter.hasNext()) { - Attribute next = iter.next(); - AttributeNode attrNode = new AttributeNode(this, next, idx++); - attributes.put(attrNode.getFingerprint(), attrNode); - } - } - return attributes; - } - - @Override - public Object getUnderlyingNode() { - return node; - } - - @Override - public int getSiblingPosition() { - return siblingPosition; - } - - @Override - public int getColumnNumber() { - return node.getBeginColumn(); - } - - @Override - public int getLineNumber() { - return node.getBeginLine(); - } - - @Override - public boolean hasChildNodes() { - return children != null; - } - - @Override - public DocumentInfo getDocumentRoot() { - return document; - } - - @Override - public String getLocalPart() { - return node.getXPathNodeName(); - } - - - @Override - public SequenceIterator getTypedValue() { - return SingletonIterator.makeIterator((AtomicValue) atomize()); - } - - @Override - public Value atomize() { - switch (getNodeKind()) { - case Type.COMMENT: - case Type.PROCESSING_INSTRUCTION: - return new StringValue(getStringValueCS()); - default: - return new UntypedAtomicValue(getStringValueCS()); - } - } - - @Override - public CharSequence getStringValueCS() { - return ""; - } - - @Override - public int compareOrder(NodeInfo other) { - int result; - if (this.isSameNodeInfo(other)) { - result = 0; - } else { - result = Integer.signum(this.getLineNumber() - other.getLineNumber()); - if (result == 0) { - result = Integer.signum(this.getColumnNumber() - other.getColumnNumber()); - } - if (result == 0) { - if (this.getParent().equals(other.getParent())) { - result = Integer.signum(this.getSiblingPosition() - ((ElementNode) other).getSiblingPosition()); - } else { - // we must not return 0 here, otherwise the node might be removed as duplicate when creating - // a union set. The the nodes are definitively different nodes (isSameNodeInfo == false). - result = 1; - } - } - } - return result; - } - - - @Override - public String getDisplayName() { - return getLocalPart(); - } - - - @Override - public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) { - if (axisNumber == Axis.ATTRIBUTE) { - if (nodeTest instanceof NameTest) { - if ((nodeTest.getNodeKindMask() & (1 << Type.ATTRIBUTE)) == 0) { - return EmptyIterator.getInstance(); - } else { - int fp = nodeTest.getFingerprint(); - if (fp != -1) { - return SingleNodeIterator.makeIterator(getAttributes().get(fp)); - } - } - } - } - return super.iterateAxis(axisNumber, nodeTest); - } - - @SuppressWarnings("PMD.MissingBreakInSwitch") - @Override - public AxisIterator iterateAxis(final byte axisNumber) { - switch (axisNumber) { - case Axis.ANCESTOR: - return new Navigator.AncestorEnumeration(this, false); - case Axis.ANCESTOR_OR_SELF: - return new Navigator.AncestorEnumeration(this, true); - case Axis.ATTRIBUTE: - return new AttributeEnumeration(); - case Axis.CHILD: - if (children == null) { - return EmptyIterator.getInstance(); - } else { - return new NodeArrayIterator(children); - } - case Axis.DESCENDANT: - return new Navigator.DescendantEnumeration(this, false, true); - case Axis.DESCENDANT_OR_SELF: - return new Navigator.DescendantEnumeration(this, true, true); - case Axis.FOLLOWING: - return new Navigator.FollowingEnumeration(this); - case Axis.FOLLOWING_SIBLING: - if (parent == null || siblingPosition == parent.children.length - 1) { - return EmptyIterator.getInstance(); - } else { - return new NodeArrayIterator(parent.children, siblingPosition + 1, parent.children.length); - } - case Axis.NAMESPACE: - return super.iterateAxis(axisNumber); - case Axis.PARENT: - return SingleNodeIterator.makeIterator(parent); - case Axis.PRECEDING: - return new Navigator.PrecedingEnumeration(this, false); - case Axis.PRECEDING_SIBLING: - if (parent == null || siblingPosition == 0) { - return EmptyIterator.getInstance(); - } else { - return new NodeArrayIterator(parent.children, 0, siblingPosition); - } - case Axis.SELF: - return SingleNodeIterator.makeIterator(this); - case Axis.PRECEDING_OR_ANCESTOR: - return new Navigator.PrecedingEnumeration(this, true); - default: - return super.iterateAxis(axisNumber); - } - } - - private class AttributeEnumeration extends BaseEnumeration { - - private final Iterator iter = getAttributes().values().iterator(); - - @Override - public void advance() { - if (iter.hasNext()) { - current = iter.next(); - } else { - current = null; - } - } - - @Override - public SequenceIterator getAnother() { - return new AttributeEnumeration(); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java index 199d37e363..0c5c019b66 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java @@ -4,10 +4,6 @@ package net.sourceforge.pmd.lang.rule; -import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_1_0; -import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_1_0_COMPATIBILITY; -import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_2_0; - import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -21,13 +17,12 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.AstProcessingStage; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger; -import net.sourceforge.pmd.lang.rule.xpath.JaxenXPathRuleQuery; import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; -import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; import net.sourceforge.pmd.properties.EnumeratedProperty; import net.sourceforge.pmd.properties.StringProperty; + /** * Rule that tries to match an XPath expression against a DOM view of an AST. */ @@ -43,33 +38,22 @@ public class XPathRule extends AbstractRule { .uiOrder(1.0f) .build(); - private static final Map XPATH_VERSIONS; - - static { - Map tmp = new HashMap<>(); - tmp.put(XPATH_1_0, XPATH_1_0); - tmp.put(XPATH_1_0_COMPATIBILITY, XPATH_1_0_COMPATIBILITY); - tmp.put(XPATH_2_0, XPATH_2_0); - XPATH_VERSIONS = Collections.unmodifiableMap(tmp); - } - - /** * @deprecated Use {@link #XPathRule(XPathVersion, String)} */ @Deprecated public static final EnumeratedProperty VERSION_DESCRIPTOR = EnumeratedProperty.named("version") .desc("XPath specification version") - .mappings(XPATH_VERSIONS) - .defaultValue(XPATH_1_0) - .type(String.class) + .mappings(getXPathVersions()) + .defaultValue(XPathVersion.XPATH_2_0) + .type(XPathVersion.class) .uiOrder(2.0f) .build(); - /** * This is initialized only once when calling {@link #evaluate(Node, RuleContext)} or {@link #getRuleChainVisits()}. */ - private XPathRuleQuery xpathRuleQuery; + private SaxonXPathRuleQuery xpathRuleQuery; + // this is shared with rules forked by deepCopy, used by the XPathRuleQuery private DeprecatedAttrLogger attrLogger = DeprecatedAttrLogger.create(this); @@ -84,6 +68,7 @@ public class XPathRule extends AbstractRule { definePropertyDescriptor(VERSION_DESCRIPTOR); } + /** * Creates a new XPathRule and associates the XPath query. * @@ -94,6 +79,7 @@ public class XPathRule extends AbstractRule { setXPath(xPath); } + /** * Make a new XPath rule with the given version + expression * @@ -148,9 +134,10 @@ public class XPathRule extends AbstractRule { */ @Deprecated public void setVersion(final String version) { - setProperty(XPathRule.VERSION_DESCRIPTOR, version); + setProperty(XPathRule.VERSION_DESCRIPTOR, XPathVersion.fromString(version)); } + @Override public void apply(List nodes, RuleContext ctx) { for (Node node : nodes) { @@ -158,6 +145,7 @@ public class XPathRule extends AbstractRule { } } + /** * Evaluate the XPath query with the AST node. All matches are reported as violations. * @@ -178,6 +166,7 @@ public class XPathRule extends AbstractRule { } } + /** * Initializes {@link #xpathRuleQuery} iff {@link #xPathRuleQueryNeedsInitialization()} is true. To select the * engine in which the query will be run it looks at the XPath version. @@ -190,17 +179,14 @@ public class XPathRule extends AbstractRule { throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason"); } - if (version == XPathVersion.XPATH_1_0) { - xpathRuleQuery = new JaxenXPathRuleQuery(attrLogger); - } else { - xpathRuleQuery = new SaxonXPathRuleQuery(attrLogger); - } - - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setVersion(version.getXmlName()); - xpathRuleQuery.setProperties(getPropertiesByPropertyDescriptor()); + xpathRuleQuery = new SaxonXPathRuleQuery(xpath, + version, + getPropertiesByPropertyDescriptor(), + getLanguage().getDefaultVersion().getLanguageVersionHandler().getXPathHandler(), + attrLogger); } + /** * Checks if the {@link #xpathRuleQuery} is null and therefore requires initialization. * @@ -222,6 +208,7 @@ public class XPathRule extends AbstractRule { return super.getRuleChainVisits(); } + @Override public String dysfunctionReason() { if (getVersion() == null) { @@ -232,10 +219,17 @@ public class XPathRule extends AbstractRule { return null; } - @Override public boolean dependsOn(AstProcessingStage stage) { // FIXME must be made language-specific return true; } + + private static Map getXPathVersions() { + Map tmp = new HashMap<>(); + for (XPathVersion v : XPathVersion.values()) { + tmp.put(v.getXmlName(), v.getXmlName()); + } + return Collections.unmodifiableMap(tmp); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java deleted file mode 100644 index 5647fb9ec8..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.rule.xpath; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -/** - * This implementation of XPathRuleQuery provides support for RuleChain visits. - * - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractXPathRuleQuery implements XPathRuleQuery { - - /** - * The XPath query string. - */ - protected String xpath; - - /** - * The XPath version; - */ - protected String version; - - /** - * The properties. - */ - protected Map, Object> properties; - - /** - * Subclasses can manage RuleChain visits via this list. - */ - protected final List ruleChainVisits = new ArrayList<>(); - - @Override - public void setXPath(final String xpath) { - this.xpath = xpath; - } - - @Override - public void setVersion(String version) throws UnsupportedOperationException { - if (!isSupportedVersion(version)) { - throw new UnsupportedOperationException( - this.getClass().getSimpleName() + " does not support XPath version: " + version); - } - this.version = version; - } - - /** - * Subclasses should implement to indicate whether an XPath version is - * supported. - * - * @param version - * The XPath version. - * @return true if the XPath version is supported, - * false otherwise. - */ - protected abstract boolean isSupportedVersion(String version); - - @Override - public void setProperties(Map, Object> properties) { - this.properties = properties; - } - - @Override - public List getRuleChainVisits() { - return ruleChainVisits; - } - - @Override - public abstract List evaluate(Node node, RuleContext data); -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java deleted file mode 100644 index 4ef992f19d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java +++ /dev/null @@ -1,282 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.rule.xpath; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jaxen.BaseXPath; -import org.jaxen.JaxenException; -import org.jaxen.Navigator; -import org.jaxen.SimpleVariableContext; -import org.jaxen.XPath; -import org.jaxen.expr.AllNodeStep; -import org.jaxen.expr.DefaultXPathFactory; -import org.jaxen.expr.Expr; -import org.jaxen.expr.LocationPath; -import org.jaxen.expr.NameStep; -import org.jaxen.expr.Predicate; -import org.jaxen.expr.Step; -import org.jaxen.expr.UnionExpr; -import org.jaxen.expr.XPathFactory; -import org.jaxen.saxpath.Axis; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.xpath.internal.ContextualizedNavigator; -import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -/** - * This is a Jaxen based XPathRule query. - * - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public class JaxenXPathRuleQuery extends AbstractXPathRuleQuery { - - private static final Logger LOG = Logger.getLogger(JaxenXPathRuleQuery.class.getName()); - - static final String AST_ROOT = "_AST_ROOT_"; - - private InitializationStatus initializationStatus = InitializationStatus.NONE; - // Mapping from Node name to applicable XPath queries - Map> nodeNameToXPaths; - - private final DeprecatedAttrLogger attrCtx; - - public JaxenXPathRuleQuery() { - this(DeprecatedAttrLogger.noop()); - } - - public JaxenXPathRuleQuery(DeprecatedAttrLogger attrCtx) { - this.attrCtx = attrCtx; - } - - @Override - public boolean isSupportedVersion(String version) { - return XPATH_1_0.equals(version); - } - - @Override - public List evaluate(final Node node, final RuleContext data) { - final List results = new ArrayList<>(); - - try { - initializeExpressionIfStatusIsNoneOrPartial(new ContextualizedNavigator(attrCtx)); - - List xPaths = getXPathsForNodeOrDefault(node.getXPathNodeName()); - for (XPath xpath : xPaths) { - @SuppressWarnings("unchecked") - final List matchedNodes = xpath.selectNodes(node); - results.addAll(matchedNodes); - } - } catch (final JaxenException e) { - throw new RuntimeException(e); - } - return results; - } - - /** - * Get the XPath queries associated with the node name. If there are none, the XPath queries for the {@link #AST_ROOT} - * are obtained. - * - * @param nodeName the id of the node - * @return the list of XPath queries that match the node name - */ - private List getXPathsForNodeOrDefault(final String nodeName) { - List xPaths = nodeNameToXPaths.get(nodeName); - if (xPaths == null) { - xPaths = nodeNameToXPaths.get(AST_ROOT); - } - return xPaths; - } - - @Override - public List getRuleChainVisits() { - try { - // No Navigator available in this context - initializeExpressionIfStatusIsNoneOrPartial(null); - return super.getRuleChainVisits(); - } catch (final JaxenException ex) { - throw new RuntimeException(ex); - } - } - - /** - * - * @param navigator the navigator which is required to be non-null if the {@link #initializationStatus} is PARTIAL. - * @throws JaxenException - */ - @SuppressWarnings("unchecked") - private void initializeExpressionIfStatusIsNoneOrPartial(final Navigator navigator) throws JaxenException { - if (initializationStatus == InitializationStatus.FULL) { - return; - } - if (initializationStatus == InitializationStatus.PARTIAL && navigator == null) { - LOG.severe("XPathRule is not initialized because no navigator was provided. " - + "Please make sure to implement getXPathHandler in the handler of the language. " - + "See also AbstractLanguageVersionHandler."); - return; - } - initializeXPathExpression(navigator); - } - - private void initializeXPathExpression(final Navigator navigator) throws JaxenException { - /* - Attempt to use the RuleChain with this XPath query. - - To do so, the queries should generally look like //TypeA or //TypeA | //TypeB. We will look at the parsed XPath - AST using the Jaxen APIs to make this determination. - - If the query is not exactly what we are looking for, do not use the - RuleChain. - */ - nodeNameToXPaths = new HashMap<>(); - - final BaseXPath originalXPath = createXPath(xpath, navigator); - addQueryToNode(originalXPath, AST_ROOT); - - boolean useRuleChain = true; - final Deque pending = new ArrayDeque<>(); - pending.push(originalXPath.getRootExpr()); - while (!pending.isEmpty()) { - final Expr node = pending.pop(); - - // Need to prove we can handle this part of the query - boolean valid = false; - - // Must be a LocationPath... that is something like //Type - if (node instanceof LocationPath) { - final LocationPath locationPath = (LocationPath) node; - if (locationPath.isAbsolute()) { - // Should be at least two steps - @SuppressWarnings("unchecked") - final List steps = locationPath.getSteps(); - - if (steps.size() >= 2) { - final Step step1 = steps.get(0); - final Step step2 = steps.get(1); - // First step should be an AllNodeStep using the - // descendant or self axis - if (step1 instanceof AllNodeStep - && step1.getAxis() == Axis.DESCENDANT_OR_SELF) { - // Second step should be a NameStep using the child - // axis. - if (step2 instanceof NameStep && step2.getAxis() == Axis.CHILD) { - // Construct a new expression that is - // appropriate for RuleChain use - final XPathFactory xpathFactory = new DefaultXPathFactory(); - - // Instead of an absolute location path, we'll - // be using a relative path - final LocationPath relativeLocationPath = xpathFactory.createRelativeLocationPath(); - // The first step will be along the self axis - final Step allNodeStep = xpathFactory.createAllNodeStep(Axis.SELF); - // Retain all predicates from the original name - // step - @SuppressWarnings("unchecked") - final List predicates = step2.getPredicates(); - - for (Predicate predicate : predicates) { - allNodeStep.addPredicate(predicate); - } - relativeLocationPath.addStep(allNodeStep); - - // Retain the remaining steps from the original - // location path - for (int i = 2; i < steps.size(); i++) { - relativeLocationPath.addStep(steps.get(i)); - } - - final BaseXPath xpath = createXPath(relativeLocationPath.getText(), navigator); - addQueryToNode(xpath, ((NameStep) step2).getLocalName()); - valid = true; - } - } - } - } - } else if (node instanceof UnionExpr) { // Or a UnionExpr, that is - // something like //TypeA | - // //TypeB - UnionExpr unionExpr = (UnionExpr) node; - pending.push(unionExpr.getLHS()); - pending.push(unionExpr.getRHS()); - valid = true; - } - if (!valid) { - useRuleChain = false; - break; - } - } - - if (useRuleChain) { - // Use the RuleChain for all the nodes extracted from the xpath - // queries - super.ruleChainVisits.addAll(nodeNameToXPaths.keySet()); - } else { - // Use original XPath if we cannot use the RuleChain - nodeNameToXPaths.clear(); - addQueryToNode(originalXPath, AST_ROOT); - if (LOG.isLoggable(Level.FINE)) { - LOG.log(Level.FINE, "Unable to use RuleChain for XPath: " + xpath); - } - } - - if (navigator == null) { - this.initializationStatus = InitializationStatus.PARTIAL; - // Clear the node data, because we did not have a Navigator - nodeNameToXPaths = null; - } else { - this.initializationStatus = InitializationStatus.FULL; - } - } - - /** - * Relates an XPath query to a node by adding the query to the {@link #nodeNameToXPaths}. - * - * @param xPath the query to do over a node - * @param nodeName the node on which to do the query - */ - private void addQueryToNode(final XPath xPath, final String nodeName) { - List xPathsForNode = nodeNameToXPaths.get(nodeName); - if (xPathsForNode == null) { - xPathsForNode = new ArrayList<>(); - nodeNameToXPaths.put(nodeName, xPathsForNode); - } - xPathsForNode.add(xPath); - } - - private BaseXPath createXPath(final String xpathQueryString, final Navigator navigator) throws JaxenException { - final BaseXPath xpath = new BaseXPath(xpathQueryString, navigator); - - if (properties.size() > 1) { - final SimpleVariableContext vc = new SimpleVariableContext(); - for (Entry, Object> e : properties.entrySet()) { - final String propName = e.getKey().name(); - if (!"xpath".equals(propName)) { - final Object value = e.getValue(); - vc.setVariableValue(propName, value != null ? value.toString() : null); - } - } - xpath.setVariableContext(vc); - } - return xpath; - } - - - private enum InitializationStatus { - NONE, PARTIAL, FULL - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java index 4bcb8f7423..37097d39ae 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.rule.xpath; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -16,25 +15,25 @@ import java.util.regex.Pattern; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.XPathHandler; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger; -import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode; -import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstDocument; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstNodeWrapper; +import net.sourceforge.pmd.lang.ast.xpath.internal.DomainConversion; +import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.internal.RuleChainAnalyzer; -import net.sourceforge.pmd.lang.xpath.Initializer; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.util.DataMap; import net.sourceforge.pmd.util.DataMap.DataKey; import net.sourceforge.pmd.util.DataMap.SimpleDataKey; +import net.sf.saxon.Configuration; import net.sf.saxon.expr.Expression; import net.sf.saxon.om.Item; import net.sf.saxon.om.NamePool; -import net.sf.saxon.om.NamespaceConstant; +import net.sf.saxon.om.Sequence; import net.sf.saxon.om.SequenceIterator; -import net.sf.saxon.om.ValueRepresentation; -import net.sf.saxon.sxpath.AbstractStaticContext; -import net.sf.saxon.sxpath.IndependentContext; import net.sf.saxon.sxpath.XPathDynamicContext; import net.sf.saxon.sxpath.XPathEvaluator; import net.sf.saxon.sxpath.XPathExpression; @@ -51,7 +50,7 @@ import net.sf.saxon.value.Int64Value; import net.sf.saxon.value.SequenceExtent; import net.sf.saxon.value.StringValue; import net.sf.saxon.value.UntypedAtomicValue; -import net.sf.saxon.value.Value; + /** * This is a Saxon based XPathRule query. @@ -60,8 +59,7 @@ import net.sf.saxon.value.Value; */ @Deprecated @InternalApi -public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { - +public class SaxonXPathRuleQuery { /** * Special nodeName that references the root expression. */ @@ -74,59 +72,77 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { /** Cache key for the wrapped tree for saxon. */ private static final SimpleDataKey SAXON_TREE_CACHE_KEY = DataMap.simpleDataKey("saxon.tree"); + private final String xpathExpr; + private final XPathVersion version; + private final Map, Object> properties; + private final XPathHandler xPathHandler; + private final List rulechainQueries = new ArrayList<>(); + private Configuration configuration; + /** * Contains for each nodeName a sub expression, used for implementing rule chain. */ Map> nodeNameToXPaths = new HashMap<>(); /** - * Representation of an XPath query, created at {@link #initializeXPathExpression()} using {@link #xpath}. + * Representation of an XPath query, created at {@link #initializeXPathExpression()} using {@link #xpathExpr}. */ XPathExpression xpathExpression; /** * Holds the static context later used to match the variables in the dynamic context in - * {@link #createDynamicContext(ElementNode)}. Created at {@link #initializeXPathExpression()} + * {@link #createDynamicContext(AstNodeWrapper)}. Created at {@link #initializeXPathExpression()} * using the properties descriptors in {@link #properties}. */ private List xpathVariables; private final DeprecatedAttrLogger attrCtx; - @Deprecated - public SaxonXPathRuleQuery() { - this(DeprecatedAttrLogger.noop()); + + public SaxonXPathRuleQuery(String xpathExpr, + XPathVersion version, + Map, Object> properties, + XPathHandler xPathHandler, + DeprecatedAttrLogger logger) { + this.xpathExpr = xpathExpr; + this.version = version; + this.properties = properties; + this.xPathHandler = xPathHandler; + this.attrCtx = logger; } - public SaxonXPathRuleQuery(DeprecatedAttrLogger attrCtx) { - this.attrCtx = attrCtx; + + public String getXpathExpression() { + return xpathExpr; } - @Override - public boolean isSupportedVersion(String version) { - return XPATH_1_0_COMPATIBILITY.equals(version) || XPATH_2_0.equals(version); + + public List getRuleChainVisits() { + initializeXPathExpression(); + return rulechainQueries; } - @Override + + @SuppressWarnings("unchecked") public List evaluate(final Node node, final RuleContext data) { initializeXPathExpression(); try { - final DocumentNode documentNode = getDocumentNodeForRootNode(node); + final AstDocument documentNode = getDocumentNodeForRootNode(node); documentNode.setAttrCtx(attrCtx); // // Map AST Node -> Saxon Node - final ElementNode rootElementNode = documentNode.nodeToElementNode.get(node); + final AstNodeWrapper rootElementNode = documentNode.getRootNode(); assert rootElementNode != null : "Cannot find " + node; final XPathDynamicContext xpathDynamicContext = createDynamicContext(rootElementNode); - final List nodes = new LinkedList<>(); + final List nodes = new ArrayList<>(); List expressions = getXPathExpressionForNodeOrDefault(node.getXPathNodeName()); for (Expression expression : expressions) { SequenceIterator iterator = expression.iterate(xpathDynamicContext.getXPathContextObject()); Item current = iterator.next(); - while (current != null) { - nodes.add((ElementNode) current); + while (current instanceof AstNodeWrapper) { + nodes.add((AstNodeWrapper) current); current = iterator.next(); } } @@ -136,13 +152,13 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { (i.e. violation found) */ final List results = new ArrayList<>(nodes.size()); - for (final ElementNode elementNode : nodes) { - results.add((Node) elementNode.getUnderlyingNode()); + for (final AstNodeWrapper elementNode : nodes) { + results.add(elementNode.getUnderlyingNode()); } - Collections.sort(results, RuleChainAnalyzer.documentOrderComparator()); + results.sort(RuleChainAnalyzer.documentOrderComparator()); return results; } catch (final XPathException e) { - throw new RuntimeException(super.xpath + " had problem: " + e.getMessage(), e); + throw new RuntimeException(xpathExpr + " had problem: " + e.getMessage(), e); } } @@ -158,20 +174,22 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { * * @param elementNode the node on which to create the context; generally this node is the root node of the Saxon * Tree + * * @return the dynamic context on which to run the query + * * @throws XPathException if the supplied value does not conform to the required type of the - * variable, when setting up the dynamic context; or if the supplied value contains a node that does not belong to - * this Configuration (or another Configuration that shares the same namePool) + * variable, when setting up the dynamic context; or if the supplied value contains a node that does not belong to + * this Configuration (or another Configuration that shares the same namePool) */ - private XPathDynamicContext createDynamicContext(final ElementNode elementNode) throws XPathException { + private XPathDynamicContext createDynamicContext(final AstNodeWrapper elementNode) throws XPathException { final XPathDynamicContext dynamicContext = xpathExpression.createDynamicContext(elementNode); // Set variable values on the dynamic context for (final XPathVariable xpathVariable : xpathVariables) { - final String variableName = xpathVariable.getVariableQName().getLocalName(); - for (final Map.Entry, Object> entry : super.properties.entrySet()) { + final String variableName = xpathVariable.getVariableQName().getLocalPart(); + for (final Map.Entry, Object> entry : properties.entrySet()) { if (variableName.equals(entry.getKey().name())) { - final ValueRepresentation valueRepresentation = getRepresentation(entry.getKey(), entry.getValue()); + final Sequence valueRepresentation = getRepresentation(entry.getKey(), entry.getValue()); dynamicContext.setVariable(xpathVariable, valueRepresentation); } } @@ -180,38 +198,42 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { } - private ValueRepresentation getRepresentation(final PropertyDescriptor descriptor, final Object value) { + private Sequence getRepresentation(final PropertyDescriptor descriptor, final Object value) { if (descriptor.isMultiValue()) { return getSequenceRepresentation((List) value); } else { - return getAtomicRepresentation(value); + return DomainConversion.getAtomicRepresentation(value); } } + /** * Gets the DocumentNode representation for the whole AST in which the node is, that is, if the node is not the root * of the AST, then the AST is traversed all the way up until the root node is found. If the DocumentNode was * cached because this method was previously called, then a new DocumentNode will not be instanced. * * @param node the node from which the root node will be looked for. + * * @return the DocumentNode representing the whole AST */ - private DocumentNode getDocumentNodeForRootNode(final Node node) { + private AstDocument getDocumentNodeForRootNode(final Node node) { final Node root = getRootNode(node); DataMap> userMap = root.getUserMap(); - DocumentNode docNode = userMap.get(SAXON_TREE_CACHE_KEY); + AstDocument docNode = userMap.get(SAXON_TREE_CACHE_KEY); if (docNode == null) { - docNode = new DocumentNode(root, getNamePool()); + docNode = new AstDocument(root, configuration); userMap.set(SAXON_TREE_CACHE_KEY, docNode); } return docNode; } + /** * Traverse the AST until the root node is found. * * @param node the node from where to start traversing the tree + * * @return the root node */ private Node getRootNode(final Node node) { @@ -241,15 +263,13 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { final XPathStaticContext xpathStaticContext = xpathEvaluator.getStaticContext(); xpathStaticContext.getConfiguration().setNamePool(getNamePool()); - // Enable XPath 1.0 compatibility - if (XPATH_1_0_COMPATIBILITY.equals(version)) { - ((AbstractStaticContext) xpathStaticContext).setBackwardsCompatibilityMode(true); - } + this.configuration = xpathStaticContext.getConfiguration(); - ((IndependentContext) xpathStaticContext).declareNamespace("fn", NamespaceConstant.FN); - // Register PMD functions - Initializer.initialize((IndependentContext) xpathStaticContext); + // TODO ((IndependentContext) xpathStaticContext).declareNamespace(); + + xPathHandler.getRegisteredExtensionFunctions().forEach(configuration::registerExtensionFunction); + /* Create XPathVariables for later use. It is a Saxon quirk that XPathVariables must be defined on the @@ -257,21 +277,21 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { createDynamicContext(ElementNode). */ xpathVariables = new ArrayList<>(); - for (final PropertyDescriptor propertyDescriptor : super.properties.keySet()) { + for (final PropertyDescriptor propertyDescriptor : properties.keySet()) { final String name = propertyDescriptor.name(); - if (!"xpath".equals(name)) { + if (!"xpath".equals(name) && !XPathRule.VERSION_DESCRIPTOR.name().equals(name)) { final XPathVariable xpathVariable = xpathStaticContext.declareVariable(null, name); xpathVariables.add(xpathVariable); } } - xpathExpression = xpathEvaluator.createExpression(super.xpath); + xpathExpression = xpathEvaluator.createExpression(xpathExpr); analyzeXPathForRuleChain(xpathEvaluator); } catch (final XPathException e) { throw new RuntimeException(e); } } - + private void analyzeXPathForRuleChain(final XPathEvaluator xpathEvaluator) { final Expression expr = xpathExpression.getInternalExpression(); @@ -296,11 +316,11 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { } if (useRuleChain) { - super.ruleChainVisits.addAll(nodeNameToXPaths.keySet()); + rulechainQueries.addAll(nodeNameToXPaths.keySet()); } else { nodeNameToXPaths.clear(); if (LOG.isLoggable(Level.FINE)) { - LOG.log(Level.FINE, "Unable to use RuleChain for XPath: " + xpath); + LOG.log(Level.FINE, "Unable to use RuleChain for XPath: " + xpathExpr); } } @@ -349,7 +369,7 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { } } - public static Value getSequenceRepresentation(List list) { + public static Sequence getSequenceRepresentation(List list) { if (list == null || list.isEmpty()) { return EmptySequence.getInstance(); } @@ -360,12 +380,6 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { return new SequenceExtent(converted); } - @Override - public List getRuleChainVisits() { - initializeXPathExpression(); - return super.getRuleChainVisits(); - } - public static NamePool getNamePool() { return NAME_POOL; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/Initializer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/Initializer.java deleted file mode 100644 index c73ef828a7..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/Initializer.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xpath; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionHandler; - -import net.sf.saxon.sxpath.IndependentContext; - -/** - * This class serves as the means to perform XPath related static - * initialization. For example, initializing custom Jaxen Functions. - * Initialization should be performed before any XPath related operations are - * performed. - * - * @deprecated Is internal API - */ -@InternalApi -@Deprecated -public final class Initializer { - - private Initializer() { } - - /** - * Perform all initialization. - */ - public static void initialize() { - // noop as initialization is done in static block below - } - - /** - * Perform all initialization. - */ - public static void initialize(IndependentContext context) { - context.declareNamespace("pmd", "java:" + PMDFunctions.class.getName()); - for (Language language : LanguageRegistry.getLanguages()) { - for (LanguageVersion languageVersion : language.getVersions()) { - LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler(); - if (languageVersionHandler != null) { - languageVersionHandler.getXPathHandler().initialize(context); - } - } - } - } - - static { - initializeGlobal(); - initializeLanguages(); - } - - private static void initializeGlobal() { - MatchesFunction.registerSelfInSimpleContext(); - } - - private static void initializeLanguages() { - for (Language language : LanguageRegistry.getLanguages()) { - for (LanguageVersion languageVersion : language.getVersions()) { - LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler(); - if (languageVersionHandler != null) { - languageVersionHandler.getXPathHandler().initialize(); - } - } - } - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 4a554fbffd..acc80b8704 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -22,6 +22,6 @@ public class CPDCommandLineInterfaceTest { System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/files/", "--format", "xml", }); - Assert.assertEquals("" + "\n" + "", log.getLog()); + Assert.assertEquals("" + "\n" + "", log.getLog().trim()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java deleted file mode 100644 index c95afbc368..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.rule.xpath; - -import java.util.Collections; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -public class JaxenXPathRuleQueryTest { - - @Test - public void testListAttribute() { - DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1); - - assertQuery(1, "//dummyNode[@SimpleAtt = \"foo\"]", dummy); - assertQuery(1, "//dummyNode[@Enum = \"FOO\"]", dummy); - assertQuery(0, "//dummyNode[@Enum = \"BAR\"]", dummy); - - // queries with lists are not supported with xpath 1.0 - assertQuery(0, "//dummyNode[@List = \"[A, B]\"]", dummy); - assertQuery(0, "//dummyNode[contains(@List, \"B\")]", dummy); - assertQuery(0, "//dummyNode[@List = \"C\"]", dummy); - assertQuery(0, "//dummyNode[@EnumList = \"[FOO, BAR]\"]", dummy); - assertQuery(0, "//dummyNode[contains(@EnumList, \"BAR\")]", dummy); - assertQuery(0, "//dummyNode[@EmptyList = \"A\"]", dummy); - } - - @Test - public void ruleChainVisits() { - final String xpath = "//dummyNode[@Image='baz']/foo | //bar[@Public = 'true'] | //dummyNode[@Public = 'false'] | //dummyNode"; - JaxenXPathRuleQuery query = createQuery(xpath); - List ruleChainVisits = query.getRuleChainVisits(); - Assert.assertEquals(3, ruleChainVisits.size()); - Assert.assertTrue(ruleChainVisits.contains("dummyNode")); - Assert.assertTrue(ruleChainVisits.contains("bar")); - // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't - // match a real node name. - Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT)); - - DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1); - RuleContext data = new RuleContext(); - data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion()); - - query.evaluate(dummy, data); - // note: the actual xpath queries are only available after evaluating - Assert.assertEquals(3, query.nodeNameToXPaths.size()); - Assert.assertEquals("self::node()", query.nodeNameToXPaths.get("dummyNode").get(0).toString()); - Assert.assertEquals("self::node()[(attribute::Public = \"false\")]", query.nodeNameToXPaths.get("dummyNode").get(1).toString()); - Assert.assertEquals("self::node()[(attribute::Image = \"baz\")]/child::foo", query.nodeNameToXPaths.get("dummyNode").get(2).toString()); - Assert.assertEquals("self::node()[(attribute::Public = \"true\")]", query.nodeNameToXPaths.get("bar").get(0).toString()); - Assert.assertEquals(xpath, query.nodeNameToXPaths.get(JaxenXPathRuleQuery.AST_ROOT).get(0).toString()); - } - - @Test - public void ruleChainVisitsMultipleFilters() { - final String xpath = "//dummyNode[@Test1 = 'false'][@Test2 = 'true']"; - JaxenXPathRuleQuery query = createQuery(xpath); - List ruleChainVisits = query.getRuleChainVisits(); - Assert.assertEquals(2, ruleChainVisits.size()); - Assert.assertTrue(ruleChainVisits.contains("dummyNode")); - // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't - // match a real node name. - Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT)); - - DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1); - RuleContext data = new RuleContext(); - data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion()); - - query.evaluate(dummy, data); - // note: the actual xpath queries are only available after evaluating - Assert.assertEquals(2, query.nodeNameToXPaths.size()); - Assert.assertEquals("self::node()[(attribute::Test1 = \"false\")][(attribute::Test2 = \"true\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString()); - Assert.assertEquals(xpath, query.nodeNameToXPaths.get(JaxenXPathRuleQuery.AST_ROOT).get(0).toString()); - } - - @Test - public void ruleChainVisitsNested() { - final String xpath = "//dummyNode/foo/*/bar[@Test = 'false']"; - JaxenXPathRuleQuery query = createQuery(xpath); - List ruleChainVisits = query.getRuleChainVisits(); - Assert.assertEquals(2, ruleChainVisits.size()); - Assert.assertTrue(ruleChainVisits.contains("dummyNode")); - // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't - // match a real node name. - Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT)); - - DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1); - RuleContext data = new RuleContext(); - data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion()); - - query.evaluate(dummy, data); - // note: the actual xpath queries are only available after evaluating - Assert.assertEquals(2, query.nodeNameToXPaths.size()); - Assert.assertEquals("self::node()/child::foo/child::*/child::bar[(attribute::Test = \"false\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString()); - Assert.assertEquals(xpath, query.nodeNameToXPaths.get(JaxenXPathRuleQuery.AST_ROOT).get(0).toString()); - } - - @Test - public void ruleChainVisitsNested2() { - final String xpath = "//dummyNode/foo[@Baz = 'a']/*/bar[@Test = 'false']"; - JaxenXPathRuleQuery query = createQuery(xpath); - List ruleChainVisits = query.getRuleChainVisits(); - Assert.assertEquals(2, ruleChainVisits.size()); - Assert.assertTrue(ruleChainVisits.contains("dummyNode")); - // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't - // match a real node name. - Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT)); - - DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1); - RuleContext data = new RuleContext(); - data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion()); - - query.evaluate(dummy, data); - // note: the actual xpath queries are only available after evaluating - Assert.assertEquals(2, query.nodeNameToXPaths.size()); - Assert.assertEquals("self::node()/child::foo[(attribute::Baz = \"a\")]/child::*/child::bar[(attribute::Test = \"false\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString()); - Assert.assertEquals(xpath, query.nodeNameToXPaths.get(JaxenXPathRuleQuery.AST_ROOT).get(0).toString()); - } - - private static void assertQuery(int resultSize, String xpath, Node node) { - JaxenXPathRuleQuery query = createQuery(xpath); - RuleContext data = new RuleContext(); - data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion()); - List result = query.evaluate(node, data); - Assert.assertEquals(resultSize, result.size()); - } - - private static JaxenXPathRuleQuery createQuery(String xpath) { - JaxenXPathRuleQuery query = new JaxenXPathRuleQuery(); - query.setVersion("1.0"); - query.setProperties(Collections., Object>emptyMap()); - query.setXPath(xpath); - return query; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQueryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQueryTest.java index 93b7d4e3a3..abc199887f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQueryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQueryTest.java @@ -13,7 +13,9 @@ import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.XPathHandler; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.XPathRule.XPathVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -158,22 +160,6 @@ public class SaxonXPathRuleQueryTest { .replaceAll("\\$zz:zz-?\\d+", "\\$zz:zz000"); } - @Test - public void ruleChainVisitsCompatibilityMode() { - SaxonXPathRuleQuery query = createQuery("//dummyNode[@Image='baz']/foo | //bar[@Public = 'true'] | //dummyNode[@Public = 'false']"); - query.setVersion(XPathRuleQuery.XPATH_1_0_COMPATIBILITY); - List ruleChainVisits = query.getRuleChainVisits(); - Assert.assertEquals(2, ruleChainVisits.size()); - Assert.assertTrue(ruleChainVisits.contains("dummyNode")); - Assert.assertTrue(ruleChainVisits.contains("bar")); - - Assert.assertEquals(3, query.nodeNameToXPaths.size()); - assertExpression("((self::node()[QuantifiedExpression(Atomizer(attribute::attribute(Image, xs:anyAtomicType)), ($qq:qq6519275 singleton eq \"baz\"))])/child::element(foo, xs:anyType))", query.nodeNameToXPaths.get("dummyNode").get(0)); - assertExpression("(self::node()[QuantifiedExpression(Atomizer(attribute::attribute(Public, xs:anyAtomicType)), ($qq:qq1529060733 singleton eq \"false\"))])", query.nodeNameToXPaths.get("dummyNode").get(1)); - assertExpression("(self::node()[QuantifiedExpression(Atomizer(attribute::attribute(Public, xs:anyAtomicType)), ($qq:qq1484171695 singleton eq \"true\"))])", query.nodeNameToXPaths.get("bar").get(0)); - assertExpression("((DocumentSorter(((((/)/descendant::element(dummyNode, xs:anyType))[QuantifiedExpression(Atomizer(attribute::attribute(Image, xs:anyAtomicType)), ($qq:qq692331943 singleton eq \"baz\"))])/child::element(foo, xs:anyType))) | (((/)/descendant::element(bar, xs:anyType))[QuantifiedExpression(Atomizer(attribute::attribute(Public, xs:anyAtomicType)), ($qq:qq2127036371 singleton eq \"true\"))])) | (((/)/descendant::element(dummyNode, xs:anyType))[QuantifiedExpression(Atomizer(attribute::attribute(Public, xs:anyAtomicType)), ($qq:qq1529060733 singleton eq \"false\"))]))", query.nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0)); - } - private static void assertQuery(int resultSize, String xpath, Node node) { SaxonXPathRuleQuery query = createQuery(xpath); List result = query.evaluate(node, new RuleContext()); @@ -181,18 +167,18 @@ public class SaxonXPathRuleQueryTest { } private static SaxonXPathRuleQuery createQuery(String xpath, PropertyDescriptor ...descriptors) { - SaxonXPathRuleQuery query = new SaxonXPathRuleQuery(); - query.setVersion(XPathRuleQuery.XPATH_2_0); + Map, Object> props = new HashMap<>(); if (descriptors != null) { - Map, Object> props = new HashMap, Object>(); for (PropertyDescriptor prop : descriptors) { props.put(prop, prop.defaultValue()); } - query.setProperties(props); - } else { - query.setProperties(Collections., Object>emptyMap()); } - query.setXPath(xpath); - return query; + + return new SaxonXPathRuleQuery( + xpath, + XPathVersion.XPATH_2_0, + props, + XPathHandler.getHandlerForFunctionDefs() + ); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java index ead5c73553..edd079c33b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java @@ -8,9 +8,13 @@ import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.lang.ast.DummyNode; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstDocument; import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode; import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode; +import net.sf.saxon.sxpath.XPathEvaluator; +import net.sf.saxon.sxpath.XPathStaticContext; + public class ElementNodeTest { @Test @@ -23,7 +27,9 @@ public class ElementNodeTest { node.jjtAddChild(foo1, 0); node.jjtAddChild(foo2, 1); - DocumentNode document = new DocumentNode(node); + final XPathEvaluator xpathEvaluator = new XPathEvaluator(); + AstDocument document = new AstDocument(node, xpathEvaluator.getConfiguration()); + ElementNode elementFoo1 = document.nodeToElementNode.get(foo1); ElementNode elementFoo2 = document.nodeToElementNode.get(foo2); diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 4e61c15994..d93c6fee16 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -115,10 +115,6 @@ net.sourceforge.pmd pmd-core - - net.sourceforge.saxon - saxon - org.ow2.asm asm @@ -132,13 +128,6 @@ commons-lang3 - - net.sourceforge.saxon - saxon - dom - runtime - - net.sourceforge.pmd diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index f4b44b3a58..99d969139c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -8,12 +8,9 @@ import java.util.Arrays; import java.util.List; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.Parser; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.XPathHandler; -import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.JavaParser; import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; @@ -23,21 +20,25 @@ import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleViolationFactory; import net.sourceforge.pmd.lang.java.xpath.GetCommentOnFunction; -import net.sourceforge.pmd.lang.java.xpath.JavaFunctions; 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.sourceforge.pmd.util.designerbindings.DesignerBindings; -import net.sf.saxon.sxpath.IndependentContext; - public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { + private static final XPathHandler XPATH_HANDLER = + XPathHandler.getHandlerForFunctionDefs( + new TypeIsFunction(), + new TypeIsExactlyFunction(), + new MetricFunction(), + new GetCommentOnFunction() + ); + private final LanguageLevelChecker levelChecker; private final LanguageMetricsProvider myMetricsProvider = new JavaMetricsProvider(); @@ -63,21 +64,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { @Override public XPathHandler getXPathHandler() { - return new DefaultASTXPathHandler() { - @Override - public void initialize() { - TypeOfFunction.registerSelfInSimpleContext(); - GetCommentOnFunction.registerSelfInSimpleContext(); - MetricFunction.registerSelfInSimpleContext(); - TypeIsFunction.registerSelfInSimpleContext(); - TypeIsExactlyFunction.registerSelfInSimpleContext(); - } - - @Override - public void initialize(IndependentContext context) { - super.initialize(context, LanguageRegistry.getLanguage(JavaLanguageModule.NAME), JavaFunctions.class); - } - }; + return XPATH_HANDLER; } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/BaseJavaXPathFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/BaseJavaXPathFunction.java new file mode 100644 index 0000000000..1ae049ea34 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/BaseJavaXPathFunction.java @@ -0,0 +1,15 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.xpath; + +import net.sourceforge.pmd.lang.ast.xpath.internal.AbstractXPathFunctionDef; +import net.sourceforge.pmd.lang.java.JavaLanguageModule; + +abstract class BaseJavaXPathFunction extends AbstractXPathFunctionDef { + + protected BaseJavaXPathFunction(String localName) { + super(localName, JavaLanguageModule.TERSE_NAME); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/GetCommentOnFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/GetCommentOnFunction.java index 9e79e5328f..946c65b3f7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/GetCommentOnFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/GetCommentOnFunction.java @@ -6,18 +6,19 @@ package net.sourceforge.pmd.lang.java.xpath; import java.util.List; -import org.jaxen.Context; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.SimpleFunctionContext; -import org.jaxen.XPathFunctionContext; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.AbstractNode; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstNodeWrapper; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.Comment; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.EmptyAtomicSequence; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.value.SequenceType; +import net.sf.saxon.value.StringValue; + + /** * The XPath query "//VariableDeclarator[contains(getCommentOn(), * '//password')]" will find all variables declared that are annotated with the @@ -25,33 +26,53 @@ import net.sourceforge.pmd.lang.java.ast.Comment; * * @author Andy Throgmorton */ -@InternalApi -@Deprecated -public class GetCommentOnFunction implements Function { +public class GetCommentOnFunction extends BaseJavaXPathFunction { - public static void registerSelfInSimpleContext() { - // see http://jaxen.org/extensions.html - ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "getCommentOn", - new GetCommentOnFunction()); + + protected GetCommentOnFunction() { + super("getCommentOn"); } @Override - public Object call(Context context, List args) throws FunctionCallException { - if (!args.isEmpty()) { - return Boolean.FALSE; - } - Node n = (Node) context.getNodeSet().get(0); - if (n instanceof AbstractNode) { - int codeBeginLine = ((AbstractNode) n).getBeginLine(); - int codeEndLine = ((AbstractNode) n).getEndLine(); + public SequenceType[] getArgumentTypes() { + return new SequenceType[0]; + } - List commentList = ((AbstractNode) n).getFirstParentOfType(ASTCompilationUnit.class).getComments(); - for (Comment comment : commentList) { - if (comment.getBeginLine() == codeBeginLine || comment.getEndLine() == codeEndLine) { - return comment.getImage(); + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.OPTIONAL_STRING; + } + + + @Override + public boolean dependsOnFocus() { + return true; + } + + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + @Override + public Sequence call(XPathContext context, Sequence[] arguments) { + Node contextNode = ((AstNodeWrapper) context.getContextItem()).getUnderlyingNode(); + + int codeBeginLine = contextNode.getBeginLine(); + int codeEndLine = contextNode.getEndLine(); + + List commentList = contextNode.getFirstParentOfType(ASTCompilationUnit.class).getComments(); + for (Comment comment : commentList) { + if (comment.getBeginLine() == codeBeginLine || comment.getEndLine() == codeEndLine) { + return new StringValue(comment.getImage()); + } } + return new EmptyAtomicSequence(); } - } - return Boolean.FALSE; + + }; } } + + + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/JavaFunctions.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/JavaFunctions.java deleted file mode 100644 index d4242f26e6..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/JavaFunctions.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.xpath; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode; - -import net.sf.saxon.expr.XPathContext; - -/** - * Exposes all Java Language specific functions for Saxon use. - */ -@InternalApi -@Deprecated -public final class JavaFunctions { - - private JavaFunctions() { - // utility class - } - - @Deprecated - public static boolean typeof(final XPathContext context, final String nodeTypeName, final String fullTypeName) { - return typeof(context, nodeTypeName, fullTypeName, null); - } - - @Deprecated - public static boolean typeof(final XPathContext context, final String nodeTypeName, - final String fullTypeName, final String shortTypeName) { - return TypeOfFunction.typeof((Node) ((ElementNode) context.getContextItem()).getUnderlyingNode(), nodeTypeName, - fullTypeName, shortTypeName); - } - - public static double metric(final XPathContext context, final String metricKeyName) { - return MetricFunction.getMetric((Node) ((ElementNode) context.getContextItem()).getUnderlyingNode(), metricKeyName); - } - - public static boolean typeIs(final XPathContext context, final String fullTypeName) { - return TypeIsFunction.typeIs((Node) ((ElementNode) context.getContextItem()).getUnderlyingNode(), fullTypeName); - } - - public static boolean typeIsExactly(final XPathContext context, final String fullTypeName) { - return TypeIsExactlyFunction.typeIsExactly((Node) ((ElementNode) context.getContextItem()).getUnderlyingNode(), fullTypeName); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/MetricFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/MetricFunction.java index 03de85754d..ee87e829d6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/MetricFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/MetricFunction.java @@ -4,19 +4,13 @@ package net.sourceforge.pmd.lang.java.xpath; -import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.lang3.EnumUtils; -import org.jaxen.Context; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.SimpleFunctionContext; -import org.jaxen.XPathFunctionContext; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstNodeWrapper; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; @@ -24,6 +18,13 @@ import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.metrics.MetricKey; import net.sourceforge.pmd.lang.metrics.MetricsUtil; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BigDecimalValue; +import net.sf.saxon.value.SequenceType; + /** * Implements the {@code metric()} XPath function. Takes the @@ -34,54 +35,63 @@ import net.sourceforge.pmd.lang.metrics.MetricsUtil; * @author Clément Fournier * @since 6.0.0 */ -@InternalApi -@Deprecated -public class MetricFunction implements Function { +public class MetricFunction extends BaseJavaXPathFunction { private static final Map CLASS_METRIC_KEY_MAP = EnumUtils.getEnumMap(JavaClassMetricKey.class); private static final Map OPERATION_METRIC_KEY_MAP = EnumUtils.getEnumMap(JavaOperationMetricKey.class); + protected MetricFunction() { + super("metric"); + } @Override - public Object call(Context context, List args) throws FunctionCallException { - - if (args.isEmpty()) { - throw new IllegalArgumentException(badMetricKeyArgMessage()); - } - - if (!(args.get(0) instanceof String)) { - throw new IllegalArgumentException(badMetricKeyArgMessage()); - } - - String metricKeyName = (String) args.get(0); - Node n = (Node) context.getNodeSet().get(0); - - return getMetric(n, metricKeyName); + public SequenceType[] getArgumentTypes() { + return new SequenceType[]{SequenceType.SINGLE_STRING}; } - public static String badOperationMetricKeyMessage() { + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_DECIMAL; + } + + + @Override + public boolean dependsOnFocus() { + return true; + } + + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node contextNode = ((AstNodeWrapper) context.getContextItem()).getUnderlyingNode(); + String metricKey = arguments[0].head().getStringValue(); + + return new BigDecimalValue(getMetric(contextNode, metricKey)); + } + }; + } + + + static String badOperationMetricKeyMessage() { return "This is not the name of an operation metric"; } - public static String badClassMetricKeyMessage() { + static String badClassMetricKeyMessage() { return "This is not the name of a class metric"; } - public static String genericBadNodeMessage() { + static String genericBadNodeMessage() { return "Incorrect node type: the 'metric' function cannot be applied"; } - - public static String badMetricKeyArgMessage() { - return "The 'metric' function expects the name of a metric key"; - } - - - public static double getMetric(Node n, String metricKeyName) { + private static double getMetric(Node n, String metricKeyName) { if (n instanceof ASTAnyTypeDeclaration) { return computeMetric(getClassMetricKey(metricKeyName), (ASTAnyTypeDeclaration) n); } else if (n instanceof MethodLikeNode) { @@ -113,10 +123,4 @@ public class MetricFunction implements Function { return OPERATION_METRIC_KEY_MAP.get(constantName); } - - public static void registerSelfInSimpleContext() { - ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, - "metric", - new MetricFunction()); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java index ad2cd4a6e5..771110ddce 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java @@ -4,55 +4,65 @@ package net.sourceforge.pmd.lang.java.xpath; -import java.util.List; - -import org.jaxen.Context; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.SimpleFunctionContext; -import org.jaxen.XPathFunctionContext; - -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstNodeWrapper; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.SequenceType; -@InternalApi -@Deprecated -public class TypeIsExactlyFunction implements Function { - public static void registerSelfInSimpleContext() { - ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "typeIsExactly", - new TypeIsExactlyFunction()); +/** + * XPath function {@code pmd-java:typeIsExactly(typeName as xs:string) as xs:boolean}. + * + *

Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIsExactly('java.lang.String')]} + * + *

Returns true if the type of the node matches, false otherwise. + */ +public class TypeIsExactlyFunction extends BaseJavaXPathFunction { + + + protected TypeIsExactlyFunction() { + super("typeIsExactly"); } @Override - public Object call(final Context context, final List args) throws FunctionCallException { - if (args.size() != 1) { - throw new IllegalArgumentException( - "typeIsExactly function takes a single String argument with the fully qualified type name to check against."); - } - final String fullTypeName = (String) args.get(0); - final Node n = (Node) context.getNodeSet().get(0); - - return typeIsExactly(n, fullTypeName); + public SequenceType[] getArgumentTypes() { + return new SequenceType[]{SequenceType.SINGLE_STRING}; } - /** - * Example XPath 1.0: {@code //ClassOrInterfaceType[typeIsExactly('java.lang.String')]} - *

- * Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIsExactly('java.lang.String')]} - * - * @param n The node on which to check for types - * @param fullTypeName The fully qualified name of the class or any supertype - * @return True if the type of the node matches, false otherwise. - */ - public static boolean typeIsExactly(final Node n, final String fullTypeName) { - if (n instanceof TypeNode) { - return TypeHelper.isExactlyA((TypeNode) n, fullTypeName); - } else { - throw new IllegalArgumentException("typeIsExactly function may only be called on a TypeNode."); - } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_BOOLEAN; } + + + @Override + public boolean dependsOnFocus() { + return true; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node contextNode = ((AstNodeWrapper) context.getContextItem()).getUnderlyingNode(); + String fullTypeName = arguments[0].head().getStringValue(); + + if (contextNode instanceof TypeNode) { + return BooleanValue.get(TypeHelper.isExactlyA((TypeNode) contextNode, fullTypeName)); + } else { + throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); + } + } + }; + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java index 1bf3853df9..53cddd66a8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java @@ -4,55 +4,64 @@ package net.sourceforge.pmd.lang.java.xpath; -import java.util.List; - -import org.jaxen.Context; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.SimpleFunctionContext; -import org.jaxen.XPathFunctionContext; - -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.internal.AstNodeWrapper; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.SequenceType; -@InternalApi -@Deprecated -public class TypeIsFunction implements Function { - public static void registerSelfInSimpleContext() { - ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "typeIs", - new TypeIsFunction()); +/** + * XPath function {@code pmd-java:typeIs(typeName as xs:string) as xs:boolean}. + * + *

Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIs('java.lang.String')]} + * + *

Returns true if the type of the node matches, false otherwise. + */ +public class TypeIsFunction extends BaseJavaXPathFunction { + + + protected TypeIsFunction() { + super("typeIs"); } @Override - public Object call(final Context context, final List args) throws FunctionCallException { - if (args.size() != 1) { - throw new IllegalArgumentException( - "typeIs function takes a single String argument with the fully qualified type name to check against."); - } - final String fullTypeName = (String) args.get(0); - final Node n = (Node) context.getNodeSet().get(0); - - return typeIs(n, fullTypeName); + public SequenceType[] getArgumentTypes() { + return new SequenceType[]{SequenceType.SINGLE_STRING}; } - /** - * Example XPath 1.0: {@code //ClassOrInterfaceType[typeIs('java.lang.String')]} - *

- * Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIs('java.lang.String')]} - * - * @param n The node on which to check for types - * @param fullTypeName The fully qualified name of the class or any supertype - * @return True if the type of the node matches, false otherwise. - */ - public static boolean typeIs(final Node n, final String fullTypeName) { - if (n instanceof TypeNode) { - return TypeHelper.isA((TypeNode) n, fullTypeName); - } else { - throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); - } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_BOOLEAN; + } + + + @Override + public boolean dependsOnFocus() { + return true; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node contextNode = ((AstNodeWrapper) context.getContextItem()).getUnderlyingNode(); + String fullTypeName = arguments[0].head().getStringValue(); + + if (contextNode instanceof TypeNode) { + return BooleanValue.get(TypeHelper.isA((TypeNode) contextNode, fullTypeName)); + } else { + throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); + } + } + }; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeOfFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeOfFunction.java deleted file mode 100644 index 2fe6167146..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeOfFunction.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.xpath; - -import java.util.Arrays; -import java.util.List; -import java.util.logging.Logger; - -import org.jaxen.Context; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.SimpleFunctionContext; -import org.jaxen.XPathFunctionContext; - -import net.sourceforge.pmd.PMDVersion; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.xpath.Attribute; -import net.sourceforge.pmd.lang.java.ast.TypeNode; - -@Deprecated -public class TypeOfFunction implements Function { - - private static final Logger LOG = Logger.getLogger(TypeOfFunction.class.getName()); - private static boolean deprecationWarned = false; - - public static void registerSelfInSimpleContext() { - ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "typeof", - new TypeOfFunction()); - } - - @Override - public Object call(Context context, List args) throws FunctionCallException { - nagDeprecatedFunction(); - - String nodeTypeName = null; - String fullTypeName = null; - String shortTypeName = null; - Attribute attr = null; - for (int i = 0; i < args.size(); i++) { - if (args.get(i) instanceof List) { - if (attr == null) { - attr = ((List) args.get(i)).get(0); - nodeTypeName = attr.getStringValue(); - } else { - throw new IllegalArgumentException( - "typeof function can take only a single argument which is an Attribute."); - } - } else { - if (fullTypeName == null) { - fullTypeName = (String) args.get(i); - } else if (shortTypeName == null) { - shortTypeName = (String) args.get(i); - } else { - break; - } - } - } - if (fullTypeName == null) { - throw new IllegalArgumentException( - "typeof function must be given at least one String argument for the fully qualified type name."); - } - Node n = (Node) context.getNodeSet().get(0); - return typeof(n, nodeTypeName, fullTypeName, shortTypeName); - } - - private static void nagDeprecatedFunction() { - if (!deprecationWarned) { - deprecationWarned = true; - LOG.warning("The XPath function typeof() is deprecated and will be removed in " - + PMDVersion.getNextMajorRelease() + ". Use typeIs() instead."); - } - } - - /** - * Example XPath 1.0: {@code //ClassOrInterfaceType[typeof(@Image, 'java.lang.String', 'String')]} - *

- * Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeof(@Image, 'java.lang.String', 'String')]} - * - * @param n - * @param nodeTypeName Usually the {@code @Image} attribute of the node - * @param fullTypeName The fully qualified name of the class or any supertype - * @param shortTypeName The simple class name, might be null - * @return - */ - public static boolean typeof(Node n, String nodeTypeName, String fullTypeName, String shortTypeName) { - nagDeprecatedFunction(); - - if (n instanceof TypeNode) { - Class type = ((TypeNode) n).getType(); - if (type == null) { - return nodeTypeName != null - && (nodeTypeName.equals(fullTypeName) || nodeTypeName.equals(shortTypeName)); - } - if (type.getName().equals(fullTypeName)) { - return true; - } - List> implementors = Arrays.asList(type.getInterfaces()); - if (implementors.contains(type)) { - return true; - } - Class superC = type.getSuperclass(); - while (superC != null && !superC.equals(Object.class)) { - if (superC.getName().equals(fullTypeName)) { - return true; - } - superC = superC.getSuperclass(); - } - } else { - throw new IllegalArgumentException("typeof function may only be called on a TypeNode."); - } - return false; - } -} diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index 915d626c88..e92be26379 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -542,10 +542,7 @@ Errors are system exceptions. Do not extend them. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java index 3979a181fc..dd862cdb53 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java @@ -30,9 +30,7 @@ import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.JavaParsingHelper; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.JaxenXPathRuleQuery; import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; -import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -132,28 +130,9 @@ public class XPathRuleTest extends RuleTst { String xpath = "//PrimarySuffix[@Image='list']"; - // XPATH version 1.0 - XPathRuleQuery xpathRuleQuery = new JaxenXPathRuleQuery(); - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setProperties(new HashMap, Object>()); - xpathRuleQuery.setVersion(XPathRuleQuery.XPATH_1_0); - List nodes = xpathRuleQuery.evaluate(cu, ruleContext); - assertEquals(1, nodes.size()); - - // XPATH version 1.0 Compatibility - xpathRuleQuery = new SaxonXPathRuleQuery(); - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setProperties(new HashMap, Object>()); - xpathRuleQuery.setVersion(XPathRuleQuery.XPATH_1_0_COMPATIBILITY); - nodes = xpathRuleQuery.evaluate(cu, ruleContext); - assertEquals(1, nodes.size()); - // XPATH version 2.0 - xpathRuleQuery = new SaxonXPathRuleQuery(); - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setProperties(new HashMap, Object>()); - xpathRuleQuery.setVersion(XPathRuleQuery.XPATH_2_0); - nodes = xpathRuleQuery.evaluate(cu, ruleContext); + SaxonXPathRuleQuery xpathRuleQuery = new SaxonXPathRuleQuery(xpath, XPathVersion.XPATH_2_0, new HashMap<>(), language.getLanguageVersionHandler().getXPathHandler()); + List nodes = xpathRuleQuery.evaluate(cu, ruleContext); assertEquals(1, nodes.size()); } @@ -174,22 +153,9 @@ public class XPathRuleTest extends RuleTst { String xpath = "//Block/BlockStatement/following-sibling::BlockStatement"; - // XPATH version 1.0 - XPathRuleQuery xpathRuleQuery = new JaxenXPathRuleQuery(); - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setProperties(new HashMap, Object>()); - xpathRuleQuery.setVersion(XPathRuleQuery.XPATH_1_0); - List nodes = xpathRuleQuery.evaluate(cu, ruleContext); - assertEquals(2, nodes.size()); - assertEquals(4, nodes.get(0).getBeginLine()); - assertEquals(5, nodes.get(1).getBeginLine()); - // XPATH version 2.0 - xpathRuleQuery = new SaxonXPathRuleQuery(); - xpathRuleQuery.setXPath(xpath); - xpathRuleQuery.setProperties(new HashMap, Object>()); - xpathRuleQuery.setVersion(XPathRuleQuery.XPATH_2_0); - nodes = xpathRuleQuery.evaluate(cu, ruleContext); + SaxonXPathRuleQuery xpathRuleQuery = new SaxonXPathRuleQuery(xpath, XPathVersion.XPATH_2_0, new HashMap<>(), language.getLanguageVersionHandler().getXPathHandler()); + List nodes = xpathRuleQuery.evaluate(cu, ruleContext); assertEquals(2, nodes.size()); assertEquals(4, nodes.get(0).getBeginLine()); assertEquals(5, nodes.get(1).getBeginLine()); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/xpath/XPathMetricFunctionTest.java similarity index 97% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/xpath/XPathMetricFunctionTest.java index 2dc75f74cc..39c23fc018 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/xpath/XPathMetricFunctionTest.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.java.metrics.xpath; +package net.sourceforge.pmd.lang.java.xpath; import static org.junit.Assert.assertTrue; @@ -24,7 +24,6 @@ import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.java.xpath.MetricFunction; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 1265609c0d..7d9121444e 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -84,10 +84,6 @@ commons-io commons-io - - net.sourceforge.saxon - saxon - junit diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 9c138a7b0c..1c09790f0c 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -84,10 +84,6 @@ commons-io commons-io - - net.sourceforge.saxon - saxon - junit diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index 1297910c60..92a0ef0274 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -92,10 +92,6 @@ commons-io commons-io - - net.sourceforge.saxon - saxon - junit diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 98f220272b..687a1b520d 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -84,10 +84,6 @@ commons-io commons-io - - net.sourceforge.saxon - saxon - junit diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index 2a2f9f7307..e40aca4e6a 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -92,10 +92,6 @@ org.apache.commons commons-lang3 - - net.sourceforge.saxon - saxon - junit diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index c588a93bed..40e4334bfb 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -34,22 +34,11 @@ net.sourceforge.pmd pmd-core - - net.sourceforge.saxon - saxon - commons-io commons-io - - net.sourceforge.saxon - saxon - dom - runtime - - junit junit diff --git a/pom.xml b/pom.xml index 9fd8633564..679811850b 100644 --- a/pom.xml +++ b/pom.xml @@ -645,15 +645,9 @@ ${project.version} - net.sourceforge.saxon - saxon - 9.1.0.8 - - - net.sourceforge.saxon - saxon - 9.1.0.8 - dom + net.sf.saxon + Saxon-HE + 9.8.0-7 org.mozilla