View | Details | Raw Unified | Return to bug 120106
Collapse All | Expand All

(-)AbstractProviderConfiguration.java (-1070 / +771 lines)
Lines 6-12 Link Here
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *    IBM Corporation - initial API and implementation 
9
 *    IBM Corporation - initial API and implementation
10
 ****************************************************************************/
10
 ****************************************************************************/
11
11
12
package org.eclipse.gmf.runtime.common.core.service;
12
package org.eclipse.gmf.runtime.common.core.service;
Lines 22-33 Link Here
22
import java.util.Map;
22
import java.util.Map;
23
import java.util.Set;
23
import java.util.Set;
24
import java.util.StringTokenizer;
24
import java.util.StringTokenizer;
25
import java.util.WeakHashMap;
26
import java.util.Map.Entry;
25
27
26
import org.eclipse.core.runtime.IAdaptable;
28
import org.eclipse.core.runtime.IAdaptable;
27
import org.eclipse.core.runtime.IConfigurationElement;
29
import org.eclipse.core.runtime.IConfigurationElement;
30
import org.eclipse.core.runtime.IExtension;
28
import org.eclipse.core.runtime.Platform;
31
import org.eclipse.core.runtime.Platform;
29
import org.eclipse.gmf.runtime.common.core.internal.CommonCorePlugin;
32
import org.eclipse.gmf.runtime.common.core.internal.CommonCorePlugin;
30
import org.eclipse.gmf.runtime.common.core.internal.CommonCoreStatusCodes;
33
import org.eclipse.gmf.runtime.common.core.internal.CommonCoreStatusCodes;
34
import org.eclipse.gmf.runtime.common.core.util.HashUtil;
31
import org.eclipse.gmf.runtime.common.core.util.Log;
35
import org.eclipse.gmf.runtime.common.core.util.Log;
32
import org.osgi.framework.Bundle;
36
import org.osgi.framework.Bundle;
33
37
Lines 38-45 Link Here
38
 * <P>
42
 * <P>
39
 * This abstract class contains a set of useful utilities for such concrete
43
 * This abstract class contains a set of useful utilities for such concrete
40
 * subclasses.
44
 * subclasses.
41
 * 
45
 *
42
 * @author melaasar, mmostafa
46
 * @author melaasar, mmostafa, keithc
43
 * @canBeSeenBy %partners
47
 * @canBeSeenBy %partners
44
 */
48
 */
