From d4b39729f10b68b37e666d03e5d49b6ad9d860cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Nov 2017 14:13:46 +0100 Subject: [PATCH] Make attributes support full value range --- .../lang/ast/xpath/AttributeAxisIterator.java | 20 +++-- .../lang/ast/xpath/saxon/AttributeNode.java | 20 +---- .../lang/rule/xpath/SaxonXPathRuleQuery.java | 74 +++++++++++-------- 3 files changed, 58 insertions(+), 56 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java index c6e05d20ae..3c4cb72094 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java @@ -6,8 +6,11 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -98,17 +101,20 @@ public class AttributeAxisIterator implements Iterator { return new Attribute(node, m.name, m.method); } + private static final Set> CONSIDERED_RETURN_TYPES + = new HashSet<>(Arrays.>asList(Integer.TYPE, Boolean.TYPE, Double.TYPE, String.class, Long.TYPE, Character.TYPE, Float.TYPE)); + + private static final Set FILTERED_OUT_NAMES + = new HashSet<>(Arrays.asList("toString", "getClass", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope")); + protected boolean isAttributeAccessor(Method method) { - String methodName = method.getName(); boolean deprecated = method.getAnnotation(Deprecated.class) != null; return !deprecated - && (Integer.TYPE == method.getReturnType() || Boolean.TYPE == method.getReturnType() - || Double.TYPE == method.getReturnType() || String.class == method.getReturnType()) - && method.getParameterTypes().length == 0 && Void.TYPE != method.getReturnType() - && !methodName.startsWith("jjt") && !"toString".equals(methodName) && !"getScope".equals(methodName) - && !"getClass".equals(methodName) && !"getTypeNameNode".equals(methodName) - && !"getImportedNameNode".equals(methodName) && !"hashCode".equals(methodName); + && CONSIDERED_RETURN_TYPES.contains(method.getReturnType()) + && method.getParameterTypes().length == 0 + && !methodName.startsWith("jjt") + && !FILTERED_OUT_NAMES.contains(methodName); } } 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 index 8b859f1c38..8470948cab 100644 --- 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 @@ -5,15 +5,12 @@ package net.sourceforge.pmd.lang.ast.xpath.saxon; import net.sourceforge.pmd.lang.ast.xpath.Attribute; +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.BooleanValue; -import net.sf.saxon.value.EmptySequence; -import net.sf.saxon.value.Int64Value; -import net.sf.saxon.value.StringValue; import net.sf.saxon.value.Value; /** @@ -48,20 +45,7 @@ public class AttributeNode extends AbstractNodeInfo { public Value atomize() throws XPathException { if (value == null) { Object v = attribute.getValue(); - // TODO Need to handle the full range of types, is there something - // Saxon can do to help? - if (v instanceof String) { - value = new StringValue((String) v); - } else if (v instanceof Boolean) { - value = BooleanValue.get(((Boolean) v).booleanValue()); - } else if (v instanceof Integer) { - value = Int64Value.makeIntegerValue((Integer) v); - } else if (v == null) { - value = EmptySequence.getInstance(); - } else { - throw new RuntimeException( - "Unable to create ValueRepresentaton for attribute value: " + v + " of type " + v.getClass()); - } + value = SaxonXPathRuleQuery.getAtomicRepresentation(v); } return value; } 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 0454b87fe7..65d74bbb16 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 @@ -26,6 +26,7 @@ import net.sf.saxon.sxpath.XPathExpression; import net.sf.saxon.sxpath.XPathStaticContext; import net.sf.saxon.sxpath.XPathVariable; import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.AtomicValue; import net.sf.saxon.value.BigIntegerValue; import net.sf.saxon.value.BooleanValue; import net.sf.saxon.value.DoubleValue; @@ -42,18 +43,19 @@ import net.sf.saxon.value.UntypedAtomicValue; */ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { - // Mapping from Node name to applicable XPath queries - private XPathExpression xpathExpression; - private List xpathVariables; - private static final int MAX_CACHE_SIZE = 20; private static final Map CACHE = new LinkedHashMap(MAX_CACHE_SIZE) { private static final long serialVersionUID = -7653916493967142443L; + protected boolean removeEldestEntry(final Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } }; + // Mapping from Node name to applicable XPath queries + private XPathExpression xpathExpression; + private List xpathVariables; + /** * {@inheritDoc} @@ -63,6 +65,7 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { return XPATH_1_0_COMPATIBILITY.equals(version) || XPATH_2_0.equals(version); } + /** * {@inheritDoc} */ @@ -112,38 +115,14 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { } Item[] converted = new Item[val.size()]; for (int i = 0; i < val.size(); i++) { - converted[i] = getItemRepresentation(val.get(i)); + converted[i] = getAtomicRepresentation(val.get(i)); } return new SequenceExtent(converted); } else { - return getItemRepresentation(value); + return getAtomicRepresentation(value); } } - - - private Item getItemRepresentation(Object value) { - 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 { - // We could maybe use UntypedAtomicValue - throw new RuntimeException("Unable to create ValueRepresentation for value of type: " + value.getClass()); - } - } - + private DocumentNode getDocumentNode(Node node) { // Get the root AST node @@ -164,6 +143,7 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { return documentNode; } + private void initializeXPathExpression() { if (xpathExpression != null) { return; @@ -200,4 +180,36 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { throw new RuntimeException(e); } } + + + /** + * 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(Object value) { + 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 { + // We could maybe use UntypedAtomicValue + throw new RuntimeException("Unable to create ValueRepresentation for value of type: " + value.getClass()); + } + } }