jdk/src/java.base/share/classes/java/lang/PublicMethods.java
changeset 42947 2453d65278f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/PublicMethods.java	Sun Dec 25 19:29:06 2016 +0100
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ *
+ * 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;
+        }
+    }
+}