forked from phoedos/pmd
Add the attributes of the underlying node to Apex xpath nodes
This commit is contained in:
@ -10,6 +10,7 @@ public class ASTBooleanExpression extends AbstractApexNode<BooleanExpression> {
|
||||
|
||||
public ASTBooleanExpression(BooleanExpression booleanExpression) {
|
||||
super(booleanExpression);
|
||||
booleanExpression.getOp();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,7 +4,14 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.apex.ast;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator;
|
||||
|
||||
import apex.jorje.data.Location;
|
||||
import apex.jorje.data.Locations;
|
||||
@ -57,4 +64,20 @@ public abstract class AbstractApexNode<T extends AstNode> extends AbstractApexNo
|
||||
return "no location";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<Attribute> getXPathAttributesIterator() {
|
||||
// Attributes of this node have precedence over same-name attributes of the underlying node
|
||||
return Stream.concat(iteratorToStream(new AttributeAxisIterator(this)),
|
||||
iteratorToStream(new AttributeAxisIterator(this, node)))
|
||||
.distinct()
|
||||
.iterator();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static <T> Stream<T> iteratorToStream(Iterator<? extends T> it) {
|
||||
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.ast.xpath;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
@ -18,6 +19,9 @@ import net.sourceforge.pmd.lang.ast.Node;
|
||||
* Attributes know their name, the node they wrap,
|
||||
* and have access to their value.
|
||||
*
|
||||
* Two attributes are equal iff their parent node is
|
||||
* equal and they have the same name.
|
||||
*
|
||||
* @author daniels
|
||||
*/
|
||||
public class Attribute {
|
||||
@ -28,8 +32,9 @@ public class Attribute {
|
||||
|
||||
private static final Object[] EMPTY_OBJ_ARRAY = new Object[0];
|
||||
|
||||
private Node parent;
|
||||
private String name;
|
||||
private final Node parent;
|
||||
private final Object myBean;
|
||||
private final String name;
|
||||
private Method method;
|
||||
private Object value;
|
||||
private String stringValue;
|
||||
@ -39,15 +44,25 @@ public class Attribute {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.method = m;
|
||||
this.myBean = parent;
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new attribute belonging to the given node using its accessor. */
|
||||
public Attribute(Node parent, Object bean, String name, Method m) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.method = m;
|
||||
this.myBean = bean;
|
||||
}
|
||||
|
||||
/** Creates a new attribute belonging to the given node using its string value. */
|
||||
public Attribute(Node parent, String name, String value) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.stringValue = value;
|
||||
this.myBean = parent;
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +88,7 @@ public class Attribute {
|
||||
|
||||
// this lazy loading reduces calls to Method.invoke() by about 90%
|
||||
try {
|
||||
value = method.invoke(parent, EMPTY_OBJ_ARRAY);
|
||||
value = method.invoke(myBean, EMPTY_OBJ_ARRAY);
|
||||
return value;
|
||||
} catch (IllegalAccessException | InvocationTargetException iae) {
|
||||
iae.printStackTrace();
|
||||
@ -93,12 +108,33 @@ public class Attribute {
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Attribute attribute = (Attribute) o;
|
||||
return Objects.equals(parent, attribute.parent) &&
|
||||
Objects.equals(name, attribute.name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(parent, name);
|
||||
}
|
||||
|
||||
|
||||
private String getLoggableAttributeName() {
|
||||
return parent.getXPathNodeName() + "/@" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ':' + getValue() + ':' + parent;
|
||||
return name + ':' + getValue() + ':' + parent.getXPathNodeName();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@ -44,6 +45,7 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
||||
private MethodWrapper[] methodWrappers;
|
||||
private int position;
|
||||
private Node node;
|
||||
private final Object bean;
|
||||
|
||||
|
||||
/**
|
||||
@ -52,24 +54,28 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
||||
* use instead the overridable {@link Node#getXPathAttributesIterator()}.
|
||||
*/
|
||||
public AttributeAxisIterator(Node contextNode) {
|
||||
this(contextNode, contextNode);
|
||||
}
|
||||
|
||||
public AttributeAxisIterator(Node contextNode, Object bean) {
|
||||
this.node = contextNode;
|
||||
if (!METHOD_CACHE.containsKey(contextNode.getClass())) {
|
||||
Method[] preFilter = contextNode.getClass().getMethods();
|
||||
this.bean = bean;
|
||||
if (!METHOD_CACHE.containsKey(bean.getClass())) {
|
||||
Method[] preFilter = bean.getClass().getMethods();
|
||||
List<MethodWrapper> postFilter = new ArrayList<>();
|
||||
for (Method element : preFilter) {
|
||||
if (isAttributeAccessor(element)) {
|
||||
postFilter.add(new MethodWrapper(element));
|
||||
}
|
||||
}
|
||||
METHOD_CACHE.putIfAbsent(contextNode.getClass(), postFilter.toArray(new MethodWrapper[0]));
|
||||
METHOD_CACHE.putIfAbsent(bean.getClass(), postFilter.toArray(new MethodWrapper[0]));
|
||||
}
|
||||
this.methodWrappers = METHOD_CACHE.get(contextNode.getClass());
|
||||
this.methodWrappers = METHOD_CACHE.get(bean.getClass());
|
||||
|
||||
this.position = 0;
|
||||
this.currObj = getNextAttribute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given method is an attribute accessor,
|
||||
* in which case a corresponding Attribute will be added to
|
||||
@ -80,12 +86,16 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
||||
protected boolean isAttributeAccessor(Method method) {
|
||||
String methodName = method.getName();
|
||||
|
||||
return CONSIDERED_RETURN_TYPES.contains(method.getReturnType())
|
||||
return isConsideredReturnType(method.getReturnType())
|
||||
&& method.getParameterTypes().length == 0
|
||||
&& !methodName.startsWith("jjt")
|
||||
&& !FILTERED_OUT_NAMES.contains(methodName);
|
||||
}
|
||||
|
||||
private boolean isConsideredReturnType(Class<?> klass) {
|
||||
return CONSIDERED_RETURN_TYPES.contains(klass) || klass.isEnum();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Attribute next() {
|
||||
@ -115,7 +125,7 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
||||
return null;
|
||||
}
|
||||
MethodWrapper m = methodWrappers[position++];
|
||||
return new Attribute(node, m.name, m.method);
|
||||
return new Attribute(node, bean, m.name, m.method);
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,7 +236,9 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
|
||||
*/
|
||||
if (value == null) {
|
||||
return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
|
||||
|
||||
} else if (value instanceof Enum) {
|
||||
// enums use their toString
|
||||
return new StringValue(value.toString());
|
||||
} else if (value instanceof String) {
|
||||
return new StringValue((String) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
|
@ -164,6 +164,7 @@ public abstract class AbstractPropertySource implements PropertySource {
|
||||
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public <V> void setProperty(MultiValuePropertyDescriptor<V> propertyDescriptor, V... values) {
|
||||
checkValidPropertyDescriptor(propertyDescriptor);
|
||||
propertyValuesByDescriptor.put(propertyDescriptor, Collections.unmodifiableList(Arrays.asList(values)));
|
||||
|
@ -112,7 +112,10 @@ public interface PropertySource {
|
||||
* @param propertyDescriptor The property descriptor for which to add a value
|
||||
* @param values Values
|
||||
* @param <V> The type of the values
|
||||
*
|
||||
* @deprecated {@link MultiValuePropertyDescriptor} is deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
<V> void setProperty(MultiValuePropertyDescriptor<V> propertyDescriptor, V... values);
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user