001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.lang.ast.xpath;
005
006 import java.lang.reflect.Method;
007 import java.util.ArrayList;
008 import java.util.HashMap;
009 import java.util.Iterator;
010 import java.util.List;
011 import java.util.Map;
012
013 import net.sourceforge.pmd.lang.ast.Node;
014
015 public class AttributeAxisIterator implements Iterator<Attribute> {
016
017 private static class MethodWrapper {
018 public Method method;
019 public String name;
020
021 public MethodWrapper(Method m) {
022 this.method = m;
023 this.name = truncateMethodName(m.getName());
024 }
025
026 private String truncateMethodName(String n) {
027 // about 70% of the methods start with 'get', so this case goes first
028 if (n.startsWith("get")) {
029 return n.substring("get".length());
030 }
031 if (n.startsWith("is")) {
032 return n.substring("is".length());
033 }
034 if (n.startsWith("has")) {
035 return n.substring("has".length());
036 }
037 if (n.startsWith("uses")) {
038 return n.substring("uses".length());
039 }
040
041 return n;
042 }
043 }
044
045 private Attribute currObj;
046 private MethodWrapper[] methodWrappers;
047 private int position;
048 private Node node;
049
050 private static Map<Class<?>, MethodWrapper[]> methodCache = new HashMap<Class<?>, MethodWrapper[]>();
051
052 public AttributeAxisIterator(Node contextNode) {
053 this.node = contextNode;
054 if (!methodCache.containsKey(contextNode.getClass())) {
055 Method[] preFilter = contextNode.getClass().getMethods();
056 List<MethodWrapper> postFilter = new ArrayList<MethodWrapper>();
057 for (Method element : preFilter) {
058 if (isAttributeAccessor(element)) {
059 postFilter.add(new MethodWrapper(element));
060 }
061 }
062 methodCache.put(contextNode.getClass(), postFilter.toArray(new MethodWrapper[postFilter.size()]));
063 }
064 this.methodWrappers = methodCache.get(contextNode.getClass());
065
066 this.position = 0;
067 this.currObj = getNextAttribute();
068 }
069
070 public Attribute next() {
071 if (currObj == null) {
072 throw new IndexOutOfBoundsException();
073 }
074 Attribute ret = currObj;
075 currObj = getNextAttribute();
076 return ret;
077 }
078
079 public boolean hasNext() {
080 return currObj != null;
081 }
082
083 public void remove() {
084 throw new UnsupportedOperationException();
085 }
086
087 private Attribute getNextAttribute() {
088 if (position == methodWrappers.length) {
089 return null;
090 }
091 MethodWrapper m = methodWrappers[position++];
092 return new Attribute(node, m.name, m.method);
093 }
094
095 protected boolean isAttributeAccessor(Method method) {
096
097 String methodName = method.getName();
098
099 return (Integer.TYPE == method.getReturnType() || Boolean.TYPE == method.getReturnType()
100 || Double.TYPE == method.getReturnType() || String.class == method.getReturnType())
101 && method.getParameterTypes().length == 0
102 && Void.TYPE != method.getReturnType()
103 && !methodName.startsWith("jjt")
104 && !methodName.equals("toString")
105 && !methodName.equals("getScope")
106 && !methodName.equals("getClass")
107 && !methodName.equals("getTypeNameNode")
108 && !methodName.equals("getImportedNameNode") && !methodName.equals("hashCode");
109 }
110 }
|