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 |
} |