$FreeBSD$ --- ../../deploy/src/plugin/src/share/classes/sun/plugin/javascript/ReflectUtil.java 1 Jan 1970 00:00:00 -0000 +++ ../../deploy/src/plugin/src/share/classes/sun/plugin/javascript/ReflectUtil.java 3 Dec 2004 03:56:58 -0000 1.1 @@ -0,0 +1,307 @@ +/* + * @(#)ReflectUtil.java 1.1 04/06/20 + * + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package sun.plugin.javascript; + +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Iterator; +import sun.plugin.javascript.JSClassLoader; + +public class ReflectUtil { + /* + * Discover the public methods on public classes + * and interfaces accessible to the calling + * JavaScript code. + */ + public static Method[] getJScriptMethods(Class cls) { + List m = new ArrayList(); /* the valid methods we find */ + + /* + * Temporary map of method signatures when we decide + * that a simple call to target.getMethods() returns + * inaccessible methods and we must search for alternative + * supermethods that might be accessible. We can toss + * this when we're done searching. + */ + Map sigs = new HashMap(); + + while (cls != null) { + boolean done = getPublicMethods(cls, m, sigs); + if (done) { + break; + } + getJScriptInterfaceMethods(cls, m, sigs); + cls = cls.getSuperclass(); + } + return (Method[]) m.toArray(new Method[m.size()]); + } + + /* + * Process the immediate interfaces of this class or interface. + */ + private static void getJScriptInterfaceMethods(Class cls, List m, Map sigs) { + Class[] intfs = cls.getInterfaces(); + for (int i=0; i < intfs.length; i++) { + Class intf = intfs[i]; + boolean done = getPublicMethods(intf, m, sigs); + if (!done) { + getJScriptInterfaceMethods(intf, m, sigs); + } + } + } + + /* + * + * Process the methods in this class or interface + */ + private static boolean getPublicMethods(Class cls, List m, Map sigs) { + Method[] methods = null; + try { + + /* + * This class or interface is non-public so we + * can't use any of it's methods. Go back and + * try again with a superclass or superinterface. + */ + if (!Modifier.isPublic(cls.getModifiers())) { + return false; + } + + if (!JSClassLoader.isPackageAccessible(cls)) { + return false; + } + + methods = cls.getMethods(); + } catch (SecurityException se) { + return false; + } + + /* + * Check for inherited methods with non-public + * declaring classes. They might override and hide + * methods from their superclasses or + * superinterfaces. + */ + boolean done = true; + for (int i=0; i < methods.length; i++) { + Class dc = methods[i].getDeclaringClass(); + if (!Modifier.isPublic(dc.getModifiers())) { + done = false; + break; + } + } + + /* + * Belatedly initialize the signature map if + * this is not the first time here. + */ + if (sigs.isEmpty() && !m.isEmpty()) { + initSignatureMap(m, sigs); + } + + if (done) { + /* + * We're done. Spray all the methods into + * the list and then we're out of here. + */ + for (int i=0; i < methods.length; i++) { + addMethod(m, sigs, methods[i]); + } + } else { + /* + * Simulate cls.getDeclaredMethods() by + * stripping away inherited methods. + */ + for (int i=0; i < methods.length; i++) { + Class dc = methods[i].getDeclaringClass(); + if (cls.equals(dc)) { + addMethod(m, sigs, methods[i]); + } + } + } + return done; + } + + private static void initSignatureMap(List m, Map sigs) { + Iterator i = m.iterator(); + while (i.hasNext()) { + Method entry = (Method) i.next(); + sigs.put(getSignature(entry), entry); + } + } + + private static void addMethod(List m, Map sigs, Method method) { + /* + * Avoid work. We ignore the signature matching + * until the map is initialized in initSignatureMap. + * This has the effect of avoiding the signature + * work for the first call of getPublicMethods(). + */ + if (sigs.isEmpty()) { + m.add(method); + return; + } + + /* + * Avoid adding duplicate accessible methods on + * the list. + */ + String signature = getSignature(method); + if (!sigs.containsKey(signature)) { + m.add(method); + sigs.put(signature, method); + } + } + + /* + * Return a canonical method signature for the method. + * We care only about the simple method name and the + * the number, type and order of the parameters. + * Exception declarations are not part of a method + * signature nor is the return type. + */ + private static String getSignature(Method method) { + StringBuffer sb = new StringBuffer(); + + sb.append(method.getName()); + Class[] params = method.getParameterTypes(); + sb.append('('); + if (params.length > 0) { + sb.append(params[0].getName()); + } + for (int i=1; i < params.length; i++) { + sb.append(','); + sb.append(params[i].getName()); + } + sb.append(')'); + + return sb.toString(); + } + + /* + * Discover the public fields on public classes + * and interfaces accessible to the calling + * JavaScript code. + */ + public static Field[] getJScriptFields(Class cls) { + List m = new ArrayList(); /* the valid fields we find */ + + /* + * Temporary map of field name when we decide + * that a simple call to target.getFields() returns + * inaccessible fields and we must search for alternative + * supermethods that might be accessible. We can toss + * this when we're done searching. + */ + Map names = new HashMap(); + + while (cls != null) { + boolean done = getPublicFields(cls, m, names); + if (done) { + break; + } + getJScriptInterfaceFields(cls, m, names); + cls = cls.getSuperclass(); + } + return (Field[]) m.toArray(new Field[m.size()]); + } + + /* + * Process the immediate interfaces of this class or interface. + */ + private static void getJScriptInterfaceFields(Class cls, List m, Map names) { + Class[] intfs = cls.getInterfaces(); + for (int i=0; i < intfs.length; i++) { + Class intf = intfs[i]; + boolean done = getPublicFields(intf, m, names); + if (!done) { + getJScriptInterfaceFields(intf, m, names); + } + } + } + + /* + * + * Process the fields in this class or interface + */ + private static boolean getPublicFields(Class cls, List m, Map names) { + Field[] fields = null; + try { + + /* + * This class or interface is non-public so we + * can't use any of it's fields. Go back and + * try again with a superclass or superinterface. + */ + if (!Modifier.isPublic(cls.getModifiers())) { + return false; + } + + if (!JSClassLoader.isPackageAccessible(cls)) { + return false; + } + fields = cls.getFields(); + } catch (SecurityException se) { + return false; + } + + /* + * Check for inherited fields with non-public + * declaring classes. They might hide + * fields from public classes or interfaces. + */ + boolean done = true; + for (int i=0; i < fields.length; i++) { + Class dc = fields[i].getDeclaringClass(); + if (!Modifier.isPublic(dc.getModifiers())) { + done = false; + break; + } + } + if (done) { + /* + * We're done. Spray all the fields into + * the list and then we're out of here. + */ + for (int i=0; i < fields.length; i++) { + addField(m, names, fields[i]); + } + } else { + /* + * Simulate cls.getDeclaredFields() by + * stripping away inherited fields. + */ + for (int i=0; i < fields.length; i++) { + Class dc = fields[i].getDeclaringClass(); + if (cls.equals(dc)) { + addField(m, names, fields[i]); + } + } + } + return done; + } + + private static void addField(List m, Map names, Field field) { + /* + * Avoid adding duplicate accessible fields on + * the list. + */ + String name = field.getName(); + if (!names.containsKey(name)) { + m.add(field); + names.put(name, field); + } + } +} + +