45
public class AbstractProviderConfiguration {
49
public class AbstractProviderConfiguration {
Lines 47-83 Link Here
47
	 * The name of the 'object' XML attribute.
51
	 * The name of the 'object' XML attribute.
48
	 */
52
	 */
49
	protected static final String OBJECT = "object"; //$NON-NLS-1$
53
	protected static final String OBJECT = "object"; //$NON-NLS-1$
50
	
54
51
	/**
55
	/**
52
	 * The name of the 'id' XML attribute.
56
	 * The name of the 'id' XML attribute.
53
	 */
57
	 */
54
	protected static final String ID = "id"; //$NON-NLS-1$
58
	protected static final String ID = "id"; //$NON-NLS-1$
55
	
59
56
	/**
60
	/**
57
	 * The name of the 'class' XML attribute.
61
	 * The name of the 'class' XML attribute.
58
	 */
62
	 */
59
	protected static final String CLASS = "class"; //$NON-NLS-1$
63
	protected static final String CLASS = "class"; //$NON-NLS-1$
60
 
64
61
	/**
65
	/**
62
	 * The name of the 'method' XML attribute.
66
	 * The name of the 'method' XML attribute.
63
	 */
67
	 */
64
	protected static final String METHOD = "method"; //$NON-NLS-1$
68
	protected static final String METHOD = "method"; //$NON-NLS-1$
65
	
69
66
	/**
70
	/**
67
	 * The name of the 'method' XML attribute.
71
	 * The name of the 'method' XML attribute.
68
	 */
72
	 */
69
	protected static final String STATIC_METHOD = "staticMethod"; //$NON-NLS-1$
73
	protected static final String STATIC_METHOD = "staticMethod"; //$NON-NLS-1$
70
	
74
71
	/**
75
	/**
72
	 * The name of the 'name' XML attribute.
76
	 * The name of the 'name' XML attribute.
73
	 */
77
	 */
74
	protected static final String NAME = "name"; //$NON-NLS-1$
78
	protected static final String NAME = "name"; //$NON-NLS-1$
75
	
79
76
	/**
80
	/**
77
	 * The name of the 'value' XML attribute.
81
	 * The name of the 'value' XML attribute.
78
	 */
82
	 */
79
	protected static final String VALUE = "value"; //$NON-NLS-1$
83
	protected static final String VALUE = "value"; //$NON-NLS-1$
80
	
84
81
	/**
85
	/**
82
	 * The name of the 'notValue' XML attribute.
86
	 * The name of the 'notValue' XML attribute.
83
	 */
87
	 */
Lines 87-257 Link Here
87
	 * The name of the 'null' XML attribute value.
91
	 * The name of the 'null' XML attribute value.
88
	 */
92
	 */
89
	protected static final String NULL = "null"; //$NON-NLS-1$
93
	protected static final String NULL = "null"; //$NON-NLS-1$
90
	
94
91
	/**
95
	/**
92
	 * the name of the context param
96
	 * the name of the context param
93
	 */
97
	 */
94
	protected static final String contextParam = "%Context"; //$NON-NLS-1$
98
	protected static final String contextParam = "%Context"; //$NON-NLS-1$
95
99
96
	/**
100
	/**
97
	 * A map to store previously successful class lookups.
101
	 * A map to cache known information about class relationships. The first
98
	 */
102
	 * level key is keyed by a Class and yields a second-level map. The
99
	private static Map isAssignableTable = new HashMap();
103
	 * second-level map is keyed by a class name. The leaf values are Boolean
100
104
	 * objects whose values signify whether the Class is assignable to the named
101
	/** 
105
	 * class.
102
	 * A map to store previously failed class lookups.
103
	 */
104
	private static Map isNotAssignableTable = new HashMap();
105
	
106
	/**
107
	 * a map of classes that get asked for methods they do not contain, by
108
	 * the provider, the map is a class to a Set of method signatures
109
	 */
110
	private static ClassToMethodSignaturesSetMap passiveClasses = 
111
		new ClassToMethodSignaturesSetMap();
112
	
113
	/**
114
	 * a class to cach passive classes, passive classes are the classes we asked 
115
	 * for a method with a specific signature and they faild to find it. The cach used 
116
	 * so in the next time we can tell if the method does not exists oin the class
117
	 * without calling getMethod by reflection, which improves the performance
118
	 * @author mmostafa
119
	 *
120
	 */
121
	private static class ClassToMethodSignaturesSetMap{
122
		
123
		/**
124
		 * internal map for the cach, it is a map of Class to Set of method signature Strings
125
		 */
126
		Map classToMethodSignaturesSetMap = new HashMap();
127
		
128
		/**
129
		 * adds a class and a method signature to the passive class cach
130
		 * @param clazz		the class
131
		 * @param signature	the method signature
132
		 */
133
		public void addMethod(Class clazz, String signature){
134
			Set signatures = (Set)classToMethodSignaturesSetMap.get(clazz);
135
			if (signatures==null){
136
				signatures = new HashSet();
137
				classToMethodSignaturesSetMap.put(clazz,signatures);
138
			}
139
			signatures.add(signature);
140
		}
141
		
142
		/**
143
		 * check if the class and the method signatrue are contained in the apssive collection,
144
		 * which means we do  need need to call get method oon the class becuase we will not 
145
		 * find it, this helps improving the performance.
146
		 * @param clazz
147
		 * @param signature
148
		 * @return
149
		 */
150
		public boolean contains(Class clazz, String signature){
151
			Set signatures = (Set)classToMethodSignaturesSetMap.get(clazz);
152
			if (signatures==null)
153
				return false;
154
			return signatures.contains(signature);
155
		}
156
	}
157
	
158
	/**
159
	 * internal class used to cach Methods, so we do not call getMethod too often 
160
	 * @author mmostafa
161
	 *
106
	 *
107
	 * We use a WeakHashMap to allow classes to be unloaded.
162
	 */
108
	 */
163
	private static class ClassToMethodSignatureToMethodCach{
109
	private static final Map assignableMap = new WeakHashMap();
164
		
110
165
		/**
166
		 * internal map to hold the cached data, it is a map of Class => Map
167
		 * of Singature string => method
168
		 */
169
		Map classToMethodSignatureToMethod = new HashMap();
170
		
171
		/**
172
		 * adds a <code>Method</code> to the cach
173
		 * @param clazz		the class we got the method from 
174
		 * @param methodSignature	the method signature
175
		 * @param method	the <code>Method</code>
176
		 */
177
		public void addMethod(Class clazz,String methodSignature, Method method ){
178
			Map signatureToMethodMap = (Map)classToMethodSignatureToMethod.get(clazz);
179
			if (signatureToMethodMap==null){
180
				signatureToMethodMap = new HashMap();
181
				classToMethodSignatureToMethod.put(clazz,signatureToMethodMap);
182
			}
183
			signatureToMethodMap.put(methodSignature,method);
184
		}
185
		
186
		/**
187
		 * gets a method from the cach using the class that owns it and the method 
188
		 * signature.
189
		 * @param clazz		the class that owns the method
190
		 * @param methodSignature	the method signature
191
		 * @return	the <code>Method</code> if found any, otherwise null
192
		 */
193
		public Method getMethod(Class clazz,String methodSignature){
194
			Map signatureToMethodMap  = (Map)classToMethodSignatureToMethod.get(clazz);
195
			if (signatureToMethodMap !=null){
196
				return (Method)signatureToMethodMap.get(methodSignature);
197
			}
198
			return null;
199
		}
200
		
201
	}
202
	
203
	/**
204
	 * map for class to Method signature to method cach
205
	 */
206
	private static ClassToMethodSignatureToMethodCach
207
		classToMethodSignatureToMethodCach = new ClassToMethodSignatureToMethodCach();
208
	
209
	
210
	
211
	/**
212
	 * Gets the class name of <code>object</code>.
213
	 * @param object the object for which the class name is to be found.
214
	 * @return the class name
215
	 */
216
	static String getClassName( Object object ) {
217
	    String cn = object.getClass().getName();
218
	    return cn.substring( cn.lastIndexOf('.')+1);
219
	}
220
	
221
	/**
111
	/**
222
	 * A descriptor for an XML configuration element that identifies a class by
112
	 * A descriptor for an XML configuration element that identifies a class by
223
	 * name and optionally its methods.
113
	 * name and optionally its methods.
224
	 */
114
	 */
225
	public static class ObjectDescriptor {
115
	public static class ObjectDescriptor {
226
		/** 
116
117
		private static final MethodValueEntry[] NO_METHODS = new MethodValueEntry[0];
118
119
		private static boolean allMatch(MethodValueEntry[] methods, Object object) {
120
			for (int i = 0, n = methods.length; i < n; ++i)
121
				if (methods[i].matches(object) == false)
122
					return false;
123
124
			return true;
125
		}
126
127
		/**
227
		 * The name of the class.
128
		 * The name of the class.
228
		 */
129
		 */
229
		private String contextClassName;
130
		private final String contextClassName;
230
		
131
231
		/**
132
		/**
232
		 * The ID of the plugin that contains the class.
133
		 * The ID of the plugin that contains the class.
233
		 */
134
		 */
234
		private String contextClassPlugin;
135
		private final String contextClassPlugin;
235
		
136
236
		/**
137
		/**
237
		 * <code>true</code> if a syntax error has occurred,
138
		 * <code>true</code> if a syntax error has occurred,
238
		 * <code>false</code> otherwise.
139
		 * <code>false</code> otherwise.
239
		 */ 
140
		 */
240
		private boolean syntaxError; 
141
		private final boolean syntaxError;
241
		
142
242
		/**
143
		/**
243
		 * A list of method descriptors for the class.
144
		 * A list of method descriptors for the class.
244
		 */
145
		 */
245
		private final List methods;
146
		private final MethodValueEntry[] methods;
246
		
147
247
		/**
148
		/**
248
		 * A list of method descriptors for the class.
149
		 * A list of method descriptors for the class.
249
		 */
150
		 */
250
		private final List staticMethods;
151
		private final MethodValueEntry[] staticMethods;
251
152
252
		/**
153
		/**
253
		 * Creates a new object descriptor from its configuration element.
154
		 * Creates a new object descriptor from its configuration element.
254
		 * 
155
		 *
255
		 * @param configElement
156
		 * @param configElement
256
		 *            The configuration element.
157
		 *            The configuration element.
257
		 */
158
		 */
Lines 261-267 Link Here
261
162
262
		/**
163
		/**
263
		 * Creates a new object descriptor from its configuration element.
164
		 * Creates a new object descriptor from its configuration element.
264
		 * 
165
		 *
265
		 * @param configElement
166
		 * @param configElement
266
		 *            The configuration element.
167
		 *            The configuration element.
267
		 * @param classNameTag
168
		 * @param classNameTag
Lines 270-357 Link Here
270
		public ObjectDescriptor(
171
		public ObjectDescriptor(
271
			IConfigurationElement configElement,
172
			IConfigurationElement configElement,
272
			String classNameTag) {
173
			String classNameTag) {
174
			super();
273
175
274
			 String s = configElement.getAttribute(classNameTag);
176
			String className = null;
275
			 if (s != null) {
177
			String classPlugin = null;
276
				 int start = s.indexOf("(");//$NON-NLS-1$
178
			String s = configElement.getAttribute(classNameTag);
277
				 if (start != -1) {
179
278
					contextClassName = s.substring(0, start).trim();
180
			if (s != null) {
279
				 	int end = s.indexOf(")");//$NON-NLS-1$
181
				int start = s.indexOf('(') + 1;
280
					if (end != -1 && end > start+1)
182
				if (start == 0) {
281
						contextClassPlugin = s.substring(start+1, end);
183
					className = s;
282
				 } else
184
				} else {
283
					contextClassName = s.trim();
185
					className = s.substring(0, start - 1);
284
			 }
186
					int end = s.indexOf(')', start);
285
187
					if (end != -1)
286
			 IConfigurationElement[] methodConfigs =
188
						classPlugin = s.substring(start + 1, end).trim().intern();
287
				configElement.getChildren(METHOD);
288
			 
289
			 IConfigurationElement[] staticMethodConfigs =
290
				configElement.getChildren(STATIC_METHOD);
291
292
			if (methodConfigs.length != 0) {
293
				methods = new ArrayList(methodConfigs.length);
294
				for (int i = 0; i < methodConfigs.length; i++) {
295
					String name = methodConfigs[i].getAttribute(NAME);
296
					if (name != null) {
297
						try {
298
							MethodDescriptor methodDescriptor =
299
								new MethodDescriptor(name);
300
							methodDescriptor.setCall(name.intern());
301
							ValueDescriptor value =
302
								new ValueDescriptor(methodConfigs[i]);
303
							if (value != null)
304
								methods.add(new MethodValueEntry(methodDescriptor, value));
305
						} catch (Exception e) {
306
							syntaxError = true;
307
							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$
308
						}
309
					} else {
310
						syntaxError = true;
311
						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$
312
					}
313
				}
189
				}
314
			} 
190
				className = className.trim().intern();
315
			else
191
			}
316
				methods = Collections.EMPTY_LIST;
192
317
			
193
			contextClassName = className;
318
			
194
			contextClassPlugin = classPlugin;
319
			if (staticMethodConfigs.length != 0) {
195
320
				staticMethods = new ArrayList(staticMethodConfigs.length);
196
			methods = getMethods(configElement, false);
321
				for (int i = 0; i < staticMethodConfigs.length; i++) {
197
			staticMethods = getMethods(configElement, true);
322
					String name = staticMethodConfigs[i].getAttribute(NAME);
198
323
					if (name != null) {
199
			syntaxError = anyNull(methods) || anyNull(staticMethods);
324
						try {
200
		}
325
							StaticMethodDescriptor methodDescriptor =
201
326
								new StaticMethodDescriptor(name);
202
		private static boolean anyNull(MethodValueEntry[] entries) {
327
							methodDescriptor.setCall(name.intern());
203
			for (int i = entries.length; --i >= 0;)
328
							ValueDescriptor value =
204
				if (entries[i] == null)
329
								new ValueDescriptor(staticMethodConfigs[i]);
205
					return true;
330
							if (value != null)
206
331
								staticMethods.add(new MethodValueEntry(methodDescriptor, value));
207
			return false;
332
						} catch (Exception e) {
208
		}
333
							syntaxError = true;
209
334
							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$
210
		private static MethodValueEntry[] getMethods(
335
						}
211
				IConfigurationElement config,
336
					} else {
212
				boolean isStatic) {
337
						syntaxError = true;
213
338
						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$
214
			IConfigurationElement[] elements;
215
			MethodValueEntry[] entries;
216
			int count;
217
218
			elements = config.getChildren(isStatic ? STATIC_METHOD : METHOD);
219
			count = elements.length;
220
221
			if (count == 0) {
222
				entries = NO_METHODS;
223
			} else {
224
				entries = new MethodValueEntry[count];
225
226
				for (int i = 0; i < count; ++i) {
227
					IConfigurationElement element = elements[i];
228
					String name = element.getAttribute(NAME);
229
230
					if (name == null) {
231
						logException(config, "missing method name"); //$NON-NLS-1$
232
						continue;
233
					}
234
235
					try {
236
						MethodDescriptor method;
237
238
						if (isStatic)
239
							method = StaticMethodDescriptor.create(name);
240
						else
241
							method = new MethodDescriptor(name);
242
243
						ValueDescriptor value = new ValueDescriptor(element);
244
						entries[i] = new MethodValueEntry(method, value);
245
					} catch (Exception e) {
246
						logException(config,
247
							"invalid syntax for method [" + name + ']'); //$NON-NLS-1$
339
					}
248
					}
340
				}
249
				}
341
			}else
250
			}
342
				staticMethods = Collections.EMPTY_LIST;
251
343
			
252
			return entries;
344
			
253
		}
345
			
254
346
			if (contextClassName != null)
255
		private static void logException(IConfigurationElement element, String message) {
347
				contextClassName = contextClassName.intern();
256
			IExtension extension = element.getDeclaringExtension();
348
			if (contextClassPlugin != null)
257
			Log.error(
349
				contextClassPlugin = contextClassPlugin.intern();
258
					CommonCorePlugin.getDefault(),
259
					CommonCoreStatusCodes.SERVICE_FAILURE,
260
					extension.getNamespace()
261
						+ ".plugin.xml extension [" //$NON-NLS-1$
262
						+ extension.getExtensionPointUniqueIdentifier()
263
						+ "]: " //$NON-NLS-1$
264
						+ message);
350
		}
265
		}
351
266
352
		/**
267
		/**
353
		 * Tests if the object descriptor applies to the given context object.
268
		 * Tests if the object descriptor applies to the given context object.
354
		 * 
269
		 *
355
		 * @param object
270
		 * @param object
356
		 *            The context object.
271
		 *            The context object.
357
		 * @return <code>true</code> if it applies; <code>false</code>
272
		 * @return <code>true</code> if it applies; <code>false</code>
Lines 361-392 Link Here
361
			if (syntaxError)
276
			if (syntaxError)
362
				return false;
277
				return false;
363
278
364
			Object targetObject = object;
279
			if (contextClassName != null && object != null) {
365
			if (contextClassName != null) {
366
				if (!isAssignableTo(object.getClass(), contextClassName)) {
280
				if (!isAssignableTo(object.getClass(), contextClassName)) {
367
					targetObject = getAdapter(object, contextClassName, contextClassPlugin);
281
					object = getAdapter(object, contextClassName, contextClassPlugin);
368
					if (targetObject == null)
282
					if (object == null)
369
						return false;
283
						return false;
370
				}
284
				}
371
			} 
372
			
373
			for(Iterator iter = methods.iterator(); iter.hasNext();) {
374
				MethodValueEntry entry = (MethodValueEntry)iter.next();
375
				Object methodValue = invokeMethod(entry.method, targetObject);
376
				
377
				if (methodValue == null || !entry.value.sameAs(methodValue))
378
					return false;
379
			}
285
			}
380
			
286
381
			for(Iterator iter = staticMethods.iterator(); iter.hasNext();) {
287
			return allMatch(methods, object)
382
				MethodValueEntry entry = (MethodValueEntry)iter.next();
288
				? allMatch(staticMethods, object)
383
				Object methodValue = invokeStaticMethod((StaticMethodDescriptor)entry.method, targetObject);
289
				: false;
384
				
290
		}
385
				if (methodValue == null || !entry.value.sameAs(methodValue))
291
	}
386
					return false;
292
293
	/**
294
	 * A simple string parser used to decompose method and value descriptors.
295
	 *
296
	 * @author keithc
297
	 */
298
	private static final class SimpleParser {
299
300
		private final String input;
301
302
		private int offset;
303
304
		public SimpleParser(String input) {
305
			super();
306
			this.input = input;
307
			this.offset = 0;
308
		}
309
310
		/**
311
		 * Return the next portion of the input string up to but not including
312
		 * the given delimiter character.
313
		 *
314
		 * @param delimiter
315
		 *            the delimiter
316
		 * @param delimiterRequired
317
		 *            is the delimiter required?
318
		 * @return String the token
319
		 * @throws IllegalArgumentException
320
		 *             if the delimiter is required but not found
321
		 */
322
		public String nextToken(char delimiter, boolean delimiterRequired) {
323
			int start = offset;
324
			int end = safeIndexOf(delimiter);
325
326
			if (end >= 0) {
327
				offset = end + 1;
328
			} else {
329
				if (delimiterRequired)
330
					throw new IllegalArgumentException();
331
332
				end = input.length();
333
				offset = end;
334
			}
335
336
			return unescape(start, end, delimiter);
337
		}
338
339
		/**
340
		 * Return the next portion of the input string up to but not including
341
		 * the last occurence of the first delimiter character that appears
342
		 * before the next occurence of the second delimiter character.
343
		 *
344
		 * @param delim1
345
		 *            the first delimiter character
346
		 * @param delim2
347
		 *            the second delimiter character
348
		 * @return String the token
349
		 * @throws IllegalArgumentException
350
		 *             if the delimiters are not found
351
		 */
352
		public String nextTokenBefore(char delim1, char delim2) {
353
			int start = offset;
354
			int end = safeIndexOf(delim2);
355
			if (end < 0)
356
				throw new IllegalArgumentException();
357
358
			end = safeLastIndexOf(delim1, end);
359
			offset = end + 1;
360
361
			return unescape(start, end, delim1);
362
		}
363
364
		public boolean moreInput() {
365
			return offset < input.length();
366
		}
367
368
		private int safeIndexOf(char delim) {
369
			for (int start = offset;;) {
370
				int index = input.indexOf(delim, start);
371
				if (index <= start || input.charAt(index - 1) != '\\')
372
					return index;
373
				start = index + 1;
374
			}
375
		}
376
377
		private int safeLastIndexOf(char delim, int end) {
378
			for (;;) {
379
				int index = input.lastIndexOf(delim, end);
380
				if (index < offset)
381
					throw new IllegalArgumentException();
382
				if (index == offset || input.charAt(index - 1) != '\\')
383
					return index;
384
				end = index = 1;
387
			}
385
			}
388
			
386
		}
389
			return true;
387
388
		public String tail() {
389
			return input.substring(offset);
390
		}
391
392
		private String unescape(int start, int end, char delimiter) {
393
			StringBuffer buffer = null;
394
395
			// don't consider the last character in the loop
396
			end -= 1;
397
398
			for (int index = start; index < end; ++index) {
399
				char c = input.charAt(index);
400
401
				if (c != '\\' || input.charAt(index + 1) != delimiter)
402
					continue;
403
404
				if (buffer == null)
405
					buffer = new StringBuffer(input.substring(0, index));
406
				else
407
					buffer.append(input.substring(start, index));
408
			}
409
410
			String result = input.substring(start, end + 1);
411
412
			if (buffer != null)
413
				result = buffer.append(result).toString();
414
415
			return result;
390
		}
416
		}
391
	}
417
	}
392
418
Lines 395-441 Link Here
395
	 * name and its formal parameters.
421
	 * name and its formal parameters.
396
	 */
422
	 */
397
	private static class MethodDescriptor {
423
	private static class MethodDescriptor {
398
		
424
425
		/**
426
		 * An empty array of types and values for methods with no parameters.
427
		 */
428
		private static final Class[] NO_PARAMETERS = new Class[0];
429
399
		/**
430
		/**
400
		 * The method call.
431
		 * A cache for information about methods. Keyed by (class, method
432
		 * signature), the corresponding value is either a Method or
433
		 * Boolean.FALSE signifying that no accessible method exists.
434
		 *
435
		 * We use a WeakHashMap to allow classes to be unloaded.
401
		 */
436
		 */
402
		private String call;
437
		private static final Map methodCache = new WeakHashMap();
403
		
438
404
		/**
439
		/**
405
		 * The method name.
440
		 * The method name.
406
		 */
441
		 */
407
		private String name;
442
		protected final String name;
408
		
443
409
		/**
444
		/**
410
		 * The array of method parameters.
445
		 * The array of method parameters.
411
		 */
446
		 */
412
		private Object parameterObjects[];
447
		protected final Object[] parameterObjects;
413
		
448
414
		/**
449
		/**
415
		 * The array of method parameter types.
450
		 * The array of method parameter types.
416
		 */
451
		 */
417
		private Class parameterTypes[];
452
		protected final Class[] parameterTypes;
418
		
453
419
		/**
454
		/**
420
		 * The next cascading method descriptor.
455
		 * The next cascading method descriptor.
421
		 */
456
		 */
422
		private MethodDescriptor next;
457
		protected final MethodDescriptor next;
423
458
424
		/**
459
		/**
425
		 * The list of method parameters.
426
		 */
427
		private List parameters;
428
		
429
		/**
430
		 * the method signature
460
		 * the method signature
431
		 *
432
		 */
461
		 */
433
		private String signature = null;
462
		private final MethodSignature signature;
434
		
435
		
436
		protected MethodDescriptor(){
437
			// empty 
438
		}
439
463
440
		/**
464
		/**
441
		 * Creates a new method descriptor from a string representing the
465
		 * Creates a new method descriptor from a string representing the
Lines 447-739 Link Here
447
		 * <P>
471
		 * <P>
448
		 * Where:
472
		 * Where:
449
		 * <UL>
473
		 * <UL>
450
		 * <LI>the <i>params </i> are comma-separated string literals without
474
		 * <LI>the <i>params</i> are comma-separated string literals without
451
		 * double quotes.</LI>
475
		 * double quotes.</LI>
452
		 * <LI>only string <i>params </i> are allowed (no texual representation
476
		 * <LI>only string <i>params</i> are allowed (no texual representation
453
		 * of non-string params are allowed)</LI>
477
		 * of non-string params are allowed)</LI>
454
		 * </UL>
478
		 * </UL>
455
		 * <P>
479
		 * <P>
456
		 * For example:
480
		 * For example:
457
		 * <P>
481
		 * <P>
458
		 * <code>getPropertyValue(Source_Connection).getName()</code>
482
		 * <code>getPropertyValue(Source_Connection).getName()</code>
459
		 * 
483
		 *
460
		 * @param string
484
		 * @param string
461
		 *            the method invocation string
485
		 *            the method invocation string
462
		 */
486
		 */
463
		public MethodDescriptor(String string) {
487
		public MethodDescriptor(String string) {
464
			// set method name
488
			super();
465
			string = parseName(string.trim());
489
490
			SimpleParser parser = new SimpleParser(string);
491
492
			name = parser.nextToken('(', true).trim().intern();
493
466
			// set method parameters
494
			// set method parameters
467
			string = parseParameterList(string.trim());
495
			List parameters = new ArrayList();
496
			String parmString = parser.nextToken(')', true).trim();
497
			SimpleParser parmParser = new SimpleParser(parmString);
498
499
			while (parmParser.moreInput())
500
				parameters.add(parmParser.nextToken(',', false));
468
501
469
			// fill the parameter objects and types arrays
502
			// fill the parameter objects and types arrays
470
			if (parameters != null && !parameters.isEmpty()) {
503
			int parmCount = parameters.size();
471
				Collections.reverse(parameters);
504
472
				parameterObjects = parameters.toArray();
505
			if (parmCount == 0) {
473
				parameterTypes = new Class[parameterObjects.length];
506
				parameterObjects = NO_PARAMETERS;
474
				for (int i = 0; i < parameterObjects.length; i++) {
507
				parameterTypes = NO_PARAMETERS;
475
					String p = (String) parameterObjects[i];
508
			} else {
476
					int objIndex = p.indexOf("[object]"); //$NON-NLS-1$
509
				parameterObjects = new Object[parmCount];
477
					boolean isObject = objIndex >= 0;
510
				parameterTypes = new Class[parmCount];
478
					int parseAsIndex = p.indexOf(":::"); //$NON-NLS-1$
511
				for (int index = 0; index < parmCount; ++index)
479
					try {
512
					setParameterAndType((String) parameters.get(index), index);
480
						if (isObject && (parseAsIndex >= 0))
513
			}
481
							// assume order: [object] before type:::param
514
482
							assert (objIndex < parseAsIndex);
515
			string = parser.tail().trim();
483
						if (parseAsIndex >= 0) {
516
484
							// "type:::param"
517
			if (string.length() == 0)
485
							String parseAs =
518
				next = null;
486
								p.substring((isObject ? 8 : 0), parseAsIndex);
519
			else if (string.charAt(0) == '.')
487
							String value =
520
				next = new MethodDescriptor(string.substring(1).trim());
488
								p.substring(parseAsIndex + 3, p.length());
521
			else
489
							if (parseAs.equalsIgnoreCase("int")) { //$NON-NLS-1$
522
				throw new IllegalArgumentException();
490
								parameterTypes[i] = Integer.class;
523
491
								parameterObjects[i] = Integer.decode(value);
524
			signature = new MethodSignature(this);
492
							} else if (parseAs.equalsIgnoreCase("bool")) { //$NON-NLS-1$
525
		}
493
								parameterTypes[i] = Boolean.class;
526
494
								parameterObjects[i] = Boolean.valueOf(value);
527
		protected void setParameterAndType(String parameter, int index) {
495
							} else if (parseAs.equalsIgnoreCase("double")) { //$NON-NLS-1$
528
			boolean isObject = parameter.startsWith("[object]"); //$NON-NLS-1$
496
								parameterTypes[i] = Double.class;
529
			int parseAsIndex = parameter.indexOf(":::"); //$NON-NLS-1$
497
								parameterObjects[i] = Double.valueOf(value);
530
			Class clazz;
498
							}
531
			Object object;
499
							// if [object] present, set type to Object
532
500
							if (isObject)
533
			try {
501
								parameterTypes[i] = Object.class;
534
				if (parseAsIndex >= 0) { // "type:::param"
502
						} else if (isObject) { // "[object]param"
535
					String type = parameter.substring(isObject ? 8 : 0, parseAsIndex);
503
							String value = p.substring(8, p.length());
536
					String value = parameter.substring(parseAsIndex + 3);
504
							parameterTypes[i] = Object.class;
537
505
							parameterObjects[i] = value;
538
					if (type.equalsIgnoreCase("int")) { //$NON-NLS-1$
506
						} else // "param"
539
						clazz = Integer.class;
507
							parameterTypes[i] = String.class;
540
						object = Integer.decode(value);
508
					} catch (Exception e) {
541
					} else if (type.equalsIgnoreCase("bool")) { //$NON-NLS-1$
509
						String value =
542
						clazz = Boolean.class;
510
							p.substring(
543
						object = Boolean.valueOf(value);
511
								((parseAsIndex >= 0) ? parseAsIndex + 3 : 0),
544
					} else if (type.equalsIgnoreCase("double")) { //$NON-NLS-1$
512
								p.length());
545
						clazz = Double.class;
513
						parameterObjects[i] = value;
546
						object = Double.valueOf(value);
514
						parameterTypes[i] = String.class;
547
					} else {
548
						clazz = Object.class;
549
						object = value;
515
					}
550
					}
551
					// if [object] present, set type to Object
552
					if (isObject)
553
						clazz = Object.class;
554
				} else if (isObject) { // "[object]param"
555
					clazz = Object.class;
556
					object = parameter.substring(8, parameter.length());
557
				} else { // "param"
558
					clazz = String.class;
559
					object = parameter;
560
				}
561
			} catch (Exception e) {
562
				if (parseAsIndex >= 0)
563
					object = parameter.substring(parseAsIndex + 3);
564
				else
565
					object = parameter;
566
				clazz = String.class;
567
			}
568
569
			parameterTypes[index] = clazz;
570
			parameterObjects[index] = object;
571
		}
572
573
		protected final Method getMethod(Class clazz) {
574
			Map classMap = getSubMap(methodCache, clazz);
575
			Object candidate = classMap.get(signature);
576
			Method method;
577
578
			if (candidate instanceof Method) {
579
				method = (Method) candidate;
580
			} else if (candidate == null) {
581
				try {
582
					method = clazz.getMethod(name, parameterTypes);
583
					classMap.put(signature, method);
584
				} catch (Exception e) {
585
					classMap.put(signature, Boolean.FALSE);
586
					method = null;
516
				}
587
				}
588
			} else {
589
				method = null;
517
			}
590
			}
518
			parameters = null;
519
591
520
			// set method parameters
592
			return method;
521
			if (string.length() != 0) {
522
				if (string.charAt(0) != '.')
523
					throw new IllegalArgumentException();
524
				next = new MethodDescriptor(string.substring(1).trim());
525
			}
526
			
527
		 if (this.name != null)
528
				name = name.intern();
529
		}
593
		}
530
594
531
		/**
595
		public Object invoke(Object object) {
532
		 * Parses and returns the method name in a method invocation string.
596
			for (MethodDescriptor desc = this; object != null;) {
533
		 * 
597
				Method method = getMethod(object.getClass());
534
		 * @param string
535
		 *            the method invocation string
536
		 * @return the method name
537
		 */
538
		protected String parseName(String string) {
539
			int index = string.indexOf('(');
540
			if (index == -1)
541
				throw new IllegalArgumentException(); //$NON-NLS-1$
542
			name = string.substring(0, index).trim();
543
			return string.substring(index + 1);
544
		}
545
598
546
		/**
599
				if (method == null)
547
		 * Parses a method invocation string for the list of parameters, which
600
					break;
548
		 * are placed in the <code>parameters</code> field.
601
549
		 * 
602
				try {
550
		 * @param string
603
					object = method.invoke(object, desc.parameterObjects);
551
		 *            the method invocation string
604
				} catch (Exception e) {
552
		 * @return the end part of the method invocation string that has not
605
					break;
553
		 *         been parsed.
606
				}
554
		 */
607
555
		protected String parseParameterList(String string) {
608
				if ((desc = desc.next) == null)
556
			int index = -1;
609
					return object;
557
			String paramStr = null;
558
			while (paramStr == null) {
559
				index = string.indexOf(')', index + 1);
560
				if (index == -1)
561
					throw new IllegalArgumentException(); //$NON-NLS-1$
562
				if (index == 0 || string.charAt(index - 1) != '\\')
563
					paramStr = string.substring(0, index);
564
			}
565
			if (paramStr.length() != 0) {
566
				parameters = new ArrayList();
567
				parseParameters(paramStr.trim());
568
			}
610
			}
569
			return string.substring(index + 1);
570
		}
571
611
572
		/**
612
			return null;
573
		 * Parses a string containing a list of method parameters and stores
574
		 * them in the <code>parameters</code> field.
575
		 * 
576
		 * @param string
577
		 *            the comma-separated list of method parameters.
578
		 */
579
		private void parseParameters(String string) {
580
			int index = string.indexOf(',');
581
			if (index != -1 && string.charAt(index - 1) != '\\') {
582
				parseParameters(string.substring(index + 1).trim());
583
				parameters.add(string.substring(0, index));
584
			} else
585
				parameters.add(string);
586
		}
613
		}
614
	}
