View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.ast.xpath;
5   
6   import java.lang.reflect.Method;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.Iterator;
10  import java.util.List;
11  import java.util.Map;
12  
13  import net.sourceforge.pmd.lang.ast.Node;
14  
15  public class AttributeAxisIterator implements Iterator<Attribute> {
16  
17      private static class MethodWrapper {
18  	public Method method;
19  	public String name;
20  
21  	public MethodWrapper(Method m) {
22  	    this.method = m;
23  	    this.name = truncateMethodName(m.getName());
24  	}
25  
26  	private String truncateMethodName(String n) {
27  	    // about 70% of the methods start with 'get', so this case goes first
28  	    if (n.startsWith("get")) {
29  		return n.substring("get".length());
30  	    }
31  	    if (n.startsWith("is")) {
32  		return n.substring("is".length());
33  	    }
34  	    if (n.startsWith("has")) {
35  		return n.substring("has".length());
36  	    }
37  	    if (n.startsWith("uses")) {
38  		return n.substring("uses".length());
39  	    }
40  
41  	    return n;
42  	}
43      }
44  
45      private Attribute currObj;
46      private MethodWrapper[] methodWrappers;
47      private int position;
48      private Node node;
49  
50      private static Map<Class<?>, MethodWrapper[]> methodCache = new HashMap<Class<?>, MethodWrapper[]>();
51  
52      public AttributeAxisIterator(Node contextNode) {
53  	this.node = contextNode;
54  	if (!methodCache.containsKey(contextNode.getClass())) {
55  	    Method[] preFilter = contextNode.getClass().getMethods();
56  	    List<MethodWrapper> postFilter = new ArrayList<MethodWrapper>();
57  	    for (Method element : preFilter) {
58  		if (isAttributeAccessor(element)) {
59  		    postFilter.add(new MethodWrapper(element));
60  		}
61  	    }
62  	    methodCache.put(contextNode.getClass(), postFilter.toArray(new MethodWrapper[postFilter.size()]));
63  	}
64  	this.methodWrappers = methodCache.get(contextNode.getClass());
65  
66  	this.position = 0;
67  	this.currObj = getNextAttribute();
68      }
69  
70      public Attribute next() {
71  	if (currObj == null) {
72  	    throw new IndexOutOfBoundsException();
73  	}
74  	Attribute ret = currObj;
75  	currObj = getNextAttribute();
76  	return ret;
77      }
78  
79      public boolean hasNext() {
80  	return currObj != null;
81      }
82  
83      public void remove() {
84  	throw new UnsupportedOperationException();
85      }
86  
87      private Attribute getNextAttribute() {
88  	if (position == methodWrappers.length) {
89  	    return null;
90  	}
91  	MethodWrapper m = methodWrappers[position++];
92  	return new Attribute(node, m.name, m.method);
93      }
94  
95      protected boolean isAttributeAccessor(Method method) {
96  
97  	String methodName = method.getName();
98  
99  	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 }