1 package java.lang;
   2 
   3 import jdk.internal.reflect.ReflectionFactory;
   4 
   5 import java.lang.reflect.Method;
   6 import java.lang.reflect.Modifier;
   7 import java.security.AccessController;
   8 import java.util.Arrays;
   9 import java.util.LinkedHashMap;
  10 
  11 /**
  12  * A HashMap of public methods keyed by (methodName, parameterTypes) tuple
  13  * with values being {@link MethodList} objects.
  14  */
  15 class PublicMethods
  16     extends LinkedHashMap<PublicMethods.Key, PublicMethods.MethodList> {
  17 
  18     private static final long serialVersionUID = 1L;
  19 
  20     /**
  21      * keeps track of the number of collected methods
  22      */
  23     private int methodCount;
  24 
  25     /**
  26      * Consolidates new method with existing methods. New method is either
  27      * ignored (if a more specific method with same signature exists) or added
  28      * to the map. When it is added to the map, it may replace one or more
  29      * existing methods with same signature if they are less specific then added
  30      * method.
  31      * See comments in code...
  32      */
  33     void consolidate(Method method) {
  34         Key key = new Key(method);
  35         MethodList existing = get(key);
  36         int xLen = existing == null ? 0 : existing.length();
  37         MethodList consolidated = MethodList.consolidate(existing, method);
  38         methodCount += consolidated.length() - xLen;
  39         // replace if head of list changed
  40         if (consolidated != existing) {
  41             put(key, consolidated);
  42         }
  43     }
  44 
  45     /**
  46      * Dumps methods to array.
  47      */
  48     Method[] toArray() {
  49         Method[] array = new Method[methodCount];
  50         int i = 0;
  51         for (MethodList ml : values()) {
  52             for (; ml != null; ml = ml.next) {
  53                 array[i++] = ml.method;
  54             }
  55         }
  56         return array;
  57     }
  58 
  59     /**
  60      * Method (name, parameter types) tuple.
  61      */
  62     static final class Key {
  63         private static final ReflectionFactory reflectionFactory =
  64             AccessController.doPrivileged(
  65                 new ReflectionFactory.GetReflectionFactoryAction());
  66 
  67         private final String name; // must be interned (as from Method.getName())
  68         private final Class<?>[] ptypes;
  69 
  70         Key(Method method) {
  71             name = method.getName();
  72             ptypes = reflectionFactory.getExecutableSharedParameterTypes(method);
  73         }
  74 
  75         static boolean matches(Method method,
  76                                String name, // may not be interned
  77                                Class<?>[] ptypes) {
  78             return method.getName().equals(name) &&
  79                    Arrays.equals(
  80                        reflectionFactory.getExecutableSharedParameterTypes(method),
  81                        ptypes
  82                    );
  83         }
  84 
  85         @SuppressWarnings("StringEquality")
  86         @Override
  87         public boolean equals(Object o) {
  88             if (this == o) return true;
  89             if (o == null || getClass() != o.getClass()) return false;
  90             Key that = (Key) o;
  91             return name == that.name && // guaranteed interned String(s)
  92                    Arrays.equals(ptypes, that.ptypes);
  93         }
  94 
  95         @Override
  96         public int hashCode() {
  97             return System.identityHashCode(name) +
  98                    31 * Arrays.hashCode(ptypes);
  99         }
 100     }
 101 
 102     /**
 103      * Linked list node containing methods sharing the same
 104      * (name, parameter types) tuple.
 105      */
 106     static final class MethodList {
 107         Method method;
 108         MethodList next;
 109 
 110         private MethodList(Method method) {
 111             this.method = method;
 112         }
 113 
 114         static MethodList filter(Method[] methods, String name,
 115                                  Class<?>[] ptypes, boolean includeStatic) {
 116             MethodList head = null, tail = null;
 117             for (Method method : methods) {
 118                 if ((includeStatic || !Modifier.isStatic(method.getModifiers())) &&
 119                     Key.matches(method, name, ptypes)) {
 120                     if (tail == null) {
 121                         head = tail = new MethodList(method);
 122                     } else {
 123                         tail = tail.next = new MethodList(method);
 124                     }
 125                 }
 126             }
 127             return head;
 128         }
 129 
 130         static MethodList consolidate(MethodList head, MethodList methodList) {
 131             for (MethodList ml = methodList; ml != null; ml = ml.next) {
 132                 head = consolidate(head, ml.method);
 133             }
 134             return head;
 135         }
 136 
 137         private static MethodList consolidate(MethodList head, Method method) {
 138             Class<?> dclass = method.getDeclaringClass();
 139             Class<?> rtype = method.getReturnType();
 140             MethodList prev = null;
 141             for (MethodList l = head; l != null; l = l.next) {
 142                 // eXisting method
 143                 Method xmethod = l.method;
 144                 // only consolidate methods with same signature:
 145                 // (return type, name, parameter types) tuple
 146                 // as we only keep methods with same (name, parameter types)
 147                 // tuple together in one list, we only need to check return type
 148                 if (rtype == xmethod.getReturnType()) {
 149                     Class<?> xdclass = xmethod.getDeclaringClass();
 150                     if (dclass.isInterface() == xdclass.isInterface()) {
 151                         // both methods are declared by interfaces
 152                         // or both by classes
 153                         if (dclass.isAssignableFrom(xdclass)) {
 154                             // existing method is the same or overrides
 155                             // new method - ignore new method
 156                             return head;
 157                         }
 158                         if (xdclass.isAssignableFrom(dclass)) {
 159                             // new method overrides existing
 160                             // method - knock out existing method
 161                             if (prev != null) {
 162                                 prev.next = l.next;
 163                             } else {
 164                                 head = l.next;
 165                             }
 166                             // keep iterating
 167                         } else {
 168                             // unrelated (should only happen for interfaces)
 169                             prev = l;
 170                             // keep iterating
 171                         }
 172                     } else if (dclass.isInterface()) {
 173                         // new method is declared by interface while
 174                         // existing method is declared by class -
 175                         // ignore new method
 176                         return head;
 177                     } else /* xdclass.isInterface() */ {
 178                         // new method is declared by class while
 179                         // existing method is declared by interface -
 180                         // knock out existing method
 181                         if (prev != null) {
 182                             prev.next = l.next;
 183                         } else {
 184                             head = l.next;
 185                         }
 186                         // keep iterating
 187                     }
 188                 } else {
 189                     // distinct signatures
 190                     prev = l;
 191                     // keep iterating
 192                 }
 193             }
 194             // append new method to the list
 195             if (prev == null) {
 196                 head = new MethodList(method);
 197             } else {
 198                 prev.next = new MethodList(method);
 199             }
 200             return head;
 201         }
 202 
 203         int length() {
 204             int len = 1;
 205             for (MethodList ml = next; ml != null; ml = ml.next) {
 206                 len++;
 207             }
 208             return len;
 209         }
 210 
 211         /**
 212          * @return 1st method in list with most specific return type
 213          */
 214         Method getMostSpecific() {
 215             Method m = method;
 216             Class<?> rt = m.getReturnType();
 217             for (MethodList ml = next; ml != null; ml = ml.next) {
 218                 Method m2 = ml.method;
 219                 Class<?> rt2 = m2.getReturnType();
 220                 if (rt2 != rt && rt.isAssignableFrom(rt2)) {
 221                     // found more specific return type
 222                     m = m2;
 223                     rt = rt2;
 224                 }
 225             }
 226             return m;
 227         }
 228     }
 229 }