587
615
588
		/**
616
	private static final class MethodSignature {
589
		 * Returns the method name.
590
		 * 
591
		 * @return the method name
592
		 */
593
		public String getName() {
594
			return name;
595
		}
596
		
597
		/**
598
		 * Sets the method name.
599
		 * @param the method name
600
		 */
601
		public void setName(String name) {
602
			this.name = name;
603
		}
604
617
605
		/**
618
		private final int hashCode;
606
		 * Returns an array of string params.
619
		private final MethodDescriptor method;
607
		 * 
608
		 * @return the parameters
609
		 */
610
		public Object[] getParameters() {
611
			return parameterObjects;
612
		}
613
620
614
		/**
621
		public MethodSignature(MethodDescriptor method) {
615
		 * Returns an array of parameter classes.
622
			super();
616
		 * 
617
		 * @return the parameter types
618
		 */
619
		public Class[] getParameterTypes() {
620
			return parameterTypes;
621
		}
622
		
623
		/**
624
		 * sets the the array of params.
625
		 * @param paramters
626
		 */
627
		protected void setParameters(Object[] paramters) {
628
			parameterObjects = paramters;
629
		}
630
623
631
		/**
624
			int hash = method.name.hashCode();
632
		 * sets the the array of parameter types.
625
			Class[] types = method.parameterTypes;
633
		 * @param paramtersTypes
634
		 */
