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