KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > attributes > Attributes


1 /*
2  * Copyright 2003-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.attributes;
17
18 import java.lang.reflect.Field JavaDoc;
19 import java.lang.reflect.Constructor JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.WeakHashMap JavaDoc;
30
31 /**
32  * API for accessing attributes.
33  *
34  * <h3>General Notes on Errors</h3>
35  *
36  * All Methods in this class may throw <code>RepositoryError</code> or subclasses thereof.
37  * This Error is thrown if an attribute repository can not be loaded for some Exceptional
38  * reason.
39  *
40  * <h4>Rationale for Errors instead of Exceptions</h4>
41  *
42  * The methods in this class throws <code>Error</code>s instead of <code>Exception</code>s.
43  * This rationale behind this is that:
44  *
45  * <ul>
46  * <li>The programmer should not have to wrap all accesses to
47  * the Attributes API in a try/catch clause.
48  * <li>An Exception being thrown here is caused by missing classes
49  * or other "Error-like" conditions.
50  * </ul>
51  *
52  * <h3>Performance Notes</h3>
53  * The process of loading attributes for a class is a
54  * (relatively) time-consuming process, as it involves some dynamic linking
55  * in the form of inheritable attributes, a lot of reflection and so on. However,
56  * once loaded the attributes are cached, so repeated access to them are fast.
57  * But despite this the process of finding one attribute of a given type
58  * or such operations does involve some iteration of HashSets that <b>runs in linear
59  * time in the number of attributes associated with the program element</b>, and you should
60  * avoid accessing attributes in your innermost loops if you can avoid it. For
61  * example, instead of:
62  *
63  * <pre><code>
64  * Class myClass = ...;
65  * for (int i = 0; i < 10000; i++) {
66  * if (Attributes.hasAttributeType (myClass, MyAttribute.class)) {
67  * doThis(myClass);
68  * } else {
69  * doThat(myClass);
70  * }
71  * }
72  * </code></pre>
73  *
74  * do:
75  *
76  * <pre><code>
77  * Class myClass = ...;
78  * boolean shouldDoThis = Attributes.hasAttributeType (myClass, MyAttribute.class);
79  * for (int i = 0; i < 10000; i++) {
80  * if (shouldDoThis) {
81  * doThis(myClass);
82  * } else {
83  * doThat(myClass);
84  * }
85  * }
86  * </code></pre>
87  *
88  * if the loop should run at maximum speed.
89  */