635
		public void setParameterTypes(Class[] paramterTypes) {
636
			this.parameterTypes = paramterTypes;
637
		}
638
626
639
		/**
627
			for (int i = 0, n = types.length; i < n; ++i)
640
		 * Returns the next cascading method descriptor, if any.
628
				hash = HashUtil.hash(hash, types[i]);
641
		 * 
642
		 * @return the next method descriptor, or <code>null</code> if there
643
		 *         is none
644
		 */
645
		public MethodDescriptor getNext() {
646
			return next;
647
		}
648
		
649
		/**
650
		 * sets the next cascading method descriptor, if any.
651
		 * @param next
652
		 */
653
		protected void setNext(MethodDescriptor next) {
654
			this.next = next;
655
		}
656
629
657
		/**
630
			this.hashCode = hash;
658
		 * Gets the method call.
631
			this.method = method;
659
		 * 
660
		 * @return the method call.
661
		 */
662
		public String getCall() {
663
			return call;
664
		}
665
		
666
		/**
667
		 * Sets the method call.
668
		 * 
669
		 * @param call
670
		 *            the new method call
671
		 */
672
		public void setCall(String call) {
673
			this.call = call;
674
		}
675
		
676
		/**
677
		 * Gets the Paramters List
678
		 * @return The list of method parameters.
679
		 */
680
		protected List getParamtersList(){
681
			return parameters;
682
		}
632
		}
683
		
633
684
		/**
634
		public boolean equals(Object other) {
685
		 * Sets the Paramters List
635
			if (other == this)
686
		 * @param Parameterlist
636
				return true;
687
		 */
637
688
		protected void setParamtersList(List parameters1){
638
			if (other instanceof MethodSignature == false)
689
			this.parameters = parameters1;
639
				return false;
640
641
			MethodSignature otherSignature = (MethodSignature) other;
642
643
			if (hashCode != otherSignature.hashCode)
644
				return false;
645
646
			MethodDescriptor thisMethod = this.method;
647
			MethodDescriptor thatMethod = otherSignature.method;
648
649
			if (thisMethod.name == thatMethod.name)
650
				return false;
651
652
			Class[] theseTypes = thisMethod.parameterTypes;
653
			Class[] thoseTypes = thatMethod.parameterTypes;
654
			int typeCount = theseTypes.length;
655
656
			if (typeCount != thoseTypes.length)
657
				return false;
658
659
			for (int i = 0; i < typeCount; ++i)
660
				if (theseTypes[i] != thoseTypes[i])
661
					return false;
662
663
			return true;
690
		}
664
		}
691
		
665
692
		/**
666
		public int hashCode() {
693
		 * utility method used to get the signature of the method this method descriptor
667
			return hashCode;
694
		 * descripe.
695
		 * @return the signature of the method
696
		 */
697
		public String getSignature(){
698
			if (this.signature==null){
699
				StringBuffer sb = 
700
					new StringBuffer();
701
				sb.append(name);
702
				sb.append('(');
703
				if(parameterTypes!=null)
704
					for(int index= 0 ; index < parameterTypes.length ; index++){
705
						Class clazz = parameterTypes[index];
706
						sb.append(clazz.getName());
707
						if(index<parameterTypes.length-1)
708
							sb.append(',');
709
					}
710
				sb.append(')');
711
				signature = sb.toString();
712
			}
713
			return signature;
714
			
715
		}
668
		}
716
	}
669
	}
717
670
718
	
