1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.lang; 26 27 import jdk.internal.reflect.ReflectionFactory; 28 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Modifier; 31 import java.security.AccessController; 32 import java.util.Arrays; 33 import java.util.LinkedHashMap; 34 import java.util.Map; 35 36 /** 37 * A collection of most specific public methods. Methods are added to it using 38 * {@link #merge(Method)} method. Only the most specific methods for a 39 * particular signature are kept. 40 */ 41 final class PublicMethods { 42 43 /** 44 * a map of (method name, parameter types) -> linked list of Method(s) 45 */ 46 private final Map<Key, MethodList> map = new LinkedHashMap<>(); 47 48 /** 49 * keeps track of the number of collected methods 50 */ 51 private int methodCount; 52 53 /** 54 * Merges new method with existing methods. New method is either 55 * ignored (if a more specific method with same signature exists) or added 56 * to the collection. When it is added to the collection, it may replace one 57 * or more existing methods with same signature if they are less specific 58 * than added method. 59 * See comments in code... 60 */ 61 void merge(Method method) { 62 Key key = new Key(method); 63 MethodList existing = map.get(key); 64 int xLen = existing == null ? 0 : existing.length(); 65 MethodList merged = MethodList.merge(existing, method); 66 methodCount += merged.length() - xLen; 67 // replace if head of list changed 68 if (merged != existing) { 69 map.put(key, merged); 70 } 71 } 72 73 /** 74 * Dumps methods to array. 75 */ 76 Method[] toArray() { 77 Method[] array = new Method[methodCount]; 78 int i = 0; 79 for (MethodList ml : map.values()) { 80 for (; ml != null; ml = ml.next) { 81 array[i++] = ml.method; 82 } 83 } 84 return array; 85 } 86 87 /** 88 * Method (name, parameter types) tuple. 89 */ 90 private static final class Key { 91 private static final ReflectionFactory reflectionFactory = 92 AccessController.doPrivileged( 93 new ReflectionFactory.GetReflectionFactoryAction()); 94 95 private final String name; // must be interned (as from Method.getName()) 96 private final Class<?>[] ptypes; 97 98 Key(Method method) { 99 name = method.getName(); 100 ptypes = reflectionFactory.getExecutableSharedParameterTypes(method); 101 } 102 103 static boolean matches(Method method, 104 String name, // may not be interned 105 Class<?>[] ptypes) { 106 return method.getName().equals(name) && 107 Arrays.equals( 108 reflectionFactory.getExecutableSharedParameterTypes(method), 109 ptypes 110 ); 111 } 112 113 @Override 114 public boolean equals(Object o) { 115 if (this == o) return true; 116 if (!(o instanceof Key)) return false; 117 Key that = (Key) o; 118 //noinspection StringEquality (guaranteed interned String(s)) 119 return name == that.name && 120 Arrays.equals(ptypes, that.ptypes); 121 } 122 123 @Override 124 public int hashCode() { 125 return System.identityHashCode(name) + // guaranteed interned String 126 31 * Arrays.hashCode(ptypes); 127 } 128 } 129 130 /** 131 * Node of a inked list containing Method(s) sharing the same 132 * (name, parameter types) tuple. 133 */ 134 static final class MethodList { 135 Method method; 136 MethodList next; 137 138 private MethodList(Method method) { 139 this.method = method; 140 } 141 142 /** 143 * @return the head of a linked list containing given {@code methods} 144 * filtered by given method {@code name}, parameter types 145 * {@code ptypes} and including or excluding static methods as 146 * requested by {@code includeStatic} flag. 147 */ 148 static MethodList filter(Method[] methods, String name, 149 Class<?>[] ptypes, boolean includeStatic) { 150 MethodList head = null, tail = null; 151 for (Method method : methods) { 152 if ((includeStatic || !Modifier.isStatic(method.getModifiers())) && 153 Key.matches(method, name, ptypes)) { 154 if (tail == null) { 155 head = tail = new MethodList(method); 156 } else { 157 tail = tail.next = new MethodList(method); 158 } 159 } 160 } 161 return head; 162 } 163 164 /** 165 * This method should only be called with the {@code head} (possibly null) 166 * of a list of Method(s) that share the same (method name, parameter types) 167 * and another {@code methodList} that also contains Method(s) with the 168 * same and equal (method name, parameter types) as the 1st list. 169 * It modifies the 1st list and returns the head of merged list 170 * containing only the most specific methods for each signature 171 * (i.e. return type). The returned head of the merged list may or 172 * may not be the same as the {@code head} of the given list. 173 * The given {@code methodList} is not modified. 174 */ 175 static MethodList merge(MethodList head, MethodList methodList) { 176 for (MethodList ml = methodList; ml != null; ml = ml.next) { 177 head = merge(head, ml.method); 178 } 179 return head; 180 } 181 182 private static MethodList merge(MethodList head, Method method) { 183 Class<?> dclass = method.getDeclaringClass(); 184 Class<?> rtype = method.getReturnType(); 185 MethodList prev = null; 186 for (MethodList l = head; l != null; l = l.next) { 187 // eXisting method 188 Method xmethod = l.method; 189 // only merge methods with same signature: 190 // (return type, name, parameter types) tuple 191 // as we only keep methods with same (name, parameter types) 192 // tuple together in one list, we only need to check return type 193 if (rtype == xmethod.getReturnType()) { 194 Class<?> xdclass = xmethod.getDeclaringClass(); 195 if (dclass.isInterface() == xdclass.isInterface()) { 196 // both methods are declared by interfaces 197 // or both by classes 198 if (dclass.isAssignableFrom(xdclass)) { 199 // existing method is the same or overrides 200 // new method - ignore new method 201 return head; 202 } 203 if (xdclass.isAssignableFrom(dclass)) { 204 // new method overrides existing 205 // method - knock out existing method 206 if (prev != null) { 207 prev.next = l.next; 208 } else { 209 head = l.next; 210 } 211 // keep iterating 212 } else { 213 // unrelated (should only happen for interfaces) 214 prev = l; 215 // keep iterating 216 } 217 } else if (dclass.isInterface()) { 218 // new method is declared by interface while 219 // existing method is declared by class - 220 // ignore new method 221 return head; 222 } else /* xdclass.isInterface() */ { 223 // new method is declared by class while 224 // existing method is declared by interface - 225 // knock out existing method 226 if (prev != null) { 227 prev.next = l.next; 228 } else { 229 head = l.next; 230 } 231 // keep iterating 232 } 233 } else { 234 // distinct signatures 235 prev = l; 236 // keep iterating 237 } 238 } 239 // append new method to the list 240 if (prev == null) { 241 head = new MethodList(method); 242 } else { 243 prev.next = new MethodList(method); 244 } 245 return head; 246 } 247 248 private int length() { 249 int len = 1; 250 for (MethodList ml = next; ml != null; ml = ml.next) { 251 len++; 252 } 253 return len; 254 } 255 256 /** 257 * @return 1st method in list with most specific return type 258 */ 259 Method getMostSpecific() { 260 Method m = method; 261 Class<?> rt = m.getReturnType(); 262 for (MethodList ml = next; ml != null; ml = ml.next) { 263 Method m2 = ml.method; 264 Class<?> rt2 = m2.getReturnType(); 265 if (rt2 != rt && rt.isAssignableFrom(rt2)) { 266 // found more specific return type 267 m = m2; 268 rt = rt2; 269 } 270 } 271 return m; 272 } 273 } 274 }