/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.ast.xpath;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.sourceforge.pmd.lang.ast.Node;
public class AttributeAxisIterator implements Iterator<Attribute> {
private static class MethodWrapper {
public Method method;
public String name;
MethodWrapper(Method m) {
this.method = m;
this.name = truncateMethodName(m.getName());
}
private String truncateMethodName(String n) {
// about 70% of the methods start with 'get', so this case goes
// first
if (n.startsWith("get")) {
return n.substring("get".length());
}
if (n.startsWith("is")) {
return n.substring("is".length());
}
if (n.startsWith("has")) {
return n.substring("has".length());
}
if (n.startsWith("uses")) {
return n.substring("uses".length());
}
return n;
}
}
private Attribute currObj;
private MethodWrapper[] methodWrappers;
private int position;
private Node node;
private static ConcurrentMap<Class<?>, MethodWrapper[]> methodCache =
new ConcurrentHashMap<Class<?>, MethodWrapper[]>();
public AttributeAxisIterator(Node contextNode) {
this.node = contextNode;
if (!methodCache.containsKey(contextNode.getClass())) {
Method[] preFilter = contextNode.getClass().getMethods();
List<MethodWrapper> postFilter = new ArrayList<>();
for (Method element : preFilter) {
if (isAttributeAccessor(element)) {
postFilter.add(new MethodWrapper(element));
}
}
methodCache.putIfAbsent(contextNode.getClass(), postFilter.toArray(new MethodWrapper[postFilter.size()]));
}
this.methodWrappers = methodCache.get(contextNode.getClass());
this.position = 0;
this.currObj = getNextAttribute();
}
@Override
public Attribute next() {
if (currObj == null) {
throw new IndexOutOfBoundsException();
}
Attribute ret = currObj;
currObj = getNextAttribute();
return ret;
}
@Override
public boolean hasNext() {
return currObj != null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private Attribute getNextAttribute() {
if (methodWrappers == null || position == methodWrappers.length) {
return null;
}
MethodWrapper m = methodWrappers[position++];
return new Attribute(node, m.name, m.method);
}
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);
}
}