671
	private static final class StaticMethodDescriptor extends MethodDescriptor {
719
	private static class StaticMethodDescriptor extends MethodDescriptor {
672
720
		/**
673
		private static boolean anyContextParameters(Object[] parameterObjects) {
721
		 * the plugin Name
674
			for (int index = parameterObjects.length; --index >= 0;)
722
		 */
675
				if (parameterObjects[index] == contextParam)
723
		private String pluginID;
676
					return true;
724
		
677
725
		/**
678
			return false;
726
		 * the Class Name
679
		}
727
		 */
680
728
		private String className;
729
		
730
		/**
681
		/**
731
		 * Creates a new method descriptor from a string representing the
682
		 * Creates a new method descriptor from a string representing the
732
		 * method's full cascading invocation with parameters.
683
		 * method's full cascading invocation with parameters.
733
		 * <P>
684
		 * <P>
734
		 * The format of the string is:
685
		 * The format of the string is:
735
		 * <P>
686
		 * <P>
736
		 * <code>PluginID\ClassName.method_name([params])[.method_name([params])]*</code>
687
		 * <code>PluginID/ClassName.method_name([params])[.method_name([params])]*</code>
737
		 * <P>
688
		 * <P>
738
		 * Where:
689
		 * Where:
739
		 * <UL>
690
		 * <UL>
Lines 746-1017 Link Here
746
		 * <P>
697
		 * <P>
747
		 * For example:
698
		 * For example:
748
		 * <P>
699
		 * <P>
749
		 * <code>MyPluginID\MyClass.MyStaticFunction(%,"some value")</code>
700
		 * <code>MyPluginID/MyClass.MyStaticFunction(%Context,"some value")</code>
750
		 * 
701
		 *
751
		 * @param string
702
		 * @param string
752
		 *            the method invocation string
703
		 *            the method invocation string
753
		 */
704
		 */
754
		public StaticMethodDescriptor(String string) {
705
		public static StaticMethodDescriptor create(String string) {
755
			// set plugin ID
706
			SimpleParser parser = new SimpleParser(string);
756
			string = parsePluginID(string.trim());
757
			// set class Name 
758
			string = parseClassName(string.trim());
759
			// set method name
760
			string = parseName(string.trim());
761
			// set method parameters
762
			string = parseParameterList(string.trim());
763
707
764
			List parameters = getParamtersList();
708
			// get plugin ID
765
			
709
			String pluginId = parser.nextToken('/', true);
766
			// fill the parameter objects and types arrays
767
			if (parameters != null && !parameters.isEmpty()) {
768
				Collections.reverse(parameters);
769
				Object[] parameterObjects = parameters.toArray();
770
				Class[] parameterTypes = new Class[parameterObjects.length];
771
				for (int i = 0; i < parameterObjects.length; i++) {
772
					String p = (String) parameterObjects[i];
773
					int objIndex = p.indexOf("[object]"); //$NON-NLS-1$
774
					boolean isObject = objIndex >= 0;
775
					int parseAsIndex = p.indexOf(":::"); //$NON-NLS-1$
776
					try {
777
						if (isObject && (parseAsIndex >= 0))
778
							// assume order: [object] before type:::param
779
							assert (objIndex < parseAsIndex);
780
						if (parseAsIndex >= 0) {
781
							// "type:::param"
782
							String parseAs =
783
								p.substring((isObject ? 8 : 0), parseAsIndex);
784
							String value =
785
								p.substring(parseAsIndex + 3, p.length());
786
							if (parseAs.equalsIgnoreCase("int")) { //$NON-NLS-1$
787
								parameterTypes[i] = Integer.class;
788
								parameterObjects[i] = Integer.decode(value);
789
							} else if (parseAs.equalsIgnoreCase("bool")) { //$NON-NLS-1$
790
								parameterTypes[i] = Boolean.class;
791
								parameterObjects[i] = Boolean.valueOf(value);
792
							} else if (parseAs.equalsIgnoreCase("double")) { //$NON-NLS-1$
793
								parameterTypes[i] = Double.class;
794
								parameterObjects[i] = Double.valueOf(value);
795
							}
796
							// if [object] present, set type to Object
797
							if (isObject)
798
								parameterTypes[i] = Object.class;
799
						} else if (isObject) { // "[object]param"
800
							String value = p.substring(8, p.length());
801
							parameterTypes[i] = Object.class;
802
							parameterObjects[i] = value;
803
						} else if (p.startsWith(contextParam)){// "param" //$NON-NLS-1$
804
							parameterTypes[i] = getParameterType(p);
805
							parameterObjects[i] = "%Context"; //$NON-NLS-1$
806
						}
807
						else
808
							parameterTypes[i] = String.class;
809
					} catch (Exception e) {
810
						String value =
811
							p.substring(
812
								((parseAsIndex >= 0) ? parseAsIndex + 3 : 0),
813
								p.length());
814
						parameterObjects[i] = value;
815
						parameterTypes[i] = String.class;
816
					}
817
				}
818
				setParameters(parameterObjects);
819
				setParameterTypes(parameterTypes);
820
			}
821
			parameters = null;
822
710
823
			// set method parameters
711
			// get class name
824
			if (string.length() != 0) {
712
			String className = parser.nextTokenBefore('.', '(');
825
				if (string.charAt(0) != '.')
713
826
					throw new IllegalArgumentException();
714
			return new StaticMethodDescriptor(pluginId, className, parser.tail());
827
				setNext(new MethodDescriptor(string.substring(1).trim()));
828
			}
829
			
830
		 if (getName() != null)
831
				setName(getName().intern());
832
		}
833
834
		
835
		/**
836
		 * parse the passed paramter to extract the paramter's class
837
		 * @param p		the parapemter 
838
		 * @return
839
		 */
840
		private Class getParameterType(String parameter) {
841
			int startIndex = parameter.indexOf("["); //$NON-NLS-1$
842
			int endIndex = parameter.indexOf("]"); //$NON-NLS-1$
843
			if(startIndex==-1 || endIndex==-1)
844
				throw new IllegalArgumentException(); //$NON-NLS-1$
845
			String parameterTypeString= parameter.substring(startIndex+1,endIndex).trim();
846
			
847
			endIndex = parameterTypeString.indexOf('/');
848
			if(endIndex==-1 || endIndex==parameterTypeString.length()-1)
849
				throw new IllegalArgumentException(); //$NON-NLS-1$
850
			String parameterPluginID = parameterTypeString.substring(0,endIndex).trim();
851
			String parameterClassName = parameterTypeString.substring(endIndex + 1);
852
			Class clazz = loadClass(parameterClassName,parameterPluginID);
853
			if(clazz==null)
854
				clazz =  Object.class;
855
			return clazz;
856
		}
715
		}
857
716
858
		/**
717
		/**
859
		 * Parses and returns the Plugin ID in a method invocation string.
718
		 * Does this object have any "%Context" parameters?
860
		 * 
719
		 */
861
		 * @param string
720
		private final boolean hasContextParameters;
862
		 *            the method invocation string
721
863
		 * @return the plugin name
722
		/**
723
		 * the Class Name
864
		 */
724
		 */
865
		private String parsePluginID(String string) {
725
		public final String className;
866
			int index = string.indexOf('/');
726
867
			if (index == -1)
727
		/**
868
				throw new IllegalArgumentException(); //$NON-NLS-1$
728
		 * the plugin Name
869
			pluginID = string.substring(0, index).trim();
729
		 */
870
			return string.substring(index + 1);
730
		public final String pluginID;
731
732
		private StaticMethodDescriptor(
733
				String pluginID,
734
				String className,
735
				String method) {
736
			super(method);
737
			this.className = className.intern();
738
			this.pluginID = pluginID.intern();
739
			this.hasContextParameters = anyContextParameters(parameterObjects);
740
		}
741
742
		private Object[] getParametersFor(Object context) {
743
			Object[] parameters = parameterObjects;
744
745
			if (hasContextParameters) {
746
				parameters = (Object[]) parameters.clone();
747
748
				for (int i = parameters.length; --i >= 0;)
749
					if (parameters[i] == contextParam)
750
						parameters[i] = context;
751
			}
752
753
			return parameters;
871
		}
754
		}
872
		
755
756
		protected void setParameterAndType(String parameter, int index) {
757
			if (!parameter.startsWith(contextParam)) {
758
				super.setParameterAndType(parameter, index);
759
				return;
760
			}
761
762
			SimpleParser parser = new SimpleParser(parameter);
763
764
			// parse "%Context[pluginID/className]"
765
766
			parser.nextToken('[', true); // skip "%Context"
767
768
			String parmPluginID = parser.nextToken('/', true).trim();
769
			String parmClassName = parser.nextToken(']', true).trim();
770
771
			Class type = loadClass(parmClassName, parmPluginID); // TODO delay loading classes
772
773
			if (type == null)
774
				type = Object.class;
775
776
			parameterTypes[index] = type;
777
			parameterObjects[index] = contextParam;
778
		}
779
873
		/**
780
		/**
874
		 * Parses and returns the Plugin ID in a method invocation string.
781
		 * A utility method to invoke a cascading list of methods.
875
		 * 
782
		 *
876
		 * @param string
783
		 * @param object
877
		 *            the method invocation string
784
		 *            The context object to use (it could be null)
878
		 * @return the plugin name
785
		 * @return the value of the invokation
879
		 */
786
		 */
880
		private String parseClassName(String string) {
787
		public Object invoke(Object object) {
881
			int index = string.indexOf('(');
788
			Class clazz = loadClass(className, pluginID);
882
			if (index == -1)
789
883
				throw new IllegalArgumentException(); //$NON-NLS-1$
790
			if (clazz == null)
884
			index = string.lastIndexOf('.',index);
791
				return null;
885
			if (index == -1)
792
886
				throw new IllegalArgumentException(); //$NON-NLS-1$
793
			Method method = getMethod(clazz);
887
			className = string.substring(0, index).trim();
794
888
			return string.substring(index + 1);
795
			if (method == null)
889
		}
796
				return null;
890
		
797
891
	public String getPluginID(){
798
			try {
892
			return pluginID;
799
				object = method.invoke(null, getParametersFor(object));
893
		}
800
			} catch (Exception e) {
894
		
801
				return null;
895
		public String getClassName(){
802
			}
896
			return className;
803
804
			return next == null ? object : next.invoke(object);
897
		}
805
		}
898
	
806
 	}
899
	}
807
900
	
901
	
902
	/**
808
	/**
903
	 * A descriptor for an XML configuration element that identifies a method
809
	 * A descriptor for an XML configuration element that identifies a method
904
	 * result by its type and <code>toString()</code> value.
810
	 * result by its type and <code>toString()</code> value.
905
	 */
811
	 */
