View Javadoc

1   /*
2    * Copyright 2002-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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 155436 2005-02-26 13:17:48Z dirkv $
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 //    public abstract List getProperties(Predicate predicate);
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 //    public abstract List getOperations(Predicate predicate);
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 //    public abstract List getInstanceFactories(Predicate predicate);
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             // @todo: define exception
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             // @todo: define exception
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     //--- Notification mechanism - verbose, but what are you gonna do?
333     
334     public void addClazzChangeListener(ClazzChangeListener listener) {
335         if (listeners == null) {
336             listeners = new ArrayList();
337         }
338         
339         // We don't want to prevent the subclasses from being garbage
340         // collection just because they are listening to their superclasses. 
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     //--- End notification mechanism
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          * This is basically a remake of java.lang.reflect.Field.getTypeName
499          * (Class), which cannot be used directly as it is declared with the
500          * default scope.
501          */
502         if (javaClass.isArray()) {
503             return getCanonicalClassName(javaClass.getComponentType()) + "[]";
504         }
505         return javaClass.getName();
506     }
507 }