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: *
*
* The format of the string is:
*
- *
* 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(indexPluginID\ClassName.method_name([params])[.method_name([params])]*
+ * PluginID/ClassName.method_name([params])[.method_name([params])]*
*
@@ -746,272 +697,251 @@
*
MyPluginID\MyClass.MyStaticFunction(%,"some value")
- *
+ * MyPluginID/MyClass.MyStaticFunction(%Context,"some value")
+ *
* @param string
* the method invocation string
*/
- public StaticMethodDescriptor(String string) {
- // set plugin ID
- string = parsePluginID(string.trim());
- // set class Name
- string = parseClassName(string.trim());
- // set method name
- string = parseName(string.trim());
- // set method parameters
- string = parseParameterList(string.trim());
+ public static StaticMethodDescriptor create(String string) {
+ SimpleParser parser = new SimpleParser(string);
- List parameters = getParamtersList();
-
- // fill the parameter objects and types arrays
- if (parameters != null && !parameters.isEmpty()) {
- Collections.reverse(parameters);
- Object[] parameterObjects = parameters.toArray();
- Class[] 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 if (p.startsWith(contextParam)){// "param" //$NON-NLS-1$
- parameterTypes[i] = getParameterType(p);
- parameterObjects[i] = "%Context"; //$NON-NLS-1$
- }
- else
- 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;
- }
- }
- setParameters(parameterObjects);
- setParameterTypes(parameterTypes);
- }
- parameters = null;
+ // get plugin ID
+ String pluginId = parser.nextToken('/', true);
- // set method parameters
- if (string.length() != 0) {
- if (string.charAt(0) != '.')
- throw new IllegalArgumentException();
- setNext(new MethodDescriptor(string.substring(1).trim()));
- }
-
- if (getName() != null)
- setName(getName().intern());
- }
-
-
- /**
- * parse the passed paramter to extract the paramter's class
- * @param p the parapemter
- * @return
- */
- private Class getParameterType(String parameter) {
- int startIndex = parameter.indexOf("["); //$NON-NLS-1$
- int endIndex = parameter.indexOf("]"); //$NON-NLS-1$
- if(startIndex==-1 || endIndex==-1)
- throw new IllegalArgumentException(); //$NON-NLS-1$
- String parameterTypeString= parameter.substring(startIndex+1,endIndex).trim();
-
- endIndex = parameterTypeString.indexOf('/');
- if(endIndex==-1 || endIndex==parameterTypeString.length()-1)
- throw new IllegalArgumentException(); //$NON-NLS-1$
- String parameterPluginID = parameterTypeString.substring(0,endIndex).trim();
- String parameterClassName = parameterTypeString.substring(endIndex + 1);
- Class clazz = loadClass(parameterClassName,parameterPluginID);
- if(clazz==null)
- clazz = Object.class;
- return clazz;
+ // get class name
+ String className = parser.nextTokenBefore('.', '(');
+
+ return new StaticMethodDescriptor(pluginId, className, parser.tail());
}
/**
- * Parses and returns the Plugin ID in a method invocation string.
- *
- * @param string
- * the method invocation string
- * @return the plugin name
+ * Does this object have any "%Context" parameters?
+ */
+ private final boolean hasContextParameters;
+
+ /**
+ * the Class Name
*/
- private String parsePluginID(String string) {
- int index = string.indexOf('/');
- if (index == -1)
- throw new IllegalArgumentException(); //$NON-NLS-1$
- pluginID = string.substring(0, index).trim();
- return string.substring(index + 1);
+ public final String className;
+
+ /**
+ * the plugin Name
+ */
+ public final String pluginID;
+
+ private StaticMethodDescriptor(
+ String pluginID,
+ String className,
+ String method) {
+ super(method);
+ this.className = className.intern();
+ this.pluginID = pluginID.intern();
+ this.hasContextParameters = anyContextParameters(parameterObjects);
+ }
+
+ private Object[] getParametersFor(Object context) {
+ Object[] parameters = parameterObjects;
+
+ if (hasContextParameters) {
+ parameters = (Object[]) parameters.clone();
+
+ for (int i = parameters.length; --i >= 0;)
+ if (parameters[i] == contextParam)
+ parameters[i] = context;
+ }
+
+ return parameters;
}
-
+
+ protected void setParameterAndType(String parameter, int index) {
+ if (!parameter.startsWith(contextParam)) {
+ super.setParameterAndType(parameter, index);
+ return;
+ }
+
+ SimpleParser parser = new SimpleParser(parameter);
+
+ // parse "%Context[pluginID/className]"
+
+ parser.nextToken('[', true); // skip "%Context"
+
+ String parmPluginID = parser.nextToken('/', true).trim();
+ String parmClassName = parser.nextToken(']', true).trim();
+
+ Class type = loadClass(parmClassName, parmPluginID); // TODO delay loading classes
+
+ if (type == null)
+ type = Object.class;
+
+ parameterTypes[index] = type;
+ parameterObjects[index] = contextParam;
+ }
+
/**
- * Parses and returns the Plugin ID in a method invocation string.
- *
- * @param string
- * the method invocation string
- * @return the plugin name
+ * A utility method to invoke a cascading list of methods.
+ *
+ * @param object
+ * The context object to use (it could be null)
+ * @return the value of the invokation
*/
- private String parseClassName(String string) {
- int index = string.indexOf('(');
- if (index == -1)
- throw new IllegalArgumentException(); //$NON-NLS-1$
- index = string.lastIndexOf('.',index);
- if (index == -1)
- throw new IllegalArgumentException(); //$NON-NLS-1$
- className = string.substring(0, index).trim();
- return string.substring(index + 1);
- }
-
- public String getPluginID(){
- return pluginID;
- }
-
- public String getClassName(){
- return className;
+ public Object invoke(Object object) {
+ Class clazz = loadClass(className, pluginID);
+
+ if (clazz == null)
+ return null;
+
+ Method method = getMethod(clazz);
+
+ if (method == null)
+ return null;
+
+ try {
+ object = method.invoke(null, getParametersFor(object));
+ } catch (Exception e) {
+ return null;
+ }
+
+ return next == null ? object : next.invoke(object);
}
-
- }
-
-
+ }
+
/**
* A descriptor for an XML configuration element that identifies a method
* result by its type and toString()
value.
*/
- private static class ValueDescriptor {
-
+ private static final class ValueDescriptor {
+
/**
- * The valid value literals.
+ * The value literals map. Keyed by a string, the corresponding value
+ * indicates whether that value is permitted or disallowed.
*/
- private Set valueLiterals;
-
+ private final Map valueMap;
+
/**
- * The invalid valud literals.
+ * Are there any permitted values specified?
*/
- private Set notValueLiterals;
-
+ private final boolean valuesSpecified;
+
/**
* The valid value objects.
*/
- private List valueObjects;
-
+ private final ObjectDescriptor[] valueObjects;
+
/**
* The invalid value objects.
*/
- private List notValueObjects;
+ private final ObjectDescriptor[] notValueObjects;
/**
* Creates a new value descriptor from its configuration element.
- *
- * @param configElement
+ *
+ * @param element
* The configuration element.
*/
- public ValueDescriptor(IConfigurationElement configElement) {
- valueLiterals = new HashSet();
- String s = configElement.getAttribute(VALUE);
- if (s != null)
- parseValueLiteralString(s, valueLiterals);
-
- notValueLiterals = new HashSet();
- s = configElement.getAttribute(NOT_VALUE);
- if (s != null)
- parseValueLiteralString(s, notValueLiterals);
-
- IConfigurationElement[] valueConfigs = configElement.getChildren(VALUE);
- valueObjects = new ArrayList(valueConfigs.length);
- for (int i=0; ilist
.
- *
- * @param s
- * the string to be parsed
- * @param list
- * the set of literal string values from s
.
- */
- private void parseValueLiteralString(String s, Set list) {
- // parse the string comma-separated string literals ignoring escaped commas
- int start = 0;
- int end = s.indexOf(',');
- while (end != -1) {
- if (s.charAt(end-1) == '\\') {
- s = s.substring(0, end-1) + s.substring(end);
- end = s.indexOf(',', end);
- continue;
- }
- list.add(s.substring(start, end).trim().intern());
- start = end +1;
- end = s.indexOf(',', start);
+ public ValueDescriptor(IConfigurationElement element) {
+ super();
+ valueObjects = parseObjects(element.getChildren(VALUE));
+ notValueObjects = parseObjects(element.getChildren(NOT_VALUE));
+
+ Map literals = new HashMap();
+
+ // For backwards compatibility when values and not-values overlap
+ // we add not-values first so values continue to take precedence.
+ addLiterals(element.getAttribute(NOT_VALUE), literals, Boolean.FALSE);
+ valuesSpecified = addLiterals(element.getAttribute(VALUE), literals, Boolean.TRUE);
+
+ switch (literals.size()) {
+ case 0:
+ valueMap = null;
+ break;
+ case 1:
+ Entry entry = (Entry) literals.entrySet().iterator().next();
+ valueMap = Collections.singletonMap(entry.getKey(), entry.getValue());
+ break;
+ default:
+ valueMap = literals;
+ break;
}
- list.add(s.substring(start).trim().intern());
}
-
+
+ /**
+ * Parse the given string, which is a comma-separated list of value
+ * literals and use them as keys in the given map where value is to be
+ * stored.
+ *
+ * @param string
+ * the string of literals
+ * @param literals
+ * the map in which the entries are to be stored
+ * @param value
+ * the value to be associated with each literal
+ * @return boolean whether the string was non-null
+ */
+ private static boolean addLiterals(String string, Map literals, Boolean value) {
+ if (string == null)
+ return false;
+
+ // parse the string comma-separated string literals
+ // (ignoring escaped commas)
+ SimpleParser parser = new SimpleParser(string);
+
+ do {
+ String token = parser.nextToken(',', false);
+
+ literals.put(token.trim().intern(), value);
+ } while (parser.moreInput());
+
+ return true;
+ }
+
+ private static ObjectDescriptor[] parseObjects(IConfigurationElement[] configs) {
+ int length = configs.length;
+
+ if (length == 0)
+ return null;
+
+ ObjectDescriptor[] descriptors = new ObjectDescriptor[length];
+
+ for (int i = 0; i < length; ++i)
+ descriptors[i] = new ObjectDescriptor(configs[i]);
+
+ return descriptors;
+ }
+
/**
* Returns true
if I am the same as object
,
* false
otherwise.
- *
+ *
* @param object
* the object to be tested
* @return true
if I am the same as object
,
* false
otherwise.
*/
public boolean sameAs(Object object) {
- if (!valueLiterals.isEmpty()) {
- if (!valueLiterals.contains(object.toString()))
- return false;
- }
- if (!notValueLiterals.isEmpty()) {
- if (notValueLiterals.contains(object.toString()))
- return false;
- }
- if (!valueObjects.isEmpty()) {
- if (!isObjectinList(object, valueObjects))
+ if (valueMap != null) {
+ Object bool = valueMap.get(object.toString());
+
+ if (bool == Boolean.FALSE)
+ // one of the specified not-values
return false;
- }
- if (!notValueObjects.isEmpty()) {
- if (isObjectinList(object, notValueObjects))
+ else if (valuesSpecified && bool == null)
+ // not one of the specified values
return false;
}
+
+ if (valueObjects != null && !isObjectInList(object, valueObjects))
+ return false;
+
+ if (notValueObjects != null && isObjectInList(object, notValueObjects))
+ return false;
+
return true;
}
-
+
/**
* Answers whether or not an object in list
is the
* {@link #sameAs(Object)}object
.
- *
+ *
* @param object
* the object to find
* @param list
@@ -1020,12 +950,11 @@
* the {@link #sameAs(Object)}object
,
* false
otherwise.
*/
- private boolean isObjectinList(Object object, List list) {
- Iterator i = list.iterator();
- while (i.hasNext()) {
- if (((ObjectDescriptor)i.next()).sameAs(object))
+ private static boolean isObjectInList(Object object, ObjectDescriptor[] list) {
+ for (int i = 0, n = list.length; i < n; ++i)
+ if (list[i].sameAs(object))
return true;
- }
+
return false;
}
}
@@ -1033,17 +962,17 @@
/**
* Describes a method value using a method descriptor and a value descriptor.
*/
- private static class MethodValueEntry {
-
+ private static final class MethodValueEntry {
+
/**
* The method descriptor.
*/
- public MethodDescriptor method;
-
+ private final MethodDescriptor method;
+
/**
* The value descriptor.
*/
- public ValueDescriptor value;
+ private final ValueDescriptor value;
/**
* Creates a new method value entry.
@@ -1055,12 +984,21 @@
this.method = method;
this.value = value;
}
+
+ public boolean matches(Object object) {
+ Object result = method.invoke(object);
+
+ if (result == null)
+ result = NULL;
+
+ return value.sameAs(result);
+ }
}
/**
* A helper method to return a list of objects whose ids are given in a
* comma-separated string and whose instances are given in an object map.
- *
+ *
* @param objectsIds
* A comma-separated object ids string
* @param objectMap
@@ -1076,7 +1014,7 @@
StringTokenizer ids = new StringTokenizer(objectsIds.trim(), ","); //$NON-NLS-1$
if (!ids.hasMoreTokens())
return null;
-
+
List objectList = new ArrayList();
while (ids.hasMoreTokens()) {
String objectId = ids.nextToken().trim();
@@ -1084,7 +1022,8 @@
if (objectVal != null)
objectList.add(objectVal);
else {
- Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, configElement.getDeclaringExtension().getNamespace()+ ".plugin.xml extension [" + configElement.getDeclaringExtension().getExtensionPointUniqueIdentifier() + "]: object id (" + objectId + ") is not in the list " + objectMap.keySet()); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ IExtension extension = configElement.getDeclaringExtension();
+ Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.SERVICE_FAILURE, extension.getNamespace()+ ".plugin.xml extension [" + extension.getExtensionPointUniqueIdentifier() + "]: object id (" + objectId + ") is not in the list " + objectMap.keySet()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
return objectList;
@@ -1093,7 +1032,7 @@
/**
* Parses the comma-separated s
string and returns a set of
* the individual entries in the string.
- *
+ *
* @param s
* A comma-separated string
* @return a set of the individual entries in the string.
@@ -1109,10 +1048,19 @@
return stringList.isEmpty() ? null : stringList;
}
+ /*private*/static Map getSubMap(Map map, Object key) {
+ Map subMap = (Map) map.get(key);
+
+ if (subMap == null)
+ map.put(key, subMap = new HashMap());
+
+ return subMap;
+ }
+
/**
* Tests if an object matches at least one in the list of object descriptors
* passed.
- *
+ *
* @param object
* the object for which to find a match
* @param objects
@@ -1133,113 +1081,88 @@
/**
* A utility method to load a class using its name and a given class loader.
- *
+ *
* @param className
* The class name
- * @param bundle
- * The class loader
+ * @param pluginId
+ * The symbolic name of the plugin which defines the class
* @return The loaded class or null
if could not be loaded
*/
- /*protected static Class loadClass(String className, Bundle bundle) {
- try {
- return bundle.loadClass(className);
- } catch (ClassNotFoundException e) {
+ protected static Class loadClass(String className, String pluginId) {
+ Map pluginMap = getSubMap(loadedClassMap, pluginId);
+ Object candidate = pluginMap.get(className);
+ Class clazz;
+
+ if (candidate instanceof WeakReference) {
+ clazz = (Class) ((WeakReference) candidate).get();
+
+ if (clazz != null)
+ return clazz;
+ } else if (candidate != null) {
+ // we already tried to load this class and failed
return null;
}
- }*/
-
- /**
- * A utility method to load a class using its name and a given class loader.
- *
- * @param className
- * The class name
- * @param bundle
- * The class loader
- * @return The loaded class or null
if could not be loaded
- */
- protected static Class loadClass(String className, String pluginId) {
- StringBuffer keyStringBuf = new StringBuffer(className.length()
- + pluginId.length() + 2); // 2 is for . and extra.
- keyStringBuf.append(pluginId);
- keyStringBuf.append('.');
- keyStringBuf.append(className);
- String keyString = keyStringBuf.toString();
- WeakReference ref = (WeakReference) successLookupTable.get(keyString);
- Class found = (ref != null) ? (Class) ref.get()
- : null;
- if (found == null) {
- if (ref != null)
- successLookupTable.remove(keyString);
- if (!failureLookupTable.contains(keyString)) {
- try {
- Bundle bundle = getPluginBundle(pluginId);
- if (bundle!=null){
- found = bundle.loadClass(className);
- successLookupTable.put(keyString, new WeakReference(found));
- }else{
- failureLookupTable.add(keyString);
- }
- } catch (ClassNotFoundException e) {
- failureLookupTable.add(keyString);
- }
+
+ Bundle bundle = getPluginBundle(pluginId);
+
+ if (bundle != null) {
+ try {
+ clazz = bundle.loadClass(className);
+
+ pluginMap.put(className, new WeakReference(clazz));
+
+ return clazz;
+
+ } catch (ClassNotFoundException e) {
+ // fall through
}
}
- return found;
+
+ // record failure
+ pluginMap.put(className, Boolean.FALSE);
+
+ return null;
}
-
-
+
/**
* Given a bundle id, it checks if the bundle is found and activated. If it
* is, the method returns the bundle, otherwise it returns null
.
- *
+ *
* @param pluginId
* the bundle ID
* @return the bundle, if found
*/
protected static Bundle getPluginBundle(String pluginId) {
Bundle bundle = Platform.getBundle(pluginId);
- if (null != bundle && bundle.getState() == org.osgi.framework.Bundle.ACTIVE)
+ if (null != bundle && bundle.getState() == Bundle.ACTIVE)
return bundle;
return null;
}
/**
- * Tests if the given class is assignable to the given class name. Optimized
- * to look first in a cache of previously retrieved results.
- *
+ * Iterate over the class and all its ancestors associating Boolean.TRUE
+ * with each class and interface name.
+ *
* @param clazz
- * the class to be tested
- * @param className
- * the class name to test against
- * @return true
if the class is assignable to the class name,
- * false
otherwise.
- */
- protected static boolean isAssignableTo(Class clazz, String className) {
- if (clazz == null)
- return false;
+ * the class
+ * @param assignable
+ * the map
+ */
+ private static void gatherClassAndInterfaceNames(Class clazz, Map assignable) {
+ for (; clazz != null; clazz = clazz.getSuperclass()) {
+ assignable.put(clazz.getName(), Boolean.TRUE);
- if ( contains(isNotAssignableTable, clazz, className) ) {
- return false;
- }
-
- if ( contains(isAssignableTable, clazz, className) ) {
- return true;
- }
-
- boolean result = isAssignableToNoCache(clazz,className);
-
- if (result) {
- add(isAssignableTable, clazz, className);
- } else {
- add(isNotAssignableTable, clazz, className);
- }
+ Class[] interfaces = clazz.getInterfaces();
- return result;
+ for (int i = interfaces.length; --i >= 0;)
+ gatherClassAndInterfaceNames(interfaces[i], assignable);
+ }
}
/**
- * Tests if the given class is assignable to the given class name.
- *
+ * Tests if the given class is assignable to the given class name. Optimized
+ * to look first in a cache of previously retrieved results.
+ *
* @param clazz
* the class to be tested
* @param className
@@ -1247,57 +1170,40 @@
* @return true
if the class is assignable to the class name,
* false
otherwise.
*/
- private static boolean isAssignableToNoCache(Class clazz, String className) {
-// mgoyal: This approach isn't safe to use as it can cause incorrect
-// plugin load. Documenting this approach for further analysis. Don't
-// remove or uncomment this.
-// try {
-// if(clazz.getName().equals(className))
-// return true;
-//
-// ClassLoader clsLoader = clazz.getClassLoader();
-// if(clsLoader != null) {
-// Class testCls = clsLoader.loadClass(className);
-// if(testCls != null && testCls.isAssignableFrom(clazz))
-// return true;
-// }
-// return false;
-// } catch (ClassNotFoundException e) {
-// return false;
-// }
-//
-
- // test the class itself
- if (clazz.getName().equals(className))
- return true;
-
- // test all the interfaces the class implements
- Class[] interfaces = clazz.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- if (checkInterfaceHierarchy(interfaces[i], className))
- return true;
+ protected static boolean isAssignableTo(Class clazz, String className) {
+ if (clazz != null) {
+ Map assignable = (Map) assignableMap.get(clazz);
+
+ if (assignable == null) {
+ assignable = new HashMap();
+ gatherClassAndInterfaceNames(clazz, assignable);
+ assignableMap.put(clazz, assignable);
+ }
+
+ Object value = assignable.get(className);
+
+ if (value != null)
+ return ((Boolean) value).booleanValue();
+
+ assignable.put(className, Boolean.FALSE);
}
-
- // test superclass
- return isAssignableTo(clazz.getSuperclass(), className);
+
+ return false;
}
/**
- * A map of classes that have been successfully loaded, keyed on the class
- * name optionally prepended by the plugin ID, if specified.
+ * A map of information about classes we found or failed to find. The first
+ * level is keyed by plugin identifiers and yields a second-level map. The
+ * second-level map is keyed by class names. A value at this level is either
+ * a WeakReference to a class or Boolean.FALSE which signals that no
+ * accessible class exists.
*/
- private static Map successLookupTable = new HashMap();
-
- /**
- * A map of classes that could not be loaded, keyed on the class name
- * optionally prepended by the plugin ID, if specified.
- */
- private static Set failureLookupTable = new HashSet();
+ private static final Map loadedClassMap = new HashMap();
/**
* Gets an adapter for object
to the class described by
* className
qualified by the optional pluginId
.
- *
+ *
* @param object
* the object to be adapted
* @param className
@@ -1307,216 +1213,11 @@
* @return the adapted object, or null
if it couldn't be found
*/
protected static Object getAdapter(Object object, String className, String pluginId) {
- if (!(object instanceof IAdaptable))
- return null;
- if(pluginId != null) {
- Class theClass = loadClass(className,pluginId);
- return theClass != null ? ((IAdaptable) object).getAdapter(theClass) : null;
+ if (pluginId != null && object instanceof IAdaptable) {
+ Class clazz = loadClass(className, pluginId);
+ if (clazz != null)
+ return ((IAdaptable) object).getAdapter(clazz);
}
return null;
}
-
- /**
- * A utility method to invoke a cascading list of methods.
- *
- * @param methodDescriptor
- * the first method descriptor
- * @param object
- * The object to invoke the method on
- * @return the value of the invokation
- */
- protected static Object invokeMethod(MethodDescriptor methodDescriptor, Object object) {
- String methodSignature = null;
- Class clazz =null;
- try {
- if (methodDescriptor == null || object == null)
- return null;
- methodSignature = methodDescriptor.getSignature();
- clazz = object.getClass();
- if (passiveClasses.contains(clazz,methodSignature))
- return null;
- Method method = classToMethodSignatureToMethodCach.
- getMethod(clazz,methodSignature);
- if(method==null){
- method = clazz.getMethod(methodDescriptor.getName(),
- methodDescriptor.getParameterTypes());
- classToMethodSignatureToMethodCach.addMethod(clazz,methodSignature,method);
- }
- Object valueObj =
- method.invoke(object, methodDescriptor.getParameters());
- if (methodDescriptor.getNext() == null)
- return valueObj == null ? NULL : valueObj;
- return invokeMethod(methodDescriptor.getNext(), valueObj);
- } catch (Exception e) {
- passiveClasses.addMethod(clazz,methodSignature);
- return null;
- }
- }
-
- /**
- * A utility method to invoke a cascading list of methods.
- *
- * @param StaticMethodDescriptor
- * the static method descriptor
- * @param object
- * The context object to use (it could be null)
- * @return the value of the invokation
- */
- protected static Object invokeStaticMethod(StaticMethodDescriptor methodDescriptor, Object object) {
- try {
- if (methodDescriptor == null)
- return null;
-
- Object[] valuesCopy = (Object[])methodDescriptor.getParameters().clone();
- for (int i = 0; i < valuesCopy.length; i++) {
- if(valuesCopy[i].equals(contextParam)){
- valuesCopy[i]=object;
- }
- }
-
- Method method = getStaticMethod(methodDescriptor);
- Object valueObj =
- method.invoke(object, valuesCopy);
-
- if (methodDescriptor.getNext() == null)
- return valueObj == null ? NULL : valueObj;
- return invokeMethod(methodDescriptor.getNext(), valueObj);
- } catch (Exception e) {
- return null;
- }
- }
-
- /**
- * utility method used to get a static method object
- * @param pluginID the plugin that owns the class
- * @param className the class to use to call hte static method
- * @param methodName the method to get
- * @param ParameterTypes the parameter types
- * @return the method object
- */
- private static Method getStaticMethod(StaticMethodDescriptor staticMethodDescriptor) {
- Class theClass = loadClass(staticMethodDescriptor.getClassName(),
- staticMethodDescriptor.getPluginID());
- if (theClass==null)
- return null;
- Method theMethod = null;
- try {
- String methodSignature = staticMethodDescriptor.getSignature();
- theMethod = classToMethodSignatureToMethodCach.getMethod(theClass,methodSignature);
- if(theMethod==null){
- theMethod = theClass.getMethod(staticMethodDescriptor.getName(),
- staticMethodDescriptor.getParameterTypes());
- classToMethodSignatureToMethodCach.addMethod(theClass,methodSignature,theMethod);
- }
- } catch (SecurityException e) {
- // no special handling needed;
- } catch (NoSuchMethodException e) {
- // no special handling needed;
- }
- return theMethod;
- }
-
- /**
- * Check the interfaces the whole way up. If one of them matches
- * className
return true
. Optimized to look
- * first in a cache of previously retrieved results.
- *
- * @param interfaceToCheck
- * The interface whose name we are testing.
- * @param className
- * the name of the interface to we are trying to match
- * @return true
if one of the interfaces in the hierarchy
- * matches className
,false
- * otherwise.
- */
- private static boolean checkInterfaceHierarchy(Class interfaceToCheck, String className) {
-
- if ( contains(isNotAssignableTable, interfaceToCheck, className) ) {
- return false;
- }
-
- if ( contains(isAssignableTable, interfaceToCheck, className) ) {
- return true;
- }
-
- boolean result = checkInterfaceHierarchyNoCache(interfaceToCheck,className);
-
- if (result) {
- add(isAssignableTable, interfaceToCheck, className);
- } else {
- add(isNotAssignableTable, interfaceToCheck, className);
- }
-
- return result;
- }
-
- /**
- * Check the interfaces the whole way up. If one of them matches
- * className
return true
.
- *
- * @param interfaceToCheck
- * The interface whose name we are testing.
- * @param className
- * the name of the interface to we are trying to match
- * @return true
if one of the interfaces in the hierarchy
- * matches className
,false
- * otherwise.
- */
- private static boolean checkInterfaceHierarchyNoCache(Class interfaceToCheck, String className) {
- if(interfaceToCheck.getName().equals(className))
- return true;
- Class[] superInterfaces = interfaceToCheck.getInterfaces();
- for (int i = 0; i < superInterfaces.length; i++) {
- if(checkInterfaceHierarchy(superInterfaces[i], className))
- return true;
- }
- return false;
- }
-
- /**
- * Determines whether the map
contains an entry for the
- * true
if the map contains the key/value pair,
- * false
otherwise
- */
- private static boolean contains(Map map, Object key, String value) {
-
- boolean result = false;
-
- Object val = map.get(key);
- if (val != null) {
- Set values = (Set)val;
- result = values.contains(value);
- }
-
- return result;
- }
-
- /**
- * Adds the map
.
- *
- * @param map
- * the map in which to add the value
- * @param key
- * the key
- * @param value
- * the value
- */
- private static void add(Map map, Object key, String value) {
-
- Set values = (Set)map.get(key);
- if (values == null) {
- values = new HashSet();
- map.put(key, values);
- }
-
- values.add(value);
- }
-}
+}
\ No newline at end of file