906
	private static class ValueDescriptor {
812
	private static final class ValueDescriptor {
907
		
813
908
		/**
814
		/**
909
		 * The valid value literals.
815
		 * The value literals map. Keyed by a string, the corresponding value
816
		 * indicates whether that value is permitted or disallowed.
910
		 */
817
		 */
911
		private Set valueLiterals;
818
		private final Map valueMap;
912
		
819
913
		/**
820
		/**
914
		 * The invalid valud literals.
821
		 * Are there any permitted values specified?
915
		 */
822
		 */
916
		private Set notValueLiterals;
823
		private final boolean valuesSpecified;
917
		
824
918
		/**
825
		/**
919
		 * The valid value objects.
826
		 * The valid value objects.
920
		 */
827
		 */
921
		private List valueObjects;
828
		private final ObjectDescriptor[] valueObjects;
922
		
829
923
		/**
830
		/**
924
		 * The invalid value objects.
831
		 * The invalid value objects.
925
		 */
832
		 */
926
		private List notValueObjects;
833
		private final ObjectDescriptor[] notValueObjects;
927
834
928
		/**
835
		/**
929
		 * Creates a new value descriptor from its configuration element.
836
		 * Creates a new value descriptor from its configuration element.
930
		 * 
837
		 *
931
		 * @param configElement
838
		 * @param element
932
		 *            The configuration element.
839
		 *            The configuration element.
933
		 */
840
		 */
934
		public ValueDescriptor(IConfigurationElement configElement) {
841
		public ValueDescriptor(IConfigurationElement element) {
935
			valueLiterals = new HashSet();
842
			super();
936
			String s = configElement.getAttribute(VALUE);
843
			valueObjects = parseObjects(element.getChildren(VALUE));
937
			if (s != null)
844
			notValueObjects = parseObjects(element.getChildren(NOT_VALUE));
938
				parseValueLiteralString(s, valueLiterals);
845
939
846
			Map literals = new HashMap();
940
			notValueLiterals = new HashSet();
847
941
			s = configElement.getAttribute(NOT_VALUE);
848
			// For backwards compatibility when values and not-values overlap
942
			if (s != null)
849
			// we add not-values first so values continue to take precedence.
943
				parseValueLiteralString(s, notValueLiterals);
850
			addLiterals(element.getAttribute(NOT_VALUE), literals, Boolean.FALSE);
944
851
			valuesSpecified = addLiterals(element.getAttribute(VALUE), literals, Boolean.TRUE);
945
			IConfigurationElement[] valueConfigs = configElement.getChildren(VALUE);
852
946
			valueObjects = new ArrayList(valueConfigs.length);
853
			switch (literals.size()) {
947
			for (int i=0; i<valueConfigs.length; i++)
854
				case 0:
948
				valueObjects.add(new ObjectDescriptor(valueConfigs[i]));
855
					valueMap = null;
949
856
					break;
950
			IConfigurationElement[] notValueConfigs = configElement.getChildren(NOT_VALUE);
857
				case 1:
951
			notValueObjects = new ArrayList(notValueConfigs.length);
858
					Entry entry = (Entry) literals.entrySet().iterator().next();
952
			for (int i=0; i<notValueConfigs.length; i++)
859
					valueMap = Collections.singletonMap(entry.getKey(), entry.getValue());
953
				notValueObjects.add(new ObjectDescriptor(notValueConfigs[i]));
860
					break;
954
		}
861
				default:
955
		
862
					valueMap = literals;
956
		/**
863
					break;
957
		 * Parse the string <code>s</code>, which is a comma-separated list
958
		 * of value literals and place them in the given <code>list</code>.
959
		 * 
960
		 * @param s
961
		 *            the string to be parsed
962
		 * @param list
963
		 *            the set of literal string values from <code>s</code>.
964
		 */
965
		private void parseValueLiteralString(String s, Set list) {
966
			// parse the string comma-separated string literals ignoring escaped commas
967
			int start = 0;
968
			int end = s.indexOf(',');
969
			while (end != -1) {
970
				if (s.charAt(end-1) == '\\') {
971
					s = s.substring(0, end-1) + s.substring(end);
972
					end = s.indexOf(',', end);
973
					continue;
974
				}
975
				list.add(s.substring(start, end).trim().intern());
976
				start = end +1;
977
				end = s.indexOf(',', start);
978
			}
864
			}
979
			list.add(s.substring(start).trim().intern());
980
		}
865
		}
981
		
866
867
		/**
868
		 * Parse the given string, which is a comma-separated list of value
869
		 * literals and use them as keys in the given map where value is to be
870
		 * stored.
871
		 *
872
		 * @param string
873
		 *            the string of literals
874
		 * @param literals
875
		 *            the map in which the entries are to be stored
876
		 * @param value
877
		 *            the value to be associated with each literal
878
		 * @return boolean whether the string was non-null
879
		 */
880
		private static boolean addLiterals(String string, Map literals, Boolean value) {
881
			if (string == null)
882
				return false;
883
884
			// parse the string comma-separated string literals
885
			// (ignoring escaped commas)
886
			SimpleParser parser = new SimpleParser(string);
887
888
			do {
889
				String token = parser.nextToken(',', false);
890
891
				literals.put(token.trim().intern(), value);
892
			} while (parser.moreInput());
893
894
			return true;
895
		}
896
897
		private static ObjectDescriptor[] parseObjects(IConfigurationElement[] configs) {
898
			int length = configs.length;
899
900
			if (length == 0)
901
				return null;
902
903
			ObjectDescriptor[] descriptors = new ObjectDescriptor[length];
904
905
			for (int i = 0; i < length; ++i)
906
				descriptors[i] = new ObjectDescriptor(configs[i]);
907
908
			return descriptors;
909
		}
910
982
		/**
911
		/**
983
		 * Returns <code>true</code> if I am the same as <code>object</code>,
912
		 * Returns <code>true</code> if I am the same as <code>object</code>,
984
		 * <code>false</code> otherwise.
913
		 * <code>false</code> otherwise.
985
		 * 
914
		 *
986
		 * @param object
915
		 * @param object
987
		 *            the object to be tested
916
		 *            the object to be tested
988
		 * @return <code>true</code> if I am the same as <code>object</code>,
917
		 * @return <code>true</code> if I am the same as <code>object</code>,
989
		 *         <code>false</code> otherwise.
918
		 *         <code>false</code> otherwise.
990
		 */
919
		 */
991
		public boolean sameAs(Object object) {
920
		public boolean sameAs(Object object) {
992
			if (!valueLiterals.isEmpty()) {
921
			if (valueMap != null) {
993
				if (!valueLiterals.contains(object.toString()))
922
				Object bool = valueMap.get(object.toString());
994
					return false;
923
995
			}
924
				if (bool == Boolean.FALSE)
996
			if (!notValueLiterals.isEmpty()) {
925
					// one of the specified not-values
997
				if (notValueLiterals.contains(object.toString()))
998
					return false;
999
			}
1000
			if (!valueObjects.isEmpty()) {
1001
				if (!isObjectinList(object, valueObjects))
1002
					return false;
926
					return false;
1003
			}
927
				else if (valuesSpecified && bool == null)
1004
			if (!notValueObjects.isEmpty()) {
928
					// not one of the specified values
1005
				if (isObjectinList(object, notValueObjects))
1006
					return false;
929
					return false;
1007
			}
930
			}
931
932
			if (valueObjects != null && !isObjectInList(object, valueObjects))
933
				return false;
934
935
			if (notValueObjects != null && isObjectInList(object, notValueObjects))
936
				return false;
937
1008
			return true;
938
			return true;
1009
		}
939
		}
1010
		
940
1011
		/**
941
		/**
1012
		 * Answers whether or not an object in <code>list</code> is the
942
		 * Answers whether or not an object in <code>list</code> is the
1013
		 * {@link #sameAs(Object)}<code>object</code>.
943
		 * {@link #sameAs(Object)}<code>object</code>.
1014
		 * 
944
		 *
1015
		 * @param object
945
		 * @param object
1016
		 *            the object to find
946
		 *            the object to find
1017
		 * @param list
947
		 * @param list
Lines 1020-1031 Link Here
1020
		 *         the {@link #sameAs(Object)}<code>object</code>,
950
		 *         the {@link #sameAs(Object)}<code>object</code>,
1021
		 *         <code>false</code> otherwise.
951
		 *         <code>false</code> otherwise.
1022
		 */
952
		 */
1023
		private boolean isObjectinList(Object object, List list) {
953
		private static boolean isObjectInList(Object object, ObjectDescriptor[] list) {
1024
			Iterator i = list.iterator();
954
			for (int i = 0, n = list.length; i < n; ++i)
1025
			while (i.hasNext()) {
955
				if (list[i].sameAs(object))
1026
				if (((ObjectDescriptor)i.next()).sameAs(object))
1027
					return true;
956
					return true;
1028
			}
957
1029
			return false;
958
			return false;
1030
		}
959
		}
1031
	}
960
	}
Lines 1033-1049 Link Here
1033
	/**
962
	/**
1034
	 * Describes a method value using a method descriptor and a value descriptor.
963
	 * Describes a method value using a method descriptor and a value descriptor.
1035
	 */
964
	 */