90 public class Attributes {
91     
92     /**
93      * A cache of attribute repositories. The map used is a WeakHashMap keyed on the
94      * Class owning the attribute repository. This works because Class objects use
95      * the identity function to compare for equality - i.e. if the two classes
96      * have the same name, and are loaded from the same two ClassLoaders, then
97      * <code>class1 == class2</code>. Also, <code>(class1.equals(class2)) == (class1 ==
98      * class2)</code>. This means that a once a Class reference has been garbage-collected,
99      * it can't be re-created. Thus we can treat the cache map as a normal map - the only
100      * entries that will ever disappear are those we can't look up anyway because we
101      * can't ever create the key for them!
102      *
103      * <p>Also, this will keep the cache from growing too big in environments where
104      * classes are loaded and unloaded all the time (i.e. application servers).
105      */

106     private final static Map JavaDoc classRepositories = new WeakHashMap JavaDoc ();
107     
108     /**
109      * List used to keep track of the initialization list in getCachedRepository.
110      * Since the method is synchronized static, we only need one list.
111      */

112     private static List JavaDoc initList = new ArrayList JavaDoc ();
113     
114     private synchronized static CachedRepository getCachedRepository (Class JavaDoc clazz) throws RepositoryError, CircularDependencyError {
115         if (classRepositories.containsKey (clazz)) {
116             CachedRepository cr = (CachedRepository) classRepositories.get (clazz);
117             if (cr == null) {
118                 // Circular references.
119
List JavaDoc dependencyList = new ArrayList JavaDoc ();
120                 dependencyList.addAll (initList);
121                 throw new CircularDependencyError (clazz.getName (), dependencyList);
122             } else {
123                 return cr;
124             }
125         } else {
126             // Indicate that we're loading it.
127
CachedRepository cached = null;
128             
129             initList.add (clazz.getName ());
130             try {
131                 classRepositories.put (clazz, null);
132                 
133                 Class JavaDoc attributeRepo = null;
134                 AttributeRepositoryClass repo = EmptyAttributeRepositoryClass.INSTANCE;
135                 try {
136                     attributeRepo = Class.forName (clazz.getName () + "$__attributeRepository", true, clazz.getClassLoader ());
137                     repo = (AttributeRepositoryClass) attributeRepo.newInstance ();
138                 } catch (ClassNotFoundException JavaDoc cnfe) {
139                     // OK, just means no repo available, so default to empty one.
140
repo = EmptyAttributeRepositoryClass.INSTANCE;
141                 } catch (InstantiationException JavaDoc ie) {
142                     throw new RepositoryError (ie);
143                 } catch (IllegalAccessException JavaDoc iae) {
144                     throw new RepositoryError (iae);
145                 }
146                 cached = new DefaultCachedRepository (clazz, repo);
147                 
148                 classRepositories.put (clazz, cached);
149                 
150                 if (repo != null) {
151                     Util.validateRepository (clazz, repo);
152                 }
153             } finally {
154                 initList.remove (initList.size () - 1);
155             }
156             
157             return cached;
158         }
159     }
160     
161     /**
162      * Selects from a collection of attributes one attribute with a given class.
163      *
164      * @return the attribute instance, or null of none could be found.
165      * @throws MultipleAttributesError if the collection contains more than one
166      * instance of the specified class.
167      */

168     private static Object JavaDoc getAttribute (Collection JavaDoc attrs, Class JavaDoc attributeClass) throws MultipleAttributesError {
169         Object JavaDoc candidate = null;
170         Iterator JavaDoc iter = attrs.iterator ();
171         while (iter.hasNext ()) {
172             Object JavaDoc attr = iter.next ();
173             if (attr.getClass () == attributeClass) {
174                 if (candidate == null) {
175                     candidate = attr;
176                 } else {
177                     throw new MultipleAttributesError (attributeClass.getName ());
178                 }
179             }
180         }
181         
182         return candidate;
183     }
184     
185     /**
186      * Get one attributes of a given type from a class.
187      *
188      * @return the attribute instance, or null of none could be found.
189      * @throws MultipleAttributesError if the collection contains more than one
190      * instance of the specified class.
191      */

192     public static Object JavaDoc getAttribute (Class JavaDoc clazz, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
193         return getAttribute (getAttributes (clazz), attributeClass);
194     }
195     
196     /**
197      * Get one attributes of a given type from a field.
198      *
199      * @return the attribute instance, or null of none could be found.
200      * @throws MultipleAttributesError if the collection contains more than one
201      * instance of the specified class.
202      */

203     public static Object JavaDoc getAttribute (Field JavaDoc field, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
204         return getAttribute (getAttributes (field), attributeClass);
205     }
206     
207     /**
208      * Get one attributes of a given type from a constructor.
209      *
210      * @return the attribute instance, or null of none could be found.
211      * @throws MultipleAttributesError if the collection contains more than one
212      * instance of the specified class.
213      */

214     public static Object JavaDoc getAttribute (Constructor JavaDoc ctor, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
215         return getAttribute (getAttributes (ctor), attributeClass);
216     }
217     
218     /**
219      * Get one attributes of a given type from a method.
220      *
221      * @return the attribute instance, or null of none could be found.
222      * @throws MultipleAttributesError if the collection contains more than one
223      * instance of the specified class.
224      */

225     public static Object JavaDoc getAttribute (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
226         return getAttribute (getAttributes (method), attributeClass);
227     }
228     
229     /**
230      * Get one attributes of a given type from a parameter.
231      *
232      * @return the attribute instance, or null of none could be found.
233      * @throws MultipleAttributesError if the collection contains more than one
234      * instance of the specified class.
235      */

236     public static Object JavaDoc getParameterAttribute (Method JavaDoc method, int parameter, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
237         return getAttribute (getParameterAttributes (method, parameter), attributeClass);
238     }
239     
240     /**
241      * Get one attributes of a given type from a constructor's parameter.
242      *
243      * @return the attribute instance, or null of none could be found.
244      * @throws MultipleAttributesError if the collection contains more than one
245      * instance of the specified class.
246      */

247     public static Object JavaDoc getParameterAttribute (Constructor JavaDoc constructor, int parameter, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
248         return getAttribute (getParameterAttributes (constructor, parameter), attributeClass);
249     }
250     
251     /**
252      * Get one attributes of a given type from a method's return value.
253      *
254      * @return the attribute instance, or null of none could be found.
255      * @throws MultipleAttributesError if the collection contains more than one
256      * instance of the specified class.
257      */

258     public static Object JavaDoc getReturnAttribute (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError, MultipleAttributesError {
259         return getAttribute (getReturnAttributes (method), attributeClass);
260     }
261     
262     /**
263      * Gets all attributes for a class.
264      */

265     public static Collection JavaDoc getAttributes (Class JavaDoc clazz) throws RepositoryError {
266         if (clazz == null) {
267             throw new NullPointerException JavaDoc ("clazz");
268         }
269         
270         return getCachedRepository (clazz).getAttributes ();
271     }
272     
273     /**
274      * Gets all attributes for a method.
275      */

276     public static Collection JavaDoc getAttributes (Method JavaDoc method) throws RepositoryError {
277         if (method == null) {
278             throw new NullPointerException JavaDoc ("method");
279         }
280         
281         return getCachedRepository (method.getDeclaringClass()).getAttributes (method);
282     }
283     
284     /**
285      * Gets all attributes for a parameter of a method.
286      */

287     public static Collection JavaDoc getParameterAttributes (Method JavaDoc method, int parameter) throws RepositoryError {
288         return getCachedRepository (method.getDeclaringClass()).getParameterAttributes (method, parameter);
289     }
290     
291     /**
292      * Gets all attributes for a parameter of a constructor.
293      */

294     public static Collection JavaDoc getParameterAttributes (Constructor JavaDoc constructor, int parameter) throws RepositoryError {
295         if (constructor == null) {
296             throw new NullPointerException JavaDoc ("constructor");
297         }
298         return getCachedRepository (constructor.getDeclaringClass()).getParameterAttributes (constructor, parameter);
299     }
300     
301     /**
302      * Gets all attributes for the return value of a method.
303      */

304     public static Collection JavaDoc getReturnAttributes (Method JavaDoc method) throws RepositoryError {
305         if (method == null) {
306             throw new NullPointerException JavaDoc ("method");
307         }
308         return getCachedRepository (method.getDeclaringClass()).getReturnAttributes (method);
309     }
310     
311     /**
312      * Gets all attributes for a field.
313      */

314     public static Collection JavaDoc getAttributes (Field JavaDoc field) throws RepositoryError {
315         if (field == null) {
316             throw new NullPointerException JavaDoc ("field");
317         }
318         return getCachedRepository (field.getDeclaringClass()).getAttributes (field);
319     }
320     
321     /**
322      * Gets all attributes for a constructor.
323      */

324     public static Collection JavaDoc getAttributes (Constructor JavaDoc cons) throws RepositoryError {
325         if (cons == null) {
326             throw new NullPointerException JavaDoc ("cons");
327         }
328         return getCachedRepository (cons.getDeclaringClass()).getAttributes (cons);
329     }
330     
331     /**
332      * Selects from a collection of attributes only those with a given class.
333      */

334     private static Collection JavaDoc getAttributes (Collection JavaDoc attrs, Class JavaDoc attributeClass) {
335         HashSet JavaDoc result = new HashSet JavaDoc ();
336         Iterator JavaDoc iter = attrs.iterator ();
337         while (iter.hasNext ()) {
338             Object JavaDoc attr = iter.next ();
339             if (attr.getClass () == attributeClass) {
340                 result.add (attr);
341             }
342         }
343         
344         return Collections.unmodifiableCollection (result);
345     }
346     
347     /**
348      * Get all attributes of a given type from a class. For all objects o in the returned
349      * collection, <code>o.getClass() == attributeClass</code>.
350      */

351     public static Collection JavaDoc getAttributes (Class JavaDoc clazz, Class JavaDoc attributeClass) throws RepositoryError {
352         return getAttributes (getAttributes (clazz), attributeClass);
353     }
354     
355     /**
356      * Get all attributes of a given type from a field. For all objects o in the returned
357      * collection, <code>o.getClass() == attributeClass</code>.
358      */

359     public static Collection JavaDoc getAttributes (Field JavaDoc field, Class JavaDoc attributeClass) throws RepositoryError {
360         return getAttributes (getAttributes (field), attributeClass);
361     }
362     
363     /**
364      * Get all attributes of a given type from a constructor. For all objects o in the returned
365      * collection, <code>o.getClass() == attributeClass</code>.
366      */

367     public static Collection JavaDoc getAttributes (Constructor JavaDoc ctor, Class JavaDoc attributeClass) throws RepositoryError {
368         return getAttributes (getAttributes (ctor), attributeClass);
369     }
370     
371     /**
372      * Get all attributes of a given type from a method. For all objects o in the returned
373      * collection, <code>o.getClass() == attributeClass</code>.
374      */

375     public static Collection JavaDoc getAttributes (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError {
376         return getAttributes (getAttributes (method), attributeClass);
377     }
378     
379     /**
380      * Get all attributes of a given type from a method's parameter. For all objects o in the returned
381      * collection, <code>o.getClass() == attributeClass</code>.
382      */

383     public static Collection JavaDoc getParameterAttributes (Method JavaDoc method, int parameter, Class JavaDoc attributeClass) throws RepositoryError {
384         return getAttributes (getParameterAttributes (method, parameter), attributeClass);
385     }
386     
387     /**
388      * Get all attributes of a given type from a method's parameter. For all objects o in the returned
389      * collection, <code>o.getClass() == attributeClass</code>.
390      */

391     public static Collection JavaDoc getParameterAttributes (Constructor JavaDoc constructor, int parameter, Class JavaDoc attributeClass) throws RepositoryError {
392         return getAttributes (getParameterAttributes (constructor, parameter), attributeClass);
393     }
394     
395     /**
396      * Get all attributes of a given type from a method's return value. For all objects o in the returned
397      * collection, <code>o.getClass() == attributeClass</code>.
398      */

399     public static Collection JavaDoc getReturnAttributes (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError {
400         return getAttributes (getReturnAttributes (method), attributeClass);
401     }
402     
403     /**
404      * Convenience function to test whether a collection of attributes contain
405      * an attribute of a given class.
406      */

407     private static boolean hasAttributeType (Collection JavaDoc attrs, Class JavaDoc attributeClass) {
408         Iterator JavaDoc iter = attrs.iterator ();
409         while (iter.hasNext ()) {
410             Object JavaDoc attr = iter.next ();
411             if (attr.getClass () == attributeClass) {
412                 return true;
413             }
414         }
415         
416         return false;
417     }
418     
419     /**
420      * Tests if a class has an attribute of a given type. That is, is there any attribute
421      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
422      */

423     public static boolean hasAttributeType (Class JavaDoc clazz, Class JavaDoc attributeClass) throws RepositoryError {
424         return hasAttributeType (getAttributes (clazz), attributeClass);
425     }
426     
427     /**
428      * Tests if a field has an attribute of a given type. That is, is there any attribute
429      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
430      */

431     public static boolean hasAttributeType (Field JavaDoc field, Class JavaDoc attributeClass) throws RepositoryError {
432         return hasAttributeType (getAttributes (field), attributeClass);
433     }
434     
435     /**
436      * Tests if a constructor has an attribute of a given type. That is, is there any attribute
437      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
438      */

439     public static boolean hasAttributeType (Constructor JavaDoc ctor, Class JavaDoc attributeClass) throws RepositoryError {
440         return hasAttributeType (getAttributes (ctor), attributeClass);
441     }
442     
443     /**
444      * Tests if a method has an attribute of a given type. That is, is there any attribute
445      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
446      */

447     public static boolean hasAttributeType (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError {
448         return hasAttributeType (getAttributes (method), attributeClass);
449     }
450     
451     /**
452      * Tests if a method's parameter has an attribute of a given type. That is, is there any attribute
453      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
454      */

455     public static boolean hasParameterAttributeType (Method JavaDoc method, int parameter, Class JavaDoc attributeClass) throws RepositoryError {
456         return hasAttributeType (getParameterAttributes (method, parameter), attributeClass);
457     }
458     
459     /**
460      * Tests if a constructor's parameter has an attribute of a given type. That is, is there any attribute
461      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
462      */

463     public static boolean hasParameterAttributeType (Constructor JavaDoc constructor, int parameter, Class JavaDoc attributeClass) throws RepositoryError {
464         return hasAttributeType (getParameterAttributes (constructor, parameter), attributeClass);
465     }
466     
467     /**
468      * Tests if a method's return value has an attribute of a given type. That is, is there any attribute
469      * <code>attr</code> such that <code>attr.getClass() == attributeClass</code>?
470      */

471     public static boolean hasReturnAttributeType (Method JavaDoc method, Class JavaDoc attributeClass) throws RepositoryError {
472         return hasAttributeType (getReturnAttributes (method), attributeClass);
473     }
474     
475     /**
476      * Convenience function to test whether a collection of attributes contain
477      * an attribute.
478      */

479     private static boolean hasAttribute (Collection JavaDoc attrs, Object JavaDoc attribute) throws RepositoryError {
480         return attrs.contains (attribute);
481     }
482     
483     /**
484      * Tests if a class has an attribute. That is, is there any attribute
485      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
486      */

487     public static boolean hasAttribute (Class JavaDoc clazz, Object JavaDoc attribute) throws RepositoryError {
488         return hasAttribute (getAttributes (clazz), attribute);
489     }
490     
491     /**
492      * Tests if a field has an attribute. That is, is there any attribute
493      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
494      */

495     public static boolean hasAttribute (Field JavaDoc field, Object JavaDoc attribute) throws RepositoryError {
496         return hasAttribute (getAttributes (field), attribute);
497     }
498     
499     /**
500      * Tests if a constructor has an attribute. That is, is there any attribute
501      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
502      */

503     public static boolean hasAttribute (Constructor JavaDoc ctor, Object JavaDoc attribute) throws RepositoryError {
504         return hasAttribute (getAttributes (ctor), attribute);
505     }
506     
507     /**
508      * Tests if a method has an attribute. That is, is there any attribute
509      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
510      */

511     public static boolean hasAttribute (Method JavaDoc method, Object JavaDoc attribute) throws RepositoryError {
512         return hasAttribute (getAttributes (method), attribute);
513     }
514     
515     /**
516      * Tests if a method's parameter has an attribute. That is, is there any attribute
517      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
518      */

519     public static boolean hasParameterAttribute (Method JavaDoc method, int parameter, Object JavaDoc attribute) throws RepositoryError {
520         return hasAttribute (getParameterAttributes (method, parameter), attribute);
521     }
522     
523     /**
524      * Tests if a constructor's parameter has an attribute. That is, is there any attribute
525      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
526      */

527     public static boolean hasParameterAttribute (Constructor JavaDoc constructor, int parameter, Object JavaDoc attribute) throws RepositoryError {
528         return hasAttribute (getParameterAttributes (constructor, parameter), attribute);
529     }
530     
531     /**
532      * Tests if a method's return value has an attribute. That is, is there any attribute
533      * <code>attr</code> such that <code>attr.equals(attribute)</code>?
534      */

535     public static boolean hasReturnAttribute (Method JavaDoc method, Object JavaDoc attribute) throws RepositoryError {
536         return hasAttribute (getReturnAttributes (method), attribute);
537     }
538     
539     /**
540      * Set attributes for a given class. The class must not have attributes set for it already
541      * (i.e. you can't redefine the attributes of a class at runtime). This because it
542      * really messes up the concept of inheritable attributes, and because the attribute set
543      * associated with a class is immutable, like the set of methods and fields.
544      *
545      * @param repo The repository. The repository will be sealed before any attributes are
546      * set. This to guarantee that the repository remains constant
547      * during setting.
548      * @throws IllegalStateException if the class whose attributes are defined already have
549      * attributes defined for it (even if it has no attributes).
550      */

551     public static synchronized void setAttributes (RuntimeAttributeRepository repo) throws IllegalStateException JavaDoc {
552         repo.seal ();
553         
554         Class JavaDoc clazz = repo.getDefinedClass ();
555         if (classRepositories.get (clazz) != null) {
556             throw new IllegalStateException JavaDoc (clazz.getName ());
557         }
558         Util.validateRepository (clazz, repo);
559         
560         DefaultCachedRepository cached = new DefaultCachedRepository (clazz, repo);
561         classRepositories.put (clazz, cached);
562     }
563 }
Popular Tags