Index: AbstractProviderConfiguration.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.runtime.common.core/src/org/eclipse/gmf/runtime/common/core/service/AbstractProviderConfiguration.java,v retrieving revision 1.3 diff -u -r1.3 AbstractProviderConfiguration.java --- AbstractProviderConfiguration.java 8 Nov 2005 19:37:06 -0000 1.3 +++ AbstractProviderConfiguration.java 9 Dec 2005 14:54:52 -0000 @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * IBM Corporation - initial API and implementation + * IBM Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.runtime.common.core.service; @@ -22,12 +22,16 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; +import java.util.WeakHashMap; +import java.util.Map.Entry; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.Platform; import org.eclipse.gmf.runtime.common.core.internal.CommonCorePlugin; import org.eclipse.gmf.runtime.common.core.internal.CommonCoreStatusCodes; +import org.eclipse.gmf.runtime.common.core.util.HashUtil; import org.eclipse.gmf.runtime.common.core.util.Log; import org.osgi.framework.Bundle; @@ -38,8 +42,8 @@ *

* This abstract class contains a set of useful utilities for such concrete * subclasses. - * - * @author melaasar, mmostafa + * + * @author melaasar, mmostafa, keithc * @canBeSeenBy %partners */ public class AbstractProviderConfiguration { @@ -47,37 +51,37 @@ * The name of the 'object' XML attribute. */ protected static final String OBJECT = "object"; //$NON-NLS-1$ - + /** * The name of the 'id' XML attribute. */ protected static final String ID = "id"; //$NON-NLS-1$ - + /** * The name of the 'class' XML attribute. */ protected static final String CLASS = "class"; //$NON-NLS-1$ - + /** * The name of the 'method' XML attribute. */ protected static final String METHOD = "method"; //$NON-NLS-1$ - + /** * The name of the 'method' XML attribute. */ protected static final String STATIC_METHOD = "staticMethod"; //$NON-NLS-1$ - + /** * The name of the 'name' XML attribute. */ protected static final String NAME = "name"; //$NON-NLS-1$ - + /** * The name of the 'value' XML attribute. */ protected static final String VALUE = "value"; //$NON-NLS-1$ - + /** * The name of the 'notValue' XML attribute. */ @@ -87,171 +91,68 @@ * The name of the 'null' XML attribute value. */ protected static final String NULL = "null"; //$NON-NLS-1$ - + /** * the name of the context param */ protected static final String contextParam = "%Context"; //$NON-NLS-1$ /** - * A map to store previously successful class lookups. - */ - private static Map isAssignableTable = new HashMap(); - - /** - * A map to store previously failed class lookups. - */ - private static Map isNotAssignableTable = new HashMap(); - - /** - * a map of classes that get asked for methods they do not contain, by - * the provider, the map is a class to a Set of method signatures - */ - private static ClassToMethodSignaturesSetMap passiveClasses = - new ClassToMethodSignaturesSetMap(); - - /** - * a class to cach passive classes, passive classes are the classes we asked - * for a method with a specific signature and they faild to find it. The cach used - * so in the next time we can tell if the method does not exists oin the class - * without calling getMethod by reflection, which improves the performance - * @author mmostafa - * - */ - private static class ClassToMethodSignaturesSetMap{ - - /** - * internal map for the cach, it is a map of Class to Set of method signature Strings - */ - Map classToMethodSignaturesSetMap = new HashMap(); - - /** - * adds a class and a method signature to the passive class cach - * @param clazz the class - * @param signature the method signature - */ - public void addMethod(Class clazz, String signature){ - Set signatures = (Set)classToMethodSignaturesSetMap.get(clazz); - if (signatures==null){ - signatures = new HashSet(); - classToMethodSignaturesSetMap.put(clazz,signatures); - } - signatures.add(signature); - } - - /** - * check if the class and the method signatrue are contained in the apssive collection, - * which means we do need need to call get method oon the class becuase we will not - * find it, this helps improving the performance. - * @param clazz - * @param signature - * @return - */ - public boolean contains(Class clazz, String signature){ - Set signatures = (Set)classToMethodSignaturesSetMap.get(clazz); - if (signatures==null) - return false; - return signatures.contains(signature); - } - } - - /** - * internal class used to cach Methods, so we do not call getMethod too often - * @author mmostafa + * A map to cache known information about class relationships. The first + * level key is keyed by a Class and yields a second-level map. The + * second-level map is keyed by a class name. The leaf values are Boolean + * objects whose values signify whether the Class is assignable to the named + * class. * + * We use a WeakHashMap to allow classes to be unloaded. */ - private static class ClassToMethodSignatureToMethodCach{ - - /** - * internal map to hold the cached data, it is a map of Class => Map - * of Singature string => method - */ - Map classToMethodSignatureToMethod = new HashMap(); - - /** - * adds a Method to the cach - * @param clazz the class we got the method from - * @param methodSignature the method signature - * @param method the Method - */ - public void addMethod(Class clazz,String methodSignature, Method method ){ - Map signatureToMethodMap = (Map)classToMethodSignatureToMethod.get(clazz); - if (signatureToMethodMap==null){ - signatureToMethodMap = new HashMap(); - classToMethodSignatureToMethod.put(clazz,signatureToMethodMap); - } - signatureToMethodMap.put(methodSignature,method); - } - - /** - * gets a method from the cach using the class that owns it and the method - * signature. - * @param clazz the class that owns the method - * @param methodSignature the method signature - * @return the Method if found any, otherwise null - */ - public Method getMethod(Class clazz,String methodSignature){ - Map signatureToMethodMap = (Map)classToMethodSignatureToMethod.get(clazz); - if (signatureToMethodMap !=null){ - return (Method)signatureToMethodMap.get(methodSignature); - } - return null; - } - - } - - /** - * map for class to Method signature to method cach - */ - private static ClassToMethodSignatureToMethodCach - classToMethodSignatureToMethodCach = new ClassToMethodSignatureToMethodCach(); - - - - /** - * Gets the class name of object. - * @param object the object for which the class name is to be found. - * @return the class name - */ - static String getClassName( Object object ) { - String cn = object.getClass().getName(); - return cn.substring( cn.lastIndexOf('.')+1); - } - + private static final Map assignableMap = new WeakHashMap(); + /** * A descriptor for an XML configuration element that identifies a class by * name and optionally its methods. */ public static class ObjectDescriptor { - /** + + private static final MethodValueEntry[] NO_METHODS = new MethodValueEntry[0]; + + private static boolean allMatch(MethodValueEntry[] methods, Object object) { + for (int i = 0, n = methods.length; i < n; ++i) + if (methods[i].matches(object) == false) + return false; + + return true; + } + + /** * The name of the class. */ - private String contextClassName; - + private final String contextClassName; + /** * The ID of the plugin that contains the class. */ - private String contextClassPlugin; - + private final String contextClassPlugin; + /** * true if a syntax error has occurred, * false otherwise. - */ - private boolean syntaxError; - + */ + private final boolean syntaxError; + /** * A list of method descriptors for the class. */ - private final List methods; - + private final MethodValueEntry[] methods; + /** * A list of method descriptors for the class. */ - private final List staticMethods; + private final MethodValueEntry[] staticMethods; /** * Creates a new object descriptor from its configuration element. - * + * * @param configElement * The configuration element. */ @@ -261,7 +162,7 @@ /** * Creates a new object descriptor from its configuration element. - * + * * @param configElement * The configuration element. * @param classNameTag @@ -270,88 +171,102 @@ public ObjectDescriptor( IConfigurationElement configElement, String classNameTag) { + super(); - String s = configElement.getAttribute(classNameTag); - if (s != null) { - int start = s.indexOf("(");//$NON-NLS-1$ - if (start != -1) { - contextClassName = s.substring(0, start).trim(); - int end = s.indexOf(")");//$NON-NLS-1$ - if (end != -1 && end > start+1) - contextClassPlugin = s.substring(start+1, end); - } else - contextClassName = s.trim(); - } - - IConfigurationElement[] methodConfigs = - configElement.getChildren(METHOD); - - IConfigurationElement[] staticMethodConfigs = - configElement.getChildren(STATIC_METHOD); - - if (methodConfigs.length != 0) { - methods = new ArrayList(methodConfigs.length); - for (int i = 0; i < methodConfigs.length; i++) { - String name = methodConfigs[i].getAttribute(NAME); - if (name != null) { - try { - MethodDescriptor methodDescriptor = - new MethodDescriptor(name); - methodDescriptor.setCall(name.intern()); - ValueDescriptor value = - new ValueDescriptor(methodConfigs[i]); - if (value != null) - methods.add(new MethodValueEntry(methodDescriptor, value)); - } catch (Exception e) { - syntaxError = true; - Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, configElement.getDeclaringExtension().getNamespace()+ ".plugin.xml extension [" + configElement.getDeclaringExtension().getExtensionPointUniqueIdentifier() + "]: invalid syntax for method [" + name + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } else { - syntaxError = true; - Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, configElement.getDeclaringExtension().getNamespace()+ ".plugin.xml extension [" + configElement.getDeclaringExtension().getExtensionPointUniqueIdentifier() + "] : missing method name"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } + String className = null; + String classPlugin = null; + String s = configElement.getAttribute(classNameTag); + + if (s != null) { + int start = s.indexOf('(') + 1; + if (start == 0) { + className = s; + } else { + className = s.substring(0, start - 1); + int end = s.indexOf(')', start); + if (end != -1) + classPlugin = s.substring(start + 1, end).trim().intern(); } - } - else - methods = Collections.EMPTY_LIST; - - - if (staticMethodConfigs.length != 0) { - staticMethods = new ArrayList(staticMethodConfigs.length); - for (int i = 0; i < staticMethodConfigs.length; i++) { - String name = staticMethodConfigs[i].getAttribute(NAME); - if (name != null) { - try { - StaticMethodDescriptor methodDescriptor = - new StaticMethodDescriptor(name); - methodDescriptor.setCall(name.intern()); - ValueDescriptor value = - new ValueDescriptor(staticMethodConfigs[i]); - if (value != null) - staticMethods.add(new MethodValueEntry(methodDescriptor, value)); - } catch (Exception e) { - syntaxError = true; - Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, configElement.getDeclaringExtension().getNamespace()+ ".plugin.xml extension [" + configElement.getDeclaringExtension().getExtensionPointUniqueIdentifier() + "]: invalid syntax for method [" + name + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } else { - syntaxError = true; - Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, configElement.getDeclaringExtension().getNamespace()+ ".plugin.xml extension [" + configElement.getDeclaringExtension().getExtensionPointUniqueIdentifier() + "] : missing method name"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + className = className.trim().intern(); + } + + contextClassName = className; + contextClassPlugin = classPlugin; + + methods = getMethods(configElement, false); + staticMethods = getMethods(configElement, true); + + syntaxError = anyNull(methods) || anyNull(staticMethods); + } + + private static boolean anyNull(MethodValueEntry[] entries) { + for (int i = entries.length; --i >= 0;) + if (entries[i] == null) + return true; + + return false; + } + + private static MethodValueEntry[] getMethods( + IConfigurationElement config, + boolean isStatic) { + + IConfigurationElement[] elements; + MethodValueEntry[] entries; + int count; + + elements = config.getChildren(isStatic ? STATIC_METHOD : METHOD); + count = elements.length; + + if (count == 0) { + entries = NO_METHODS; + } else { + entries = new MethodValueEntry[count]; + + for (int i = 0; i < count; ++i) { + IConfigurationElement element = elements[i]; + String name = element.getAttribute(NAME); + + if (name == null) { + logException(config, "missing method name"); //$NON-NLS-1$ + continue; + } + + try { + MethodDescriptor method; + + if (isStatic) + method = StaticMethodDescriptor.create(name); + else + method = new MethodDescriptor(name); + + ValueDescriptor value = new ValueDescriptor(element); + entries[i] = new MethodValueEntry(method, value); + } catch (Exception e) { + logException(config, + "invalid syntax for method [" + name + ']'); //$NON-NLS-1$ } } - }else - staticMethods = Collections.EMPTY_LIST; - - - - if (contextClassName != null) - contextClassName = contextClassName.intern(); - if (contextClassPlugin != null) - contextClassPlugin = contextClassPlugin.intern(); + } + + return entries; + } + + private static void logException(IConfigurationElement element, String message) { + IExtension extension = element.getDeclaringExtension(); + Log.error( + CommonCorePlugin.getDefault(), + CommonCoreStatusCodes.SERVICE_FAILURE, + extension.getNamespace() + + ".plugin.xml extension [" //$NON-NLS-1$ + + extension.getExtensionPointUniqueIdentifier() + + "]: " //$NON-NLS-1$ + + message); } /** * Tests if the object descriptor applies to the given context object. - * + * * @param object * The context object. * @return true if it applies; false @@ -361,32 +276,143 @@ if (syntaxError) return false; - Object targetObject = object; - if (contextClassName != null) { + if (contextClassName != null && object != null) { if (!isAssignableTo(object.getClass(), contextClassName)) { - targetObject = getAdapter(object, contextClassName, contextClassPlugin); - if (targetObject == null) + object = getAdapter(object, contextClassName, contextClassPlugin); + if (object == null) return false; } - } - - for(Iterator iter = methods.iterator(); iter.hasNext();) { - MethodValueEntry entry = (MethodValueEntry)iter.next(); - Object methodValue = invokeMethod(entry.method, targetObject); - - if (methodValue == null || !entry.value.sameAs(methodValue)) - return false; } - - for(Iterator iter = staticMethods.iterator(); iter.hasNext();) { - MethodValueEntry entry = (MethodValueEntry)iter.next(); - Object methodValue = invokeStaticMethod((StaticMethodDescriptor)entry.method, targetObject); - - if (methodValue == null || !entry.value.sameAs(methodValue)) - return false; + + return allMatch(methods, object) + ? allMatch(staticMethods, object) + : false; + } + } + + /** + * A simple string parser used to decompose method and value descriptors. + * + * @author keithc + */ + private static final class SimpleParser { + + private final String input; + + private int offset; + + public SimpleParser(String input) { + super(); + this.input = input; + this.offset = 0; + } + + /** + * Return the next portion of the input string up to but not including + * the given delimiter character. + * + * @param delimiter + * the delimiter + * @param delimiterRequired + * is the delimiter required? + * @return String the token + * @throws IllegalArgumentException + * if the delimiter is required but not found + */ + public String nextToken(char delimiter, boolean delimiterRequired) { + int start = offset; + int end = safeIndexOf(delimiter); + + if (end >= 0) { + offset = end + 1; + } else { + if (delimiterRequired) + throw new IllegalArgumentException(); + + end = input.length(); + offset = end; + } + + return unescape(start, end, delimiter); + } + + /** + * Return the next portion of the input string up to but not including + * the last occurence of the first delimiter character that appears + * before the next occurence of the second delimiter character. + * + * @param delim1 + * the first delimiter character + * @param delim2 + * the second delimiter character + * @return String the token + * @throws IllegalArgumentException + * if the delimiters are not found + */ + public String nextTokenBefore(char delim1, char delim2) { + int start = offset; + int end = safeIndexOf(delim2); + if (end < 0) + throw new IllegalArgumentException(); + + end = safeLastIndexOf(delim1, end); + offset = end + 1; + + return unescape(start, end, delim1); + } + + public boolean moreInput() { + return offset < input.length(); + } + + private int safeIndexOf(char delim) { + for (int start = offset;;) { + int index = input.indexOf(delim, start); + if (index <= start || input.charAt(index - 1) != '\\') + return index; + start = index + 1; + } + } + + private int safeLastIndexOf(char delim, int end) { + for (;;) { + int index = input.lastIndexOf(delim, end); + if (index < offset) + throw new IllegalArgumentException(); + if (index == offset || input.charAt(index - 1) != '\\') + return index; + end = index = 1; } - - return true; + } + + public String tail() { + return input.substring(offset); + } + + private String unescape(int start, int end, char delimiter) { + StringBuffer buffer = null; + + // don't consider the last character in the loop + end -= 1; + + for (int index = start; index < end; ++index) { + char c = input.charAt(index); + + if (c != '\\' || input.charAt(index + 1) != delimiter) + continue; + + if (buffer == null) + buffer = new StringBuffer(input.substring(0, index)); + else + buffer.append(input.substring(start, index)); + } + + String result = input.substring(start, end + 1); + + if (buffer != null) + result = buffer.append(result).toString(); + + return result; } } @@ -395,47 +421,45 @@ * name and its formal parameters. */ private static class MethodDescriptor { - + + /** + * An empty array of types and values for methods with no parameters. + */ + private static final Class[] NO_PARAMETERS = new Class[0]; + /** - * The method call. + * A cache for information about methods. Keyed by (class, method + * signature), the corresponding value is either a Method or + * Boolean.FALSE signifying that no accessible method exists. + * + * We use a WeakHashMap to allow classes to be unloaded. */ - private String call; - + private static final Map methodCache = new WeakHashMap(); + /** * The method name. */ - private String name; - + protected final String name; + /** * The array of method parameters. */ - private Object parameterObjects[]; - + protected final Object[] parameterObjects; + /** * The array of method parameter types. */ - private Class parameterTypes[]; - + protected final Class[] parameterTypes; + /** * The next cascading method descriptor. */ - private MethodDescriptor next; + protected final MethodDescriptor next; /** - * The list of method parameters. - */ - private List parameters; - - /** * the method signature - * */ - private String signature = null; - - - protected MethodDescriptor(){ - // empty - } + private final MethodSignature signature; /** * Creates a new method descriptor from a string representing the @@ -447,293 +471,220 @@ *

* Where: *

*

* For example: *

* getPropertyValue(Source_Connection).getName() - * + * * @param string * the method invocation string */ public MethodDescriptor(String string) { - // set method name - string = parseName(string.trim()); + super(); + + SimpleParser parser = new SimpleParser(string); + + name = parser.nextToken('(', true).trim().intern(); + // set method parameters - string = parseParameterList(string.trim()); + List parameters = new ArrayList(); + String parmString = parser.nextToken(')', true).trim(); + SimpleParser parmParser = new SimpleParser(parmString); + + while (parmParser.moreInput()) + parameters.add(parmParser.nextToken(',', false)); // fill the parameter objects and types arrays - if (parameters != null && !parameters.isEmpty()) { - Collections.reverse(parameters); - parameterObjects = parameters.toArray(); - parameterTypes = new Class[parameterObjects.length]; - for (int i = 0; i < parameterObjects.length; i++) { - String p = (String) parameterObjects[i]; - int objIndex = p.indexOf("[object]"); //$NON-NLS-1$ - boolean isObject = objIndex >= 0; - int parseAsIndex = p.indexOf(":::"); //$NON-NLS-1$ - try { - if (isObject && (parseAsIndex >= 0)) - // assume order: [object] before type:::param - assert (objIndex < parseAsIndex); - if (parseAsIndex >= 0) { - // "type:::param" - String parseAs = - p.substring((isObject ? 8 : 0), parseAsIndex); - String value = - p.substring(parseAsIndex + 3, p.length()); - if (parseAs.equalsIgnoreCase("int")) { //$NON-NLS-1$ - parameterTypes[i] = Integer.class; - parameterObjects[i] = Integer.decode(value); - } else if (parseAs.equalsIgnoreCase("bool")) { //$NON-NLS-1$ - parameterTypes[i] = Boolean.class; - parameterObjects[i] = Boolean.valueOf(value); - } else if (parseAs.equalsIgnoreCase("double")) { //$NON-NLS-1$ - parameterTypes[i] = Double.class; - parameterObjects[i] = Double.valueOf(value); - } - // if [object] present, set type to Object - if (isObject) - parameterTypes[i] = Object.class; - } else if (isObject) { // "[object]param" - String value = p.substring(8, p.length()); - parameterTypes[i] = Object.class; - parameterObjects[i] = value; - } else // "param" - parameterTypes[i] = String.class; - } catch (Exception e) { - String value = - p.substring( - ((parseAsIndex >= 0) ? parseAsIndex + 3 : 0), - p.length()); - parameterObjects[i] = value; - parameterTypes[i] = String.class; + int parmCount = parameters.size(); + + if (parmCount == 0) { + parameterObjects = NO_PARAMETERS; + parameterTypes = NO_PARAMETERS; + } else { + parameterObjects = new Object[parmCount]; + parameterTypes = new Class[parmCount]; + for (int index = 0; index < parmCount; ++index) + setParameterAndType((String) parameters.get(index), index); + } + + string = parser.tail().trim(); + + if (string.length() == 0) + next = null; + else if (string.charAt(0) == '.') + next = new MethodDescriptor(string.substring(1).trim()); + else + throw new IllegalArgumentException(); + + signature = new MethodSignature(this); + } + + protected void setParameterAndType(String parameter, int index) { + boolean isObject = parameter.startsWith("[object]"); //$NON-NLS-1$ + int parseAsIndex = parameter.indexOf(":::"); //$NON-NLS-1$ + Class clazz; + Object object; + + try { + if (parseAsIndex >= 0) { // "type:::param" + String type = parameter.substring(isObject ? 8 : 0, parseAsIndex); + String value = parameter.substring(parseAsIndex + 3); + + if (type.equalsIgnoreCase("int")) { //$NON-NLS-1$ + clazz = Integer.class; + object = Integer.decode(value); + } else if (type.equalsIgnoreCase("bool")) { //$NON-NLS-1$ + clazz = Boolean.class; + object = Boolean.valueOf(value); + } else if (type.equalsIgnoreCase("double")) { //$NON-NLS-1$ + clazz = Double.class; + object = Double.valueOf(value); + } else { + clazz = Object.class; + object = value; } + // if [object] present, set type to Object + if (isObject) + clazz = Object.class; + } else if (isObject) { // "[object]param" + clazz = Object.class; + object = parameter.substring(8, parameter.length()); + } else { // "param" + clazz = String.class; + object = parameter; + } + } catch (Exception e) { + if (parseAsIndex >= 0) + object = parameter.substring(parseAsIndex + 3); + else + object = parameter; + clazz = String.class; + } + + parameterTypes[index] = clazz; + parameterObjects[index] = object; + } + + protected final Method getMethod(Class clazz) { + Map classMap = getSubMap(methodCache, clazz); + Object candidate = classMap.get(signature); + Method method; + + if (candidate instanceof Method) { + method = (Method) candidate; + } else if (candidate == null) { + try { + method = clazz.getMethod(name, parameterTypes); + classMap.put(signature, method); + } catch (Exception e) { + classMap.put(signature, Boolean.FALSE); + method = null; } + } else { + method = null; } - parameters = null; - // set method parameters - if (string.length() != 0) { - if (string.charAt(0) != '.') - throw new IllegalArgumentException(); - next = new MethodDescriptor(string.substring(1).trim()); - } - - if (this.name != null) - name = name.intern(); + return method; } - /** - * Parses and returns the method name in a method invocation string. - * - * @param string - * the method invocation string - * @return the method name - */ - protected String parseName(String string) { - int index = string.indexOf('('); - if (index == -1) - throw new IllegalArgumentException(); //$NON-NLS-1$ - name = string.substring(0, index).trim(); - return string.substring(index + 1); - } + public Object invoke(Object object) { + for (MethodDescriptor desc = this; object != null;) { + Method method = getMethod(object.getClass()); - /** - * Parses a method invocation string for the list of parameters, which - * are placed in the parameters field. - * - * @param string - * the method invocation string - * @return the end part of the method invocation string that has not - * been parsed. - */ - protected String parseParameterList(String string) { - int index = -1; - String paramStr = null; - while (paramStr == null) { - index = string.indexOf(')', index + 1); - if (index == -1) - throw new IllegalArgumentException(); //$NON-NLS-1$ - if (index == 0 || string.charAt(index - 1) != '\\') - paramStr = string.substring(0, index); - } - if (paramStr.length() != 0) { - parameters = new ArrayList(); - parseParameters(paramStr.trim()); + if (method == null) + break; + + try { + object = method.invoke(object, desc.parameterObjects); + } catch (Exception e) { + break; + } + + if ((desc = desc.next) == null) + return object; } - return string.substring(index + 1); - } - /** - * Parses a string containing a list of method parameters and stores - * them in the parameters field. - * - * @param string - * the comma-separated list of method parameters. - */ - private void parseParameters(String string) { - int index = string.indexOf(','); - if (index != -1 && string.charAt(index - 1) != '\\') { - parseParameters(string.substring(index + 1).trim()); - parameters.add(string.substring(0, index)); - } else - parameters.add(string); + return null; } + } - /** - * Returns the method name. - * - * @return the method name - */ - public String getName() { - return name; - } - - /** - * Sets the method name. - * @param the method name - */ - public void setName(String name) { - this.name = name; - } + private static final class MethodSignature { - /** - * Returns an array of string params. - * - * @return the parameters - */ - public Object[] getParameters() { - return parameterObjects; - } + private final int hashCode; + private final MethodDescriptor method; - /** - * Returns an array of parameter classes. - * - * @return the parameter types - */ - public Class[] getParameterTypes() { - return parameterTypes; - } - - /** - * sets the the array of params. - * @param paramters - */ - protected void setParameters(Object[] paramters) { - parameterObjects = paramters; - } + public MethodSignature(MethodDescriptor method) { + super(); - /** - * sets the the array of parameter types. - * @param paramtersTypes - */ - public void setParameterTypes(Class[] paramterTypes) { - this.parameterTypes = paramterTypes; - } + int hash = method.name.hashCode(); + Class[] types = method.parameterTypes; - /** - * Returns the next cascading method descriptor, if any. - * - * @return the next method descriptor, or null if there - * is none - */ - public MethodDescriptor getNext() { - return next; - } - - /** - * sets the next cascading method descriptor, if any. - * @param next - */ - protected void setNext(MethodDescriptor next) { - this.next = next; - } + for (int i = 0, n = types.length; i < n; ++i) + hash = HashUtil.hash(hash, types[i]); - /** - * Gets the method call. - * - * @return the method call. - */ - public String getCall() { - return call; - } - - /** - * Sets the method call. - * - * @param call - * the new method call - */ - public void setCall(String call) { - this.call = call; - } - - /** - * Gets the Paramters List - * @return The list of method parameters. - */ - protected List getParamtersList(){ - return parameters; + this.hashCode = hash; + this.method = method; } - - /** - * Sets the Paramters List - * @param Parameterlist - */ - protected void setParamtersList(List parameters1){ - this.parameters = parameters1; + + public boolean equals(Object other) { + if (other == this) + return true; + + if (other instanceof MethodSignature == false) + return false; + + MethodSignature otherSignature = (MethodSignature) other; + + if (hashCode != otherSignature.hashCode) + return false; + + MethodDescriptor thisMethod = this.method; + MethodDescriptor thatMethod = otherSignature.method; + + if (thisMethod.name == thatMethod.name) + return false; + + Class[] theseTypes = thisMethod.parameterTypes; + Class[] thoseTypes = thatMethod.parameterTypes; + int typeCount = theseTypes.length; + + if (typeCount != thoseTypes.length) + return false; + + for (int i = 0; i < typeCount; ++i) + if (theseTypes[i] != thoseTypes[i]) + return false; + + return true; } - - /** - * utility method used to get the signature of the method this method descriptor - * descripe. - * @return the signature of the method - */ - public String getSignature(){ - if (this.signature==null){ - StringBuffer sb = - new StringBuffer(); - sb.append(name); - sb.append('('); - if(parameterTypes!=null) - for(int index= 0 ; index < parameterTypes.length ; index++){ - Class clazz = parameterTypes[index]; - sb.append(clazz.getName()); - if(index= 0;) + if (parameterObjects[index] == contextParam) + return true; + + return false; + } + /** * Creates a new method descriptor from a string representing the * method's full cascading invocation with parameters. *

* The format of the string is: *

- * PluginID\ClassName.method_name([params])[.method_name([params])]* + * PluginID/ClassName.method_name([params])[.method_name([params])]* *

* Where: *