1036
	private static class MethodValueEntry {
965
	private static final class MethodValueEntry {
1037
		
966
1038
		/**
967
		/**
1039
		 * The method descriptor.
968
		 * The method descriptor.
1040
		 */
969
		 */
1041
		public MethodDescriptor method;
970
		private final MethodDescriptor method;
1042
		
971
1043
		/**
972
		/**
1044
		 * The value descriptor.
973
		 * The value descriptor.
1045
		 */
974
		 */
1046
		public ValueDescriptor value;
975
		private final ValueDescriptor value;
1047
976
1048
		/**
977
		/**
1049
		 * Creates a new method value entry.
978
		 * Creates a new method value entry.
Lines 1055-1066 Link Here
1055
			this.method = method;
984
			this.method = method;
1056
			this.value = value;
985
			this.value = value;
1057
		}
986
		}
987
988
		public boolean matches(Object object) {
989
			Object result = method.invoke(object);
990
991
			if (result == null)
992
				result = NULL;
993
994
			return value.sameAs(result);
995
		}
1058
	}
996
	}
1059
997
1060
	/**
998
	/**
1061
	 * A helper method to return a list of objects whose ids are given in a
999
	 * A helper method to return a list of objects whose ids are given in a
1062
	 * comma-separated string and whose instances are given in an object map.
1000
	 * comma-separated string and whose instances are given in an object map.
1063
	 * 
1001
	 *
1064
	 * @param objectsIds
1002
	 * @param objectsIds
1065
	 *            A comma-separated object ids string
1003
	 *            A comma-separated object ids string
1066
	 * @param objectMap
1004
	 * @param objectMap
Lines 1076-1082 Link Here
1076
		StringTokenizer ids = new StringTokenizer(objectsIds.trim(), ","); //$NON-NLS-1$
1014
		StringTokenizer ids = new StringTokenizer(objectsIds.trim(), ","); //$NON-NLS-1$
1077
		if (!ids.hasMoreTokens())
1015
		if (!ids.hasMoreTokens())
1078
			return null;
1016
			return null;
1079
		
1017
1080
		List objectList = new ArrayList();
1018
		List objectList = new ArrayList();
1081
		while (ids.hasMoreTokens()) {
1019
		while (ids.hasMoreTokens()) {
1082
			String objectId = ids.nextToken().trim();
1020
			String objectId = ids.nextToken().trim();
Lines 1084-1090 Link Here
1084
			if (objectVal != null)
1022
			if (objectVal != null)
1085
				objectList.add(objectVal);
1023
				objectList.add(objectVal);
1086
			else {
1024
			else {
1087
				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$
1025
				IExtension extension = configElement.getDeclaringExtension();
1026
				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$
1088
			}
1027
			}
1089
		}
1028
		}
1090
		return objectList;
1029
		return objectList;
Lines 1093-1099 Link Here
1093
	/**
1032
	/**
1094
	 * Parses the comma-separated <code>s</code> string and returns a set of
1033
	 * Parses the comma-separated <code>s</code> string and returns a set of
1095
	 * the individual entries in the string.
1034
	 * the individual entries in the string.
1096
	 * 
1035
	 *
1097
	 * @param s
1036
	 * @param s
1098
	 *            A comma-separated string
1037
	 *            A comma-separated string
1099
	 * @return a set of the individual entries in the string.
1038
	 * @return a set of the individual entries in the string.
Lines 1109-1118 Link Here
1109
		return stringList.isEmpty() ? null : stringList;
1048
		return stringList.isEmpty() ? null : stringList;
1110
	}
1049
	}
1111
1050
1051
	/*private*/static Map getSubMap(Map map, Object key) {
1052
		Map subMap = (Map) map.get(key);
1053
1054
		if (subMap == null)
1055
			map.put(key, subMap = new HashMap());
1056
1057
		return subMap;
1058
	}
1059
1112
	/**
1060
	/**
1113
	 * Tests if an object matches at least one in the list of object descriptors
1061
	 * Tests if an object matches at least one in the list of object descriptors
1114
	 * passed.
1062
	 * passed.
1115
	 * 
1063
	 *
1116
	 * @param object
1064
	 * @param object
1117
	 *            the object for which to find a match
1065
	 *            the object for which to find a match
1118
	 * @param objects
1066
	 * @param objects
Lines 1133-1245 Link Here
1133
1081
1134
	/**
1082
	/**
1135
	 * A utility method to load a class using its name and a given class loader.
1083
	 * A utility method to load a class using its name and a given class loader.
1136
	 * 
1084
	 *
1137
	 * @param className
1085
	 * @param className
1138
	 *            The class name
1086
	 *            The class name
1139
	 * @param bundle
1087
	 * @param pluginId
1140
	 *            The class loader
1088
	 *            The symbolic name of the plugin which defines the class
1141
	 * @return The loaded class or <code>null</code> if could not be loaded
1089
	 * @return The loaded class or <code>null</code> if could not be loaded
1142
	 */
1090
	 */
1143
	 /*protected static Class loadClass(String className, Bundle bundle) {
1091
	protected static Class loadClass(String className, String pluginId) {
1144
		try {
1092
		Map pluginMap = getSubMap(loadedClassMap, pluginId);
1145
			return bundle.loadClass(className);
1093
		Object candidate = pluginMap.get(className);
1146
		} catch (ClassNotFoundException e) {
1094
		Class clazz;
1095
1096
		if (candidate instanceof WeakReference) {
1097
			clazz = (Class) ((WeakReference) candidate).get();
1098
1099
			if (clazz != null)
1100
				return clazz;
1101
		} else if (candidate != null) {
1102
			// we already tried to load this class and failed
1147
			return null;
1103
			return null;
1148
		}
1104
		}
1149
	}*/
1105
1150
	
1106
		Bundle bundle = getPluginBundle(pluginId);
1151
	/**
1107
1152
	 * A utility method to load a class using its name and a given class loader.
1108
		if (bundle != null) {
1153
	 * 
1109
			try {
1154
	 * @param className
1110
				clazz = bundle.loadClass(className);
1155
	 *            The class name
1111
1156
	 * @param bundle
1112
				pluginMap.put(className, new WeakReference(clazz));
1157
	 *            The class loader
1113
1158
	 * @return The loaded class or <code>null</code> if could not be loaded
1114
				return clazz;
1159
	 */
1115
1160
	protected static Class loadClass(String className, String pluginId) {
1116
			} catch (ClassNotFoundException e) {
1161
		StringBuffer keyStringBuf = new StringBuffer(className.length()
1117
				// fall through
1162
			+ pluginId.length() + 2); // 2 is for . and extra.
1163
		keyStringBuf.append(pluginId);
1164
		keyStringBuf.append('.');
1165
		keyStringBuf.append(className);
1166
		String keyString = keyStringBuf.toString();
1167
		WeakReference ref = (WeakReference) successLookupTable.get(keyString);
1168
		Class found = (ref != null) ? (Class) ref.get()
1169
			: null;
1170
		if (found == null) {
1171
			if (ref != null)
1172
				successLookupTable.remove(keyString);
1173
			if (!failureLookupTable.contains(keyString)) {
1174
				try {
1175
					Bundle bundle = getPluginBundle(pluginId);
1176
					if (bundle!=null){
1177
						found = bundle.loadClass(className);
1178
						successLookupTable.put(keyString, new WeakReference(found));
1179
					}else{
1180
						failureLookupTable.add(keyString);
1181
					}
1182
				} catch (ClassNotFoundException e) {
1183
					failureLookupTable.add(keyString);
1184
				}
1185
			}
1118
			}
1186
		}
1119
		}
1187
		return found;
1120
1121
		// record failure
1122
		pluginMap.put(className, Boolean.FALSE);
1123
1124
		return null;
1188
	}
1125
	}
1189
	
1126
1190
	
1191
	/**
1127
	/**
1192
	 * Given a bundle id, it checks if the bundle is found and activated. If it
1128
	 * Given a bundle id, it checks if the bundle is found and activated. If it
1193
	 * is, the method returns the bundle, otherwise it returns <code>null</code>.
1129
	 * is, the method returns the bundle, otherwise it returns <code>null</code>.
1194
	 * 
1130
	 *
1195
	 * @param pluginId
1131
	 * @param pluginId
1196
	 *            the bundle ID
1132
	 *            the bundle ID
1197
	 * @return the bundle, if found
1133
	 * @return the bundle, if found
1198
	 */
1134
	 */
1199
	protected static Bundle getPluginBundle(String pluginId) {
1135
	protected static Bundle getPluginBundle(String pluginId) {
1200
		Bundle bundle = Platform.getBundle(pluginId);
1136
		Bundle bundle = Platform.getBundle(pluginId);
1201
		if (null != bundle && bundle.getState() == org.osgi.framework.Bundle.ACTIVE)
1137
		if (null != bundle && bundle.getState() == Bundle.ACTIVE)
1202
			return bundle;
1138
			return bundle;
1203
		return null;
1139
		return null;
1204
	}
1140
	}
1205
1141
1206
	/**
1142
	/**
1207
	 * Tests if the given class is assignable to the given class name. Optimized
1143
	 * Iterate over the class and all its ancestors associating Boolean.TRUE
1208
	 * to look first in a cache of previously retrieved results.
1144
	 * with each class and interface name.
1209
	 * 
1145
	 *
1210
	 * @param clazz
1146
	 * @param clazz
1211
	 *            the class to be tested
1147
	 *            the class
1212
	 * @param className
1148
	 * @param assignable
1213
	 *            the class name to test against
1149
	 *            the map
1214
	 * @return <code>true</code> if the class is assignable to the class name,
1150
	 */
1215
	 *         <code>false</code> otherwise.
1151
	private static void gatherClassAndInterfaceNames(Class clazz, Map assignable) {
1216
	 */
1152
		for (; clazz != null; clazz = clazz.getSuperclass()) {
1217
	protected static boolean isAssignableTo(Class clazz, String className) {
1153
			assignable.put(clazz.getName(), Boolean.TRUE);
1218
		if (clazz == null)
1219
			return false;
1220
1154
1221
		if ( contains(isNotAssignableTable, clazz, className) ) {
1155
			Class[] interfaces = clazz.getInterfaces();
1222
			return false;
1223
		}
1224
		
1225
		if ( contains(isAssignableTable, clazz, className) ) {
1226
			return true;
1227
		}
1228
	
1229
		boolean result = isAssignableToNoCache(clazz,className);
1230
		
1231
		if (result) {
1232
			add(isAssignableTable, clazz, className);
1233
		} else {
1234
			add(isNotAssignableTable, clazz, className);
1235
		}
1236
1156
1237
		return result;
1157
			for (int i = interfaces.length; --i >= 0;)
1158
				gatherClassAndInterfaceNames(interfaces[i], assignable);
1159
		}
1238
	}
1160
	}
1239
1161
1240
	/**
1162
	/**
1241
	 * Tests if the given class is assignable to the given class name.
1163
	 * Tests if the given class is assignable to the given class name. Optimized
1242
	 * 
1164
	 * to look first in a cache of previously retrieved results.
1165
	 *
1243
	 * @param clazz
1166
	 * @param clazz
1244
	 *            the class to be tested
1167
	 *            the class to be tested
1245
	 * @param className
1168
	 * @param className
Lines 1247-1303 Link Here
1247
	 * @return <code>true</code> if the class is assignable to the class name,
1170
	 * @return <code>true</code> if the class is assignable to the class name,
1248
	 *         <code>false</code> otherwise.
1171
	 *         <code>false</code> otherwise.
1249
	 */
1172
	 */
1250
	private static boolean isAssignableToNoCache(Class clazz, String className) {
1173
	protected static boolean isAssignableTo(Class clazz, String className) {
1251
// mgoyal: This approach isn't safe to use as it can cause incorrect
1174
		if (clazz != null) {
1252
// plugin load. Documenting this approach for further analysis. Don't
1175
			Map assignable = (Map) assignableMap.get(clazz);
1253
// remove or uncomment this.
1176
1254
//		try {
1177
			if (assignable == null) {
1255
//			if(clazz.getName().equals(className))
1178
				assignable = new HashMap();
1256
//				return true;
1179
				gatherClassAndInterfaceNames(clazz, assignable);
1257
//			
1180
				assignableMap.put(clazz, assignable);
1258
//			ClassLoader clsLoader = clazz.getClassLoader();
1181
			}
1259
//			if(clsLoader != null) {
1182
1260
//				Class testCls = clsLoader.loadClass(className);
1183
			Object value = assignable.get(className);
1261
//				if(testCls != null && testCls.isAssignableFrom(clazz))
1184
1262
//					return true;
1185
			if (value != null)
1263
//			}
1186
				return ((Boolean) value).booleanValue();
1264
//			return false;
1187
1265
//		} catch (ClassNotFoundException e) {
1188
			assignable.put(className, Boolean.FALSE);
1266
//			return false;
1267
//		}
1268
//		
1269
		
1270
		// test the class itself
1271
		if (clazz.getName().equals(className))
1272
			return true;
1273
		
1274
		// test all the interfaces the class implements
1275
		Class[] interfaces = clazz.getInterfaces();
1276
		for (int i = 0; i < interfaces.length; i++) {
1277
			if (checkInterfaceHierarchy(interfaces[i], className))
1278
				return true;
1279
		}
1189
		}
1280
		
1190
1281
		// test superclass
1191
		return false;
1282
		return isAssignableTo(clazz.getSuperclass(), className);
1283
	}
1192
	}
