1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.clazz;
17
18 import java.lang.ref.WeakReference;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.clazz.common.ClazzElementSupport;
26 import org.apache.commons.clazz.common.ExtendedClazzLoaderFactory;
27 import org.apache.commons.clazz.common.StandardClazzLoaderFactory;
28
29 /***
30 *
31 *
32 * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a>
33 * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
34 * @version $Id: Clazz.java,v 1.11 2004/02/19 23:58:37 scolebourne Exp $
35 */
36 public abstract class Clazz extends ClazzElementSupport
37 implements ClazzElement
38 {
39 private ClazzLoader loader;
40 private String name;
41 private List listeners;
42
43 /***
44 * The name of the standard clazz model. The value of the constant is
45 * "Standard".
46 */
47 public static final String STANDARD_CLAZZ_MODEL =
48 StandardClazzLoaderFactory.MODEL;
49
50 /***
51 * The name of the extended clazz model. The value of the constant is
52 * "Extended".
53 */
54 public static final String EXTENDED_CLAZZ_MODEL =
55 ExtendedClazzLoaderFactory.MODEL;
56
57 private static String defaultClazzModel = EXTENDED_CLAZZ_MODEL;
58 private static ClazzLoader defaultClazzLoader;
59 private static Map clazzLoaderFactories = new HashMap();
60
61 static {
62 addClazzLoaderFactory(StandardClazzLoaderFactory.FACTORY);
63 addClazzLoaderFactory(ExtendedClazzLoaderFactory.FACTORY);
64 }
65
66 /***
67 * Register a clazz loader factory, which manages ClazzLoaders, which manage
68 * Clazzes.
69 *
70 * @param clazzLoaderFactory
71 */
72 public static void addClazzLoaderFactory(ClazzLoaderFactory factory) {
73 clazzLoaderFactories.put(factory.getClazzModel(), factory);
74 }
75
76 /***
77 * Returns a ClazzLoaderFactory registered for the supplied model. We can
78 * have multiple clazz loader factories implementing different models (e.g.
79 * Standard JavaBeans, Extended JavaBeans etc).
80 *
81 * @param model is the type of the model we need
82 * @return ClazzLoaderFactory
83 */
84 public static ClazzLoaderFactory getClazzLoaderFactory(String model) {
85 return (ClazzLoaderFactory) clazzLoaderFactories.get(model);
86 }
87
88 /***
89 * Select the default clazz model.
90 *
91 * @param defaultClazzModel
92 */
93 public static void setDefaultClazzModel(String defaultClazzModel) {
94 Clazz.defaultClazzModel = defaultClazzModel;
95 }
96
97 /***
98 * Returns the name of the default clazz model.
99 */
100 public static String getDefaultClazzModel() {
101 return defaultClazzModel;
102 }
103
104 /***
105 * Returns a clazz loader for the specified model. The provided ClassLoader
106 * can be used by the ClazzLoader for reflection.
107 *
108 * @param model
109 * @param classLoader
110 * @return ClazzLoader
111 */
112 public static ClazzLoader getClazzLoader(
113 String model,
114 ClassLoader classLoader)
115 {
116 if (classLoader == null) {
117 classLoader = Clazz.class.getClassLoader();
118 }
119 return getClazzLoaderFactory(model).getClazzLoader(classLoader);
120 }
121
122 /***
123 * Returns the ClazzLoader for the default clazz model.
124 */
125 public static ClazzLoader getDefaultClazzLoader(ClassLoader classLoader) {
126 return getClazzLoader(getDefaultClazzModel(), classLoader);
127 }
128
129 /***
130 * Uses the clazz loader for the specified model to obtain the Clazz for the
131 * supplied object.
132 */
133 public static Clazz getClazz(Object instance, String model) {
134 if (instance == null) {
135 throw new NullPointerException();
136 }
137
138 ClassLoader classLoader = instance.getClass().getClassLoader();
139 Clazz clazz = getClazzLoader(model, classLoader).getClazz(instance);
140 if (clazz == null) {
141 throw new ClazzNotFoundException(instance.getClass().getName());
142 }
143 return clazz;
144 }
145
146 /***
147 * Uses the default clazz loader to obtain the Clazz for the supplied
148 * object.
149 */
150 public static Clazz getClazz(Object instance) {
151 return getClazz(instance, getDefaultClazzModel());
152 }
153
154 /***
155 * Keep the constructor protected or private, we want Clazzes to
156 * be created by the ClazzLoaders only.
157 *
158 * @param loader is the owning model clazz loader
159 * @param name must be unique within the model
160 */
161 protected Clazz(ClazzLoader modelClazzLoader, String name) {
162 this.loader = modelClazzLoader;
163 this.name = name;
164 }
165
166 /***
167 * Every Clazz belongs to one and only one ClazzLoader. Its name is unique
168 * within that loader.
169 */
170 public ClazzLoader getClazzLoader() {
171 return loader;
172 }
173
174 /***
175 * Returns the name of the Clazz. It is ok for the same name to be present
176 * in different ClazzLoaders, except CachingClazzLoaders.
177 */
178 public String getName() {
179 return name;
180 }
181
182 /***
183 * Gets the package name.
184 *
185 * @return the package name
186 */
187 public String getPackageName() {
188 if (name == null) {
189 return null;
190 }
191 int index = name.lastIndexOf('.');
192 if (index == -1) {
193 return "";
194 }
195
196 return name.substring(0, index);
197 }
198
199 /***
200 * Gets the class name (without the package).
201 *
202 * @return the class name (without the package)
203 */
204 public String getShortClassName() {
205 if (name == null) {
206 return null;
207 }
208 int index = name.lastIndexOf('.');
209 if (index == -1) {
210 return name;
211 }
212
213 return name.substring(index + 1);
214 }
215
216 /***
217 * Returns the class of instances created by the <code>newInstance()</code>
218 * method.
219 */
220 public abstract Class getInstanceClass();
221
222 /***
223 * Returns the superclazz for this Clazz, or null if there is none.
224 */
225 public abstract Clazz getSuperclazz();
226
227 /***
228 * Returns true if the supplied clazz is either the same or a subclazz of
229 * this clazz.
230 */
231 public boolean isAssignableFrom(Clazz clazz) {
232 if (clazz == this) {
233 return true;
234 }
235 Clazz superclazz = clazz.getSuperclazz();
236 if (superclazz != null) {
237 return isAssignableFrom(superclazz);
238 }
239 return false;
240 }
241
242 /***
243 * Returns properties declared by this Clazz, not its superclazzes
244 */
245 public abstract List getDeclaredProperties();
246
247 /***
248 * Returns all properties for this clazz, including those declared
249 * by superclazzes.
250 */
251 public abstract List getProperties();
252
253 /***
254 * Returns the properties that match the Predicate.
255 */
256
257
258 /***
259 * Returns a ClazzProperty for the given name
260 */
261 public abstract ClazzProperty getProperty(String name);
262
263 /***
264 * Returns all Operations for this clazz.
265 */
266 public abstract List getOperations();
267
268 /***
269 * Returns all Operations declared by this clazz, not its superclazzes.
270 */
271 public abstract List getDeclaredOperations();
272
273 /***
274 * Returns the Operations that match the Predicate.
275 */
276
277
278 /***
279 * Returns the Operation for the given signature. The signature should be
280 * formatted as follows: <code>"name(paramClazzName1,...)"</code>
281 */
282 public abstract ClazzOperation getOperation(String signature);
283
284 /***
285 * Returns all InstanceFactories for this clazz.
286 */
287 public abstract List getInstanceFactories();
288
289 /***
290 * Returns the InstanceFactories that match the Predicate.
291 */
292
293
294 /***
295 * Returns ClazzInstanceFactory for the given signature. The signature
296 * should be formatted as follows: <code>"(paramClazzName1,...)"</code>. You
297 * can pass <code>null</code> in place of <code>"()"</code>.
298 */
299 public abstract ClazzInstanceFactory getInstanceFactory(String signature);
300
301 /***
302 * Creates a new instance of this Clazz using the InstanceFactory that takes
303 * no parameters.
304 */
305 public Object newInstance() {
306 ClazzInstanceFactory factory = getInstanceFactory("()");
307 if (factory == null) {
308
309 throw new RuntimeException(
310 "No such instance factory " + getName() + " ()");
311 }
312 return factory.newInstance(null);
313 }
314
315 /***
316 * Creates a new instance of this Clazz using the InstanceFactory
317 * with the specified signature.
318 */
319 public Object newInstance(String signature, Object[] parameters) {
320 ClazzInstanceFactory factory = getInstanceFactory(signature);
321 if (factory == null) {
322
323 throw new RuntimeException(
324 "No such instance factory "
325 + getName()
326 + (signature == null ? "" : " " + signature));
327 }
328 return factory.newInstance(parameters);
329 }
330
331
332
333
334 public void addClazzChangeListener(ClazzChangeListener listener) {
335 if (listeners == null) {
336 listeners = new ArrayList();
337 }
338
339
340
341 WeakReference reference = new WeakReference(listener);
342 listeners.add(reference);
343 }
344
345 public void removeClazzChangeListener(ClazzChangeListener listener) {
346 if (listeners == null) {
347 return;
348 }
349 for (Iterator iter = listeners.iterator(); iter.hasNext();) {
350 WeakReference reference = (WeakReference) iter.next();
351 if (reference.get() == listener) {
352 iter.remove();
353 break;
354 }
355 }
356 }
357
358 private static final WeakReference[] CLAZZ_CHANGE_LISTENER_ARRAY =
359 new WeakReference[0];
360
361 private abstract static class Notifier {
362 void fire(Clazz clazz, Object parameter) {
363 if (clazz.listeners != null && clazz.listeners.size() != 0) {
364 WeakReference listenerArray[] =
365 (WeakReference[]) clazz.listeners.toArray(
366 CLAZZ_CHANGE_LISTENER_ARRAY);
367 for (int i = 0; i < listenerArray.length; i++) {
368 fire(
369 clazz,
370 (ClazzChangeListener) listenerArray[i].get(),
371 parameter);
372 }
373 }
374 }
375
376 abstract void fire(
377 Clazz clazz, ClazzChangeListener listener, Object parameter);
378 }
379
380 private static final Notifier PROPERTY_ADDED_NOTIFIER = new Notifier() {
381 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
382 listener.propertyAdded(clazz, (ClazzProperty) parameter);
383 }
384 };
385
386 protected void firePropertyAdded(ClazzProperty property) {
387 PROPERTY_ADDED_NOTIFIER.fire(this, property);
388 }
389
390 private static final Notifier PROPERTY_REMOVED_NOTIFIER = new Notifier() {
391 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
392 listener.propertyRemoved(clazz, (ClazzProperty) parameter);
393 }
394 };
395
396 protected void firePropertyRemoved(ClazzProperty property) {
397 PROPERTY_REMOVED_NOTIFIER.fire(this, property);
398 }
399
400 private static final Notifier OPERATION_ADDED_NOTIFIER = new Notifier() {
401 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
402 listener.operationAdded(clazz, (ClazzOperation) parameter);
403 }
404 };
405
406 protected void fireOperationAdded(ClazzOperation operation) {
407 OPERATION_ADDED_NOTIFIER.fire(this, operation);
408 }
409
410 private static final Notifier OPERATION_REMOVED_NOTIFIER = new Notifier() {
411 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
412 listener.operationRemoved(clazz, (ClazzOperation) parameter);
413 }
414 };
415
416 protected void fireOperationRemoved(ClazzOperation operation) {
417 OPERATION_REMOVED_NOTIFIER.fire(this, operation);
418 }
419
420
421 private static final Notifier FACTORY_ADDED_NOTIFIER = new Notifier() {
422 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
423 listener.instanceFactoryAdded(
424 clazz,
425 (ClazzInstanceFactory) parameter);
426 }
427 };
428
429 protected void fireInstanceFactoryAdded(ClazzInstanceFactory factory) {
430 FACTORY_ADDED_NOTIFIER.fire(this, factory);
431 }
432
433 private static final Notifier FACTORY_REMOVED_NOTIFIER = new Notifier() {
434 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) {
435 listener.instanceFactoryRemoved(
436 clazz,
437 (ClazzInstanceFactory) parameter);
438 }
439 };
440
441 protected void fireInstanceFactoryRemoved(ClazzInstanceFactory factory) {
442 FACTORY_REMOVED_NOTIFIER.fire(this, factory);
443 }
444
445
446
447
448 /***
449 * Creates a signature string out of an operation or instance factory name
450 * and parameter types.
451 */
452 public static String constructSignature(String name, Class[] arguments) {
453 StringBuffer buffer = new StringBuffer();
454 if (name != null) {
455 buffer.append(name);
456 }
457 buffer.append('(');
458 if (arguments != null) {
459 for (int i = 0; i < arguments.length; i++) {
460 if (i != 0) {
461 buffer.append(',');
462 }
463 buffer.append(getCanonicalClassName(arguments[i]));
464 }
465 }
466 buffer.append(')');
467 return buffer.toString();
468 }
469
470 /***
471 * Creates a signature string out of an operation or instance factory name
472 * and parameter types.
473 */
474 public static String constructSignature(String name, Clazz[] arguments) {
475 StringBuffer buffer = new StringBuffer();
476 if (name != null) {
477 buffer.append(name);
478 }
479 buffer.append('(');
480 if (arguments != null) {
481 for (int i = 0; i < arguments.length; i++) {
482 if (i != 0) {
483 buffer.append(',');
484 }
485 buffer.append(arguments[i].getName());
486 }
487 }
488 buffer.append(')');
489 return buffer.toString();
490 }
491
492 /***
493 * Produces a nice type name for a classes representing an array, e.g. "[I"
494 * is shown as "int[]". For non-array types returns the regular type name.
495 */
496 public static String getCanonicalClassName(Class javaClass) {
497
498
499
500
501
502 if (javaClass.isArray()) {
503 return getCanonicalClassName(javaClass.getComponentType()) + "[]";
504 }
505 return javaClass.getName();
506 }
507 }