| /* | |
|  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. | |
|  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
|  * | |
|  * This code is free software; you can redistribute it and/or modify it | |
|  * under the terms of the GNU General Public License version 2 only, as | |
|  * published by the Free Software Foundation.  Oracle designates this | |
|  * particular file as subject to the "Classpath" exception as provided | |
|  * by Oracle in the LICENSE file that accompanied this code. | |
|  * | |
|  * This code is distributed in the hope that it will be useful, but WITHOUT | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | |
|  * version 2 for more details (a copy is included in the LICENSE file that | |
|  * accompanied this code). | |
|  * | |
|  * You should have received a copy of the GNU General Public License version | |
|  * 2 along with this work; if not, write to the Free Software Foundation, | |
|  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
|  * | |
|  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
|  * or visit www.oracle.com if you need additional information or have any | |
|  * questions. | |
| */ | |
| package java.lang; | |
| import jdk.internal.reflect.ReflectionFactory; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Modifier; | |
| import java.security.AccessController; | |
| import java.util.Arrays; | |
| import java.util.LinkedHashMap; | |
| import java.util.Map; | |
| /** | |
|  * A collection of most specific public methods. Methods are added to it using | |
|  * {@link #merge(Method)} method. Only the most specific methods for a | |
|  * particular signature are kept. | |
| */ | |
| final class PublicMethods { | |
|     /** | |
|      * a map of (method name, parameter types) -> linked list of Method(s) | |
| */ | |
| private final Map<Key, MethodList> map = new LinkedHashMap<>(); | |
|     /** | |
|      * keeps track of the number of collected methods | |
| */ | |
| private int methodCount; | |
|     /** | |
|      * Merges new method with existing methods. New method is either | |
|      * ignored (if a more specific method with same signature exists) or added | |
|      * to the collection. When it is added to the collection, it may replace one | |
|      * or more existing methods with same signature if they are less specific | |
|      * than added method. | |
|      * See comments in code... | |
| */ | |
| void merge(Method method) { | |
| Key key = new Key(method); | |
| MethodList existing = map.get(key); | |
| int xLen = existing == null ? 0 : existing.length(); | |
| MethodList merged = MethodList.merge(existing, method); | |
| methodCount += merged.length() - xLen; | |
|         // replace if head of list changed | |
| if (merged != existing) { | |
| map.put(key, merged); | |
| } | |
| } | |
|     /** | |
|      * Dumps methods to array. | |
| */ | |
| Method[] toArray() { | |
| Method[] array = new Method[methodCount]; | |
| int i = 0; | |
| for (MethodList ml : map.values()) { | |
|             for (; ml != null; ml = ml.next) { | |
| array[i++] = ml.method; | |
| } | |
| } | |
| return array; | |
| } | |
|     /** | |
|      * Method (name, parameter types) tuple. | |
| */ | |
|     private static final class Key { | |
| private static final ReflectionFactory reflectionFactory = | |
| AccessController.doPrivileged( | |
| new ReflectionFactory.GetReflectionFactoryAction()); | |
| private final String name; // must be interned (as from Method.getName()) | |
| private final Class<?>[] ptypes; | |
| Key(Method method) { | |
| name = method.getName(); | |
| ptypes = reflectionFactory.getExecutableSharedParameterTypes(method); | |
| } | |
| static boolean matches(Method method, | |
| String name, // may not be interned | |
| Class<?>[] ptypes) { | |
| return method.getName().equals(name) && | |
| Arrays.equals( | |
| reflectionFactory.getExecutableSharedParameterTypes(method), | |
| ptypes | |
| ); | |
| } | |
| @Override | |
| public boolean equals(Object o) { | |
| if (this == o) return true; | |
| if (!(o instanceof Key)) return false; | |
| Key that = (Key) o; | |
|             //noinspection StringEquality (guaranteed interned String(s)) | |
| return name == that.name && | |
| Arrays.equals(ptypes, that.ptypes); | |
| } | |
| @Override | |
|         public int hashCode() { | |
| return System.identityHashCode(name) + // guaranteed interned String | |
| 31 * Arrays.hashCode(ptypes); | |
| } | |
| } | |
|     /** | |
|      * Node of a inked list containing Method(s) sharing the same | |
|      * (name, parameter types) tuple. | |
| */ | |
|     static final class MethodList { | |
| Method method; | |
| MethodList next; | |
| private MethodList(Method method) { | |
| this.method = method; | |
| } | |
|         /** | |
|          * @return the head of a linked list containing given {@code methods} | |
|          *         filtered by given method {@code name}, parameter types | |
|          *         {@code ptypes} and including or excluding static methods as | |
|          *         requested by {@code includeStatic} flag. | |
| */ | |
| static MethodList filter(Method[] methods, String name, | |
| Class<?>[] ptypes, boolean includeStatic) { | |
| MethodList head = null, tail = null; | |
| for (Method method : methods) { | |
| if ((includeStatic || !Modifier.isStatic(method.getModifiers())) && | |
| Key.matches(method, name, ptypes)) { | |
| if (tail == null) { | |
| head = tail = new MethodList(method); | |
|                     } else { | |
| tail = tail.next = new MethodList(method); | |
| } | |
| } | |
| } | |
| return head; | |
| } | |
|         /** | |
|          * This method should only be called with the {@code head} (possibly null) | |
|          * of a list of Method(s) that share the same (method name, parameter types) | |
|          * and another {@code methodList} that also contains Method(s) with the | |
|          * same and equal (method name, parameter types) as the 1st list. | |
|          * It modifies the 1st list and returns the head of merged list | |
|          * containing only the most specific methods for each signature | |
|          * (i.e. return type). The returned head of the merged list may or | |
|          * may not be the same as the {@code head} of the given list. | |
|          * The given {@code methodList} is not modified. | |
| */ | |
| static MethodList merge(MethodList head, MethodList methodList) { | |
| for (MethodList ml = methodList; ml != null; ml = ml.next) { | |
| head = merge(head, ml.method); | |
| } | |
| return head; | |
| } | |
| private static MethodList merge(MethodList head, Method method) { | |
| Class<?> dclass = method.getDeclaringClass(); | |
| Class<?> rtype = method.getReturnType(); | |
| MethodList prev = null; | |
| for (MethodList l = head; l != null; l = l.next) { | |
|                 // eXisting method | |
| Method xmethod = l.method; | |
| // only merge methods with same signature: | |
| // (return type, name, parameter types) tuple | |
| // as we only keep methods with same (name, parameter types) | |
|                 // tuple together in one list, we only need to check return type | |
| if (rtype == xmethod.getReturnType()) { | |
| Class<?> xdclass = xmethod.getDeclaringClass(); | |
| if (dclass.isInterface() == xdclass.isInterface()) { | |
| // both methods are declared by interfaces | |
|                         // or both by classes | |
| if (dclass.isAssignableFrom(xdclass)) { | |
| // existing method is the same or overrides | |
|                             // new method - ignore new method | |
| return head; | |
| } | |
| if (xdclass.isAssignableFrom(dclass)) { | |
| // new method overrides existing | |
|                             // method - knock out existing method | |
| if (prev != null) { | |
| prev.next = l.next; | |
|                             } else { | |
| head = l.next; | |
| } | |
| // keep iterating | |
|                         } else { | |
|                             // unrelated (should only happen for interfaces) | |
| prev = l; | |
| // keep iterating | |
| } | |
| } else if (dclass.isInterface()) { | |
| // new method is declared by interface while | |
| // existing method is declared by class - | |
|                         // ignore new method | |
| return head; | |
|                     } else /* xdclass.isInterface() */ { | |
| // new method is declared by class while | |
| // existing method is declared by interface - | |
|                         // knock out existing method | |
| if (prev != null) { | |
| prev.next = l.next; | |
|                         } else { | |
| head = l.next; | |
| } | |
| // keep iterating | |
| } | |
|                 } else { | |
|                     // distinct signatures | |
| prev = l; | |
| // keep iterating | |
| } | |
| } | |
|             // append new method to the list | |
| if (prev == null) { | |
| head = new MethodList(method); | |
|             } else { | |
| prev.next = new MethodList(method); | |
| } | |
| return head; | |
| } | |
|         private int length() { | |
| int len = 1; | |
| for (MethodList ml = next; ml != null; ml = ml.next) { | |
| len++; | |
| } | |
| return len; | |
| } | |
|         /** | |
|          * @return 1st method in list with most specific return type | |
| */ | |
| Method getMostSpecific() { | |
| Method m = method; | |
| Class<?> rt = m.getReturnType(); | |
| for (MethodList ml = next; ml != null; ml = ml.next) { | |
| Method m2 = ml.method; | |
| Class<?> rt2 = m2.getReturnType(); | |
| if (rt2 != rt && rt.isAssignableFrom(rt2)) { | |
|                     // found more specific return type | |
| m = m2; | |
| rt = rt2; | |
| } | |
| } | |
| return m; | |
| } | |
| } | |
| } |