1284
1193
1285
	/**
1194
	/**
1286
	 * A map of classes that have been successfully loaded, keyed on the class
1195
	 * A map of information about classes we found or failed to find. The first
1287
	 * name optionally prepended by the plugin ID, if specified.
1196
	 * level is keyed by plugin identifiers and yields a second-level map. The
1197
	 * second-level map is keyed by class names. A value at this level is either
1198
	 * a WeakReference to a class or Boolean.FALSE which signals that no
1199
	 * accessible class exists.
1288
	 */
1200
	 */
1289
	private static Map successLookupTable = new HashMap();
1201
	private static final Map loadedClassMap = new HashMap();
1290
	
1291
	/**
1292
	 * A map of classes that could not be loaded, keyed on the class name
1293
	 * optionally prepended by the plugin ID, if specified.
1294
	 */
1295
	private static Set failureLookupTable = new HashSet();
1296
1202
1297
	/**
1203
	/**
1298
	 * Gets an adapter for <code>object</code> to the class described by
1204
	 * Gets an adapter for <code>object</code> to the class described by
1299
	 * <code>className</code> qualified by the optional <code>pluginId</code>.
1205
	 * <code>className</code> qualified by the optional <code>pluginId</code>.
1300
	 * 
1206
	 *
1301
	 * @param object
1207
	 * @param object
1302
	 *            the object to be adapted
1208
	 *            the object to be adapted
1303
	 * @param className
1209
	 * @param className
Lines 1307-1522 Link Here
1307
	 * @return the adapted object, or <code>null</code> if it couldn't be found
1213
	 * @return the adapted object, or <code>null</code> if it couldn't be found
1308
	 */
1214
	 */
1309
	protected static Object getAdapter(Object object, String className, String pluginId) {
1215
	protected static Object getAdapter(Object object, String className, String pluginId) {
1310
		if (!(object instanceof IAdaptable))
1216
		if (pluginId != null && object instanceof IAdaptable) {
1311
			return null;
1217
			Class clazz = loadClass(className, pluginId);
1312
		if(pluginId != null) {
1218
			if (clazz != null)
1313
			Class theClass = loadClass(className,pluginId);
1219
				return ((IAdaptable) object).getAdapter(clazz);
1314
			return theClass != null ? ((IAdaptable) object).getAdapter(theClass) : null;
1315
		}
1220
		}
1316
		return null;
1221
		return null;
1317
	}
1222
	}
1318
1223
}
1319
	/**
1320
	 * A utility method to invoke a cascading list of methods.
1321
	 * 
1322
	 * @param methodDescriptor
1323
	 *            the first method descriptor
1324
	 * @param object
1325
	 *            The object to invoke the method on
1326
	 * @return the value of the invokation
1327
	 */
1328
	protected static Object invokeMethod(MethodDescriptor methodDescriptor, Object object) {
1329
		String methodSignature = null;
1330
		Class clazz =null;
1331
		try {
1332
			if (methodDescriptor == null || object == null)
1333
				return null;
1334
			methodSignature = methodDescriptor.getSignature();
1335
			clazz = object.getClass();
1336
			if (passiveClasses.contains(clazz,methodSignature))
1337
				return null;
1338
			Method method = classToMethodSignatureToMethodCach.
1339
				getMethod(clazz,methodSignature);
1340
			if(method==null){
1341
				method = clazz.getMethod(methodDescriptor.getName(),
1342
										 methodDescriptor.getParameterTypes());
1343
				classToMethodSignatureToMethodCach.addMethod(clazz,methodSignature,method);
1344
			}
1345
			Object valueObj = 
1346
				method.invoke(object, methodDescriptor.getParameters());
1347
			if (methodDescriptor.getNext() == null)
1348
				return valueObj == null ? NULL : valueObj;
1349
			return invokeMethod(methodDescriptor.getNext(), valueObj);
1350
		} catch (Exception e) {
1351
			passiveClasses.addMethod(clazz,methodSignature);
1352
			return null;
1353
		}
1354
	}
1355
	
1356
	/**
1357
	 * A utility method to invoke a cascading list of methods.
1358
	 * 
1359
	 * @param StaticMethodDescriptor
1360
	 *            the static method descriptor
1361
	 * @param object
1362
	 *            The context object to use (it could be null)
1363
	 * @return the value of the invokation
1364
	 */
1365
	protected static Object invokeStaticMethod(StaticMethodDescriptor methodDescriptor, Object object) {
1366
		try {
1367
			if (methodDescriptor == null)
1368
				return null;
1369
			
1370
			Object[] valuesCopy = (Object[])methodDescriptor.getParameters().clone();
1371
			for (int i = 0; i < valuesCopy.length; i++) {
1372
				if(valuesCopy[i].equals(contextParam)){
1373
					valuesCopy[i]=object;
1374
				}
1375
			}
1376
			
1377
			Method method = getStaticMethod(methodDescriptor);
1378
			Object valueObj = 
1379
				method.invoke(object, valuesCopy);
1380
1381
			if (methodDescriptor.getNext() == null)
1382
				return valueObj == null ? NULL : valueObj;
1383
			return invokeMethod(methodDescriptor.getNext(), valueObj);
1384
		} catch (Exception e) {
1385
			return null;
1386
		}
1387
	}
1388
1389
	/**
1390
	 * utility method used to get a static method object
1391
	 * @param pluginID			the plugin that owns the class
1392
	 * @param className			the class to use to call hte static method
1393
	 * @param methodName		the method to get
1394
	 * @param ParameterTypes	the parameter types 
1395
	 * @return					the  method object
1396
	 */
1397
	private static Method getStaticMethod(StaticMethodDescriptor staticMethodDescriptor) {
1398
		Class theClass = loadClass(staticMethodDescriptor.getClassName(),
1399
									staticMethodDescriptor.getPluginID());
1400
		if (theClass==null)
1401
			return null;
1402
		Method theMethod = null;
1403
		try {
1404
			String methodSignature = staticMethodDescriptor.getSignature(); 
1405
			theMethod = classToMethodSignatureToMethodCach.getMethod(theClass,methodSignature);
1406
			if(theMethod==null){
1407
			   theMethod = theClass.getMethod(staticMethodDescriptor.getName(),
1408
											staticMethodDescriptor.getParameterTypes());
1409
				classToMethodSignatureToMethodCach.addMethod(theClass,methodSignature,theMethod);
1410
			}
1411
		} catch (SecurityException e) {
1412
			// no special handling needed;
1413
		} catch (NoSuchMethodException e) {
1414
			// no special handling needed;
1415
		}
1416
		return theMethod;
1417
	}
1418
1419
	/**
1420
	 * Check the interfaces the whole way up. If one of them matches
1421
	 * <code>className</code> return <code>true</code>. Optimized to look
1422
	 * first in a cache of previously retrieved results.
1423
	 * 
1424
	 * @param interfaceToCheck
1425
	 *            The interface whose name we are testing.
1426
	 * @param className
1427
	 *            the name of the interface to we are trying to match
1428
	 * @return <code>true</code> if one of the interfaces in the hierarchy
1429
	 *         matches <code>className</code>,<code>false</code>
1430
	 *         otherwise.
1431
	 */
1432
	private static boolean checkInterfaceHierarchy(Class interfaceToCheck, String className) {
1433
		
1434
		if ( contains(isNotAssignableTable, interfaceToCheck, className) ) {
1435
			return false;
1436
		}
1437
		
1438
		if ( contains(isAssignableTable, interfaceToCheck, className) ) {
1439
			return true;
1440
		}
1441
		
1442
		boolean result = checkInterfaceHierarchyNoCache(interfaceToCheck,className);
1443
		
1444
		if (result) {
1445
			add(isAssignableTable, interfaceToCheck, className);
1446
		} else {
1447
			add(isNotAssignableTable, interfaceToCheck, className);
1448
		}
1449
		
1450
		return result;
1451
	}
1452
1453
	/**
1454
	 * Check the interfaces the whole way up. If one of them matches
1455
	 * <code>className</code> return <code>true</code>.
1456
	 * 
1457
	 * @param interfaceToCheck
1458
	 *            The interface whose name we are testing.
1459
	 * @param className
1460
	 *            the name of the interface to we are trying to match
1461
	 * @return <code>true</code> if one of the interfaces in the hierarchy
1462
	 *         matches <code>className</code>,<code>false</code>
1463
	 *         otherwise.
1464
	 */
1465
	private static boolean checkInterfaceHierarchyNoCache(Class interfaceToCheck, String className) {
1466
		if(interfaceToCheck.getName().equals(className))
1467
			return true;
1468
		Class[] superInterfaces = interfaceToCheck.getInterfaces();
1469
		for (int i = 0; i < superInterfaces.length; i++) {
1470
			if(checkInterfaceHierarchy(superInterfaces[i], className))
1471
				return true;
1472
		}
1473
		return false;
1474
	}
1475
	
1476
	/**
1477
	 * Determines whether the <code>map</code> contains an entry for the
1478
	 * <key,value>pair.
1479
	 * 
1480
	 * @param map
1481
	 *            the map in which to find the key and value
1482
	 * @param key
1483
	 *            the key
1484
	 * @param value
1485
	 *            the value
1486
	 * @return <code>true</code> if the map contains the key/value pair,
1487
	 *         <code>false</code> otherwise
1488
	 */
1489
	private static boolean contains(Map map, Object key, String value) {
1490
		
1491
		boolean result = false;
1492
		
1493
		Object val = map.get(key);
1494
		if (val != null) {
1495
			Set values = (Set)val;
1496
			result = values.contains(value);
1497
		}
1498
		
1499
		return result;
1500
	}
1501
	
1502
	/**
1503
	 * Adds the <key,value>pair to the <code>map</code>.
1504
	 * 
1505
	 * @param map
1506
	 *            the map in which to add the value
1507
	 * @param key
1508
	 *            the key
1509
	 * @param value
1510
	 *            the value
1511
	 */
1512
	private static void add(Map map, Object key, String value) {
1513
		
1514
		Set values = (Set)map.get(key);
1515
		if (values == null) {
1516
			values = new HashSet();
1517
			map.put(key, values);
1518
		}
1519
		
1520
		values.add(value);
1521
	}
1522
}

Return to bug 120106