AttributeAxisIterator.java
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 }