Document AttributeAxisIterator
This commit is contained in:
@ -16,17 +16,36 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
|
|
||||||
import net.sourceforge.pmd.lang.ast.Node;
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explores an AST node reflectively to iterate over its XPath
|
||||||
|
* attributes. This is the default way the attributes of a node
|
||||||
|
* are made accessible to XPath rules, and defines an important
|
||||||
|
* piece of PMD's XPath support.
|
||||||
|
*/
|
||||||
public class AttributeAxisIterator implements Iterator<Attribute> {
|
public class AttributeAxisIterator implements Iterator<Attribute> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates an attribute accessor with the XPath-accessible
|
||||||
|
* name of the attribute. This is used to avoid recomputing
|
||||||
|
* the name of the attribute for each attribute (it's only done
|
||||||
|
* once and put inside the {@link #METHOD_CACHE}.
|
||||||
|
*/
|
||||||
private static class MethodWrapper {
|
private static class MethodWrapper {
|
||||||
public Method method;
|
public Method method;
|
||||||
public String name;
|
public String name;
|
||||||
|
|
||||||
|
|
||||||
MethodWrapper(Method m) {
|
MethodWrapper(Method m) {
|
||||||
this.method = m;
|
this.method = m;
|
||||||
this.name = truncateMethodName(m.getName());
|
this.name = truncateMethodName(m.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method produces the actual XPath name of an attribute
|
||||||
|
* from the name of its accessor.
|
||||||
|
*/
|
||||||
private String truncateMethodName(String n) {
|
private String truncateMethodName(String n) {
|
||||||
// about 70% of the methods start with 'get', so this case goes
|
// about 70% of the methods start with 'get', so this case goes
|
||||||
// first
|
// first
|
||||||
@ -52,12 +71,15 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
|||||||
private int position;
|
private int position;
|
||||||
private Node node;
|
private Node node;
|
||||||
|
|
||||||
private static ConcurrentMap<Class<?>, MethodWrapper[]> methodCache =
|
private static final ConcurrentMap<Class<?>, MethodWrapper[]> METHOD_CACHE = new ConcurrentHashMap<>();
|
||||||
new ConcurrentHashMap<Class<?>, MethodWrapper[]>();
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterator that enumerates the attributes of the given node.
|
||||||
|
*/
|
||||||
public AttributeAxisIterator(Node contextNode) {
|
public AttributeAxisIterator(Node contextNode) {
|
||||||
this.node = contextNode;
|
this.node = contextNode;
|
||||||
if (!methodCache.containsKey(contextNode.getClass())) {
|
if (!METHOD_CACHE.containsKey(contextNode.getClass())) {
|
||||||
Method[] preFilter = contextNode.getClass().getMethods();
|
Method[] preFilter = contextNode.getClass().getMethods();
|
||||||
List<MethodWrapper> postFilter = new ArrayList<>();
|
List<MethodWrapper> postFilter = new ArrayList<>();
|
||||||
for (Method element : preFilter) {
|
for (Method element : preFilter) {
|
||||||
@ -65,17 +87,18 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
|||||||
postFilter.add(new MethodWrapper(element));
|
postFilter.add(new MethodWrapper(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
methodCache.putIfAbsent(contextNode.getClass(), postFilter.toArray(new MethodWrapper[0]));
|
METHOD_CACHE.putIfAbsent(contextNode.getClass(), postFilter.toArray(new MethodWrapper[0]));
|
||||||
}
|
}
|
||||||
this.methodWrappers = methodCache.get(contextNode.getClass());
|
this.methodWrappers = METHOD_CACHE.get(contextNode.getClass());
|
||||||
|
|
||||||
this.position = 0;
|
this.position = 0;
|
||||||
this.currObj = getNextAttribute();
|
this.currObj = getNextAttribute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attribute next() {
|
public Attribute next() {
|
||||||
if (currObj == null) {
|
if (!hasNext()) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
Attribute ret = currObj;
|
Attribute ret = currObj;
|
||||||
@ -83,16 +106,19 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return currObj != null;
|
return currObj != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Attribute getNextAttribute() {
|
private Attribute getNextAttribute() {
|
||||||
if (methodWrappers == null || position == methodWrappers.length) {
|
if (methodWrappers == null || position == methodWrappers.length) {
|
||||||
return null;
|
return null;
|
||||||
@ -102,20 +128,26 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static final Set<Class<?>> CONSIDERED_RETURN_TYPES
|
private static final Set<Class<?>> CONSIDERED_RETURN_TYPES
|
||||||
= new HashSet<>(Arrays.<Class<?>>asList(Integer.TYPE, Boolean.TYPE, Double.TYPE, String.class, Long.TYPE, Character.TYPE, Float.TYPE));
|
= new HashSet<>(Arrays.<Class<?>>asList(Integer.TYPE, Boolean.TYPE, Double.TYPE, String.class, Long.TYPE, Character.TYPE, Float.TYPE));
|
||||||
|
|
||||||
private static final Set<String> FILTERED_OUT_NAMES
|
private static final Set<String> FILTERED_OUT_NAMES
|
||||||
= new HashSet<>(Arrays.asList("toString", "getClass", "getXPathNodeName", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope"));
|
= new HashSet<>(Arrays.asList("toString", "getClass", "getXPathNodeName", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope"));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given method is an attribute accessor,
|
||||||
|
* in which case a corresponding Attribute will be added to
|
||||||
|
* the iterator.
|
||||||
|
*
|
||||||
|
* @param method The method to test
|
||||||
|
*/
|
||||||
protected boolean isAttributeAccessor(Method method) {
|
protected boolean isAttributeAccessor(Method method) {
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
|
|
||||||
return CONSIDERED_RETURN_TYPES.contains(method.getReturnType())
|
return CONSIDERED_RETURN_TYPES.contains(method.getReturnType())
|
||||||
&& method.getParameterTypes().length == 0
|
&& method.getParameterTypes().length == 0
|
||||||
&& !methodName.startsWith("jjt")
|
&& !methodName.startsWith("jjt")
|
||||||
&& !FILTERED_OUT_NAMES.contains(methodName);
|
&& !FILTERED_OUT_NAMES.contains(methodName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user