KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jfun > yan > util > ClassDescriptor


1 package jfun.yan.util;
2
3 import java.lang.reflect.Constructor JavaDoc;
4 import java.lang.reflect.Field JavaDoc;
5 import java.lang.reflect.Member JavaDoc;
6 import java.lang.reflect.Method JavaDoc;
7 import java.lang.reflect.Modifier JavaDoc;
8 import java.util.ArrayList JavaDoc;
9 import java.util.Arrays JavaDoc;
10 import java.util.HashMap JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.Map JavaDoc;
14
15 import jfun.util.Misc;
16
17 /**
18  * A descriptor class for all methods/fields/contructors of a class.
19  * Hash table is pre-built to speed up query.
20  * <p>
21  * throughout this class, public members are defined as members
22  * defined with "public" keyword and declared in a public type.
23  * public members declared by a non-public class is considered non-public
24  * because access to it from outside is prohibited by the java access control
25  * anyway.
26  * </p>
27  * <p>
28  * public members defined in public classes are always prefered even
29  * when we allow private/protected members and types to be visible.
30  * So if a non-public subtype and a public super type both have a field
31  * with the same name, the field in the public super type is always used.
32  * </p>
33  * <p>
34  * @author Ben Yu
35  * Nov 14, 2005 9:10:41 PM
36  */

37 public class ClassDescriptor<T> {
38   
39   private static final class Sig{
40     private final String JavaDoc name;
41     private final Params params;
42     
43     public String JavaDoc getName() {
44       return name;
45     }
46     public Params getParams() {
47       return params;
48     }
49     Sig(String JavaDoc name, Class JavaDoc[] ptypes){
50       this(name, new Params(ptypes));
51     }
52     Sig(String JavaDoc name, Params params) {
53       this.name = name;
54       this.params = params;
55     }
56     public boolean equals(Object JavaDoc obj) {
57       if(obj instanceof Sig){
58         final Sig other = (Sig)obj;
59         return name.equals(other.name)&&params.equals(other.params);
60       }
61       return false;
62     }
63     public int hashCode() {
64       return name.hashCode()*31+params.hashCode();
65     }
66     public String JavaDoc toString() {
67       return name+params;
68     }
69   }
70   private static final Class JavaDoc[] noparams = new Class JavaDoc[0];
71   private static final class Params{
72     private final Class JavaDoc[] ptypes;
73     Params(Class JavaDoc[] ptypes) {
74       this.ptypes = ptypes==null?noparams:ptypes;
75     }
76     public Class JavaDoc[] getParameterTypes() {
77       return ptypes;
78     }
79     public boolean equals(Object JavaDoc obj) {
80       if(obj instanceof Params){
81         final Params other = (Params)obj;
82         return Arrays.equals(ptypes, other.ptypes);
83       }
84       else return false;
85     }
86     public int hashCode() {
87       return Misc.getArrayHashcode(ptypes);
88     }
89     public String JavaDoc toString() {
90       return Utils.toString(ptypes);
91     }
92   }
93   private static boolean isSub(Method JavaDoc a, Method JavaDoc b){
94     return
95       a.getDeclaringClass().isAssignableFrom(b.getDeclaringClass())
96       && Arrays.equals(a.getParameterTypes(), b.getParameterTypes());
97   }
98   private static Object JavaDoc combine(Method JavaDoc a, Method JavaDoc b){
99     if(isSub(a,b)){
100       return b;
101     }
102     else if(isSub(b,a)){
103       return a;
104     }
105     else{
106       final ArrayList JavaDoc list = new ArrayList JavaDoc();
107       list.add(a);
108       list.add(b);
109       return list;
110     }
111   }
112   private static ArrayList JavaDoc merge(ArrayList JavaDoc mbrs, Method JavaDoc b){
113     final int sz = mbrs.size();
114     final ArrayList JavaDoc result = new ArrayList JavaDoc(sz+1);
115     for(int i=0; i<sz; i++){
116       final Method JavaDoc mbr = (Method JavaDoc)mbrs.get(i);
117       if(isSub(b, mbr)){
118         //the new one is overriden by an existing one.
119
return mbrs;
120       }
121       else if(!isSub(mbr, b)){
122         //new one and the existing one can co-exist.
123
result.add(mbr);
124       }
125     }
126     result.add(b);
127     return result;
128   }
129   private final class Methods {
130     private final HashMap JavaDoc mtds = new HashMap JavaDoc();
131     private final HashMap JavaDoc mtd_names = new HashMap JavaDoc();
132     boolean isEmpty(){
133       return mtds.isEmpty();
134     }
135     void addAll(Methods other){
136       for(Iterator JavaDoc it = other.mtds.keySet().iterator(); it.hasNext();){
137         final Sig sig = (Sig)it.next();
138         final Method JavaDoc mtd = other.getMethod(sig);
139         addMethod(sig, mtd);
140       }
141     }
142     void addMethod(Sig sig, Method JavaDoc mtd){
143       final String JavaDoc name = sig.getName();
144       final Method JavaDoc old = (Method JavaDoc)mtds.get(sig);
145       if(old!=null &&
146           !old.getDeclaringClass().isAssignableFrom(mtd.getDeclaringClass())){
147         //only subtype can override super type.
148
return;
149       }
150       final Object JavaDoc dup = mtd_names.get(name);
151       if(dup==null){
152         mtd_names.put(name, mtd);
153       }
154       else{
155         if(dup instanceof Method JavaDoc){
156           mtd_names.put(name, combine((Method JavaDoc)dup, mtd));
157         }
158         else{
159           final ArrayList JavaDoc dups = (ArrayList JavaDoc)dup;
160           //dups.add(mtd);
161
mtd_names.put(name, merge(dups, mtd));
162         }
163       }
164       mtds.put(sig, mtd);
165     }
166
167     Method JavaDoc getMethod(Sig sig){
168       return (Method JavaDoc)mtds.get(sig);
169     }
170     Method JavaDoc filterMethod(String JavaDoc name, MethodPredicate pred)
171     throws AmbiguityException{
172       final Object JavaDoc r = mtd_names.get(name);
173       if(r==null) return null;
174       if(r instanceof Method JavaDoc){
175         final Method JavaDoc ret = (Method JavaDoc)r;
176         if(pred.isMethod(ret)){
177           return ret;
178         }
179         else return null;
180       }
181       else{
182         final List JavaDoc list = (List JavaDoc)r;
183         Method JavaDoc ret = null;
184         final int sz = list.size();
185         for(int i=0;i<sz;i++){
186           final Method JavaDoc m = (Method JavaDoc)list.get(i);
187           if(pred.isMethod(m)){
188             if(ret!=null){
189               throw new AmbiguityException("type "
190                   +Misc.getTypeName(type) +
191                   " has more than one " + pred + ".");
192             }
193             ret = m;
194           }
195         }
196         return ret;
197       }
198     }
199     Method JavaDoc findMethod(String JavaDoc name)
200     throws AmbiguityException{
201       final Object JavaDoc r = mtd_names.get(name);
202       if(r==null) return null;
203       if(r instanceof Method JavaDoc){
204         return (Method JavaDoc)r;
205       }
206       else{
207         final List JavaDoc list = (List JavaDoc)r;
208         if(list.size()>1){
209           throw new AmbiguityException("type "
210             +Misc.getTypeName(type) +
211             " has more than one qualified methods named "+name);
212         }
213         return (Method JavaDoc)list.get(0);
214       }
215     }
216     /*
217     List getMethods(String name){
218       final Object dup = mtd_names.get(name);
219       if(dup==null){
220         return Collections.EMPTY_LIST;
221       }
222       else if(dup instanceof Method){
223         final Object[] result = new Object[]{dup};
224         return Arrays.asList(result);
225       }
226       else{
227         return Collections.unmodifiableList((List)dup);
228       }
229     }*/

230   }
231   private static final class Fields{
232     private final HashMap JavaDoc flds = new HashMap JavaDoc();
233     boolean isEmpty(){
234       return flds.isEmpty();
235     }
236     void addField(String JavaDoc name, Field JavaDoc fld){
237       final Field JavaDoc old = (Field JavaDoc)flds.get(name);
238       if(old!=null &&
239           !old.getDeclaringClass().isAssignableFrom(fld.getDeclaringClass())){
240         return;
241       }
242       flds.put(name, fld);
243     }
244     Field JavaDoc getField(String JavaDoc name){
245       return (Field JavaDoc)flds.get(name);
246     }
247     void addAll(Fields other){
248       for(Iterator JavaDoc it = other.flds.keySet().iterator(); it.hasNext();){
249         final String JavaDoc name = (String JavaDoc)it.next();
250         final Field JavaDoc fld = other.getField(name);
251         addField(name, fld);
252       }
253     }
254   }
255   //all public ones declared by a public type.
256
private final Class JavaDoc<T> type;
257   
258   private final Methods mtds = new Methods();
259   
260   //private final Methods pub_mtds = new Methods();
261
//Sig -> Method
262
private final Methods priv_mtds = new Methods();
263   
264   
265   //String -> Field
266
private final Fields flds = new Fields();
267   //String -> Field
268
//private final Fields pub_flds = new Fields();
269
//String -> Field
270
private final Fields priv_flds = new Fields();
271   
272   
273   //Params -> Constructor
274
private final HashMap JavaDoc ctors = new HashMap JavaDoc();
275   //Params -> Constructor
276
private final HashMap JavaDoc priv_ctors = new HashMap JavaDoc();
277   //private ones or public ones but declared by private type.
278
//public member declared in private type is preferred over private members.
279

280
281
282
283
284   void addAll(ClassDescriptor<T> other){
285     //to add members of super type to this one.
286
final Class JavaDoc supertype = other.getType();
287     if(!supertype.isAssignableFrom(type)){
288       throw new IllegalArgumentException JavaDoc(
289           "only parent type can be added to this descriptor.");
290     }
291     flds.addAll(other.flds);
292     //pub_flds.addAll(other.pub_flds);
293
priv_flds.addAll(other.priv_flds);
294     
295     mtds.addAll(other.mtds);
296     //pub_mtds.addAll(other.pub_mtds);
297
priv_mtds.addAll(other.priv_mtds);
298   }
299   private static boolean isPublic(Member JavaDoc mbr){
300     return Modifier.isPublic(mbr.getModifiers());
301   }
302   private static boolean isPublic(Class JavaDoc c){
303     return Modifier.isPublic(c.getModifiers());
304   }
305   void addConstructor(Constructor JavaDoc<T> ctor){
306     final Params params = new Params(ctor.getParameterTypes());
307     if(isPublic(ctor)){
308       if(!isPublic(type)){
309         Utils.forceAccess(ctor);
310       }
311       ctors.put(params, ctor);
312     }
313     else{
314       Utils.forceAccess(ctor);
315       priv_ctors.put(params, ctor);
316     }
317   }
318
319   void addMethod(Method JavaDoc mtd){
320     final Sig sig = new Sig(mtd.getName(), mtd.getParameterTypes());
321     if(isPublic(mtd)){
322       if(isPublic(mtd.getDeclaringClass())){
323         mtds.addMethod(sig, mtd);
324       }
325     }
326     Utils.forceAccess(mtd);
327     priv_mtds.addMethod(sig, mtd);
328   }
329   void addField(Field JavaDoc fld){
330     final String JavaDoc name = fld.getName();
331     if(isPublic(fld)){
332       if(isPublic(fld.getDeclaringClass())){
333         flds.addField(name, fld);
334       }
335     }
336     Utils.forceAccess(fld);
337     priv_flds.addField(name, fld);
338   }
339   private static <T> Constructor JavaDoc<T> findConstructor(Class JavaDoc<T> type, Map JavaDoc from){
340     if(from.isEmpty())
341       return null;
342     if(from.size()>1){
343       throw new AmbiguityException("type "
344           +Misc.getTypeName(type) +
345           " has more than one qualified constructors");
346     }
347     return (Constructor JavaDoc<T>)from.values().iterator().next();
348   }
349   private static <T> Constructor JavaDoc<T> findConstructor(Class JavaDoc<T> type, Map JavaDoc from,
350       int param_count){
351     Constructor JavaDoc result = null;
352     for(Iterator JavaDoc it=from.keySet().iterator();it.hasNext();){
353       final Params params = (Params)it.next();
354       if(params.ptypes.length==param_count){
355         if(result!=null)
356           throw new AmbiguityException("type "
357               +Misc.getTypeName(type) +
358               " has more than one qualified constructors with "
359               + param_count+" formal parameters.");
360         result = (Constructor JavaDoc)from.get(params);
361       }
362     }
363     return result;
364   }
365   /**
366    * Get the Constructor object identified by an array of parameter types.
367    * @param param_types the parameter types.
368    * @param suppress_security whether access to non-public constructor is allowed.
369    * @return the Constructor. or null if not found.
370    */

371   public Constructor JavaDoc<T> getConstructor(Class JavaDoc[] param_types,
372       boolean suppress_security){
373     if(suppress_security){
374       return getAnyConstructor(param_types);
375     }
376     else return getConstructor(param_types);
377   }
378   /**
379    * Get the public Constructor object identified by an array of parameter types.
380    * @param param_types the parameter types.
381    * @return the Constructor. or null if not found.
382    */

383   public Constructor JavaDoc<T> getConstructor(Class JavaDoc[] param_types){
384     final Params params = new Params(param_types);
385     return (Constructor JavaDoc)ctors.get(params);
386   }
387   /**
388    * Get the only one constructor declared in the type.
389    * @param suppress_security whether access to non-public constructor is allowed.
390    * @return the Constructor object. or null if not found.
391    * @throws AmbiguityException if more than one constructor is found.
392    */

393   public Constructor JavaDoc<T> getConstructor(boolean suppress_security)
394   throws AmbiguityException{
395     if(suppress_security)
396       return getAnyConstructor();
397     else
398       return getConstructor();
399   }
400   /**
401    * Get the only one public constructor declared in the type.
402    * @return the Constructor object. or null if not found.
403    * @throws AmbiguityException if more than one constructor is found.
404    */

405   public Constructor JavaDoc<T> getConstructor()
406   throws AmbiguityException{
407     return findConstructor(type, ctors);
408   }
409   
410   /**
411    * Get the only one constructor declared in the type.
412    * @param param_count the number of formal parameters.
413    * @param suppress_security whether access to non-public constructor is allowed.
414    * @return the Constructor object. or null if not found.
415    * @throws AmbiguityException if more than one constructor is found.
416    */

417   public Constructor JavaDoc<T> getConstructor(int param_count, boolean suppress_security)
418   throws AmbiguityException{
419     if(suppress_security)
420       return getAnyConstructor(param_count);
421     else
422       return getConstructor(param_count);
423   }
424   /**
425    * Get the only one public constructor declared in the type.
426    * @param param_count the number of formal parameters.
427    * @return the Constructor object. or null if not found.
428    * @throws AmbiguityException if more than one constructor is found.
429    */

430   public Constructor JavaDoc<T> getConstructor(int param_count)
431   throws AmbiguityException{
432     return findConstructor(type, ctors, param_count);
433   }
434   private Constructor JavaDoc<T> getAnyConstructor(Class JavaDoc[] param_types){
435     final Params params = new Params(param_types);
436     final Constructor JavaDoc ret = (Constructor JavaDoc)ctors.get(params);
437     if(ret!=null) return ret;
438     return (Constructor JavaDoc)priv_ctors.get(params);
439   }
440   private Constructor JavaDoc<T> getAnyConstructor()
441   throws AmbiguityException{
442     final Constructor JavaDoc ret = getConstructor();
443     if(ret==null){
444       return findConstructor(type, priv_ctors);
445     }
446     else return ret;
447   }
448   private Constructor JavaDoc<T> getAnyConstructor(int param_count)
449   throws AmbiguityException{
450     final Constructor JavaDoc ret = getConstructor(param_count);
451     if(ret==null){
452       return findConstructor(type, priv_ctors, param_count);
453     }
454     else return ret;
455   }
456   /**
457    * Get the method with a certain name.
458    * @param name the method name.
459    * @param suppress_security whether to look at non-public ones.
460    * @return the Method object. or null if not found.
461    * @throws AmbiguityException when more than one are found.
462    */

463   public Method JavaDoc getMethod(String JavaDoc name, boolean suppress_security)
464   throws AmbiguityException{
465     if(suppress_security)
466       return getAnyMethod(name);
467     else
468       return getMethod(name);
469   }
470   /**
471    * Get the public method with a certain name.
472    * @param name the method name.
473    * @return the Method object. or null if not found.
474    * @throws AmbiguityException when more than one are found.
475    */

476   public Method JavaDoc getMethod(String JavaDoc name)
477   throws AmbiguityException{
478     return mtds.findMethod(name);
479   }
480   /**
481    * Get a public Method with a certain name and satisfies a certain predicate.
482    * @param name the method name. the method name.
483    * @param pred the predicate.
484    * <code>pred.toString()</code> is used in the ambiguity exception error message.
485    * @return the Method object, or null if not found.
486    * @throws AmbiguityException when more than one qualified methods
487    * are found.
488    */

489   public Method JavaDoc filterMethod(String JavaDoc name, MethodPredicate pred)
490   throws AmbiguityException{
491     return mtds.filterMethod(name, pred);
492   }
493   /**
494    * Get a Method with a certain name and satisfies a certain predicate.
495    * Because public methods are prefered, private methods
496    * are searched only when no qualified public method is found.
497    * @param name the method name. the method name.
498    * @param pred the predicate.
499    * <code>pred.toString()</code> is used in the ambiguity exception error message.
500    * @param suppress_security whether to look at non-public ones.
501    * @return the Method object, or null if not found.
502    * @throws AmbiguityException when more than one qualified methods
503    * are found.
504    */

505   public Method JavaDoc filterMethod(String JavaDoc name, MethodPredicate pred,
506       boolean suppress_security)
507   throws AmbiguityException{
508     if(suppress_security)
509       return filterAnyMethod(name, pred);
510     else return filterMethod(name, pred);
511   }
512   private Method JavaDoc filterAnyMethod(String JavaDoc name, MethodPredicate pred){
513     final Method JavaDoc ret = mtds.filterMethod(name, pred);
514     if(ret!=null) return ret;
515     else return priv_mtds.filterMethod(name, pred);
516   }
517   /**
518    * Get a read-only list of the methods with a certain name.
519    * Because public methods are prefered, so private member
520    * are searched only when no public method is found.
521    * @param name the method name.
522    * @param suppress_security whether to look at non-public ones.
523    * @return the list of Method objects.
524    */

525   /*
526   public List getMethods(String name, boolean suppress_security){
527     if(suppress_security)
528       return getAnyMethods(name);
529     else
530       return getMethods(name);
531   }*/

532   /**
533    * Get a read-only list of the public methods with a certain name.
534    * @param name the method name.
535    * @return the list of Method objects.
536    */

537   /*
538   public List getMethods(String name){
539     return mtds.getMethods(name);
540   }*/

541   /**
542    * Get the method identified by a signature.
543    * @param name the method name.
544    * @param param_types the formal parameter types.
545    * @param suppress_security whether to look at non-public ones.
546    * @return the Method object. or null if not found.
547    */

548   public Method JavaDoc getMethod(String JavaDoc name, Class JavaDoc[] param_types, boolean suppress_security){
549     if(suppress_security)
550       return getAnyMethod(name, param_types);
551     else
552       return getMethod(name, param_types);
553   }
554   /**
555    * Get the public method identified by a signature.
556    * @param name the method name.
557    * @param param_types the formal parameter types.
558    * @return the Method object. or null if not found.
559    */

560   public Method JavaDoc getMethod(String JavaDoc name, Class JavaDoc[] param_types){
561     return mtds.getMethod(new Sig(name, param_types));
562   }
563   private Method JavaDoc getAnyMethod(String JavaDoc name, Class JavaDoc[] param_types){
564     final Sig sig = new Sig(name, param_types);
565     Method JavaDoc ret = mtds.getMethod(sig);
566     if(ret != null) return ret;
567     //ret = pub_mtds.getMethod(sig);
568
//if(ret != null) return ret;
569
return priv_mtds.getMethod(sig);
570   }
571   private Method JavaDoc getAnyMethod(String JavaDoc name)
572   throws AmbiguityException{
573     Method JavaDoc ret = mtds.findMethod(name);
574     if(ret != null) return ret;
575     //ret = pub_mtds.findMethod(name);
576
//if(ret != null) return ret;
577
return priv_mtds.findMethod(name);
578   }
579   /*
580   private List getAnyMethods(String name){
581     final List pubs = mtds.getMethods(name);
582     if(!pubs.isEmpty()) return pubs;
583     return priv_mtds.getMethods(name);
584   }*/

585   /**
586    * Get the Field object identified by name.
587    * @param name the field name.
588    * @param suppress_security whether to look at non-public ones.
589    * @return the Field object. or null if not found.
590    */

591   public Field JavaDoc getField(String JavaDoc name, boolean suppress_security){
592     if(suppress_security)
593       return getAnyField(name);
594     else
595       return getField(name);
596   }
597   /**
598    * Get the public Field object identified by name.
599    * @param name the field name.
600    * @return the Field object. or null if not found.
601    */

602   public Field JavaDoc getField(String JavaDoc name){
603     return flds.getField(name);
604   }
605   private Field JavaDoc getAnyField(String JavaDoc name){
606     Field JavaDoc ret = flds.getField(name);
607     if(ret!=null) return ret;
608     //ret = pub_flds.getField(name);
609
//if(ret!=null) return ret;
610
return priv_flds.getField(name);
611   }
612   /**
613    * Get the class object that this descriptor describes.
614    */

615   public Class JavaDoc<T> getType(){
616     return type;
617   }
618   ClassDescriptor(Class JavaDoc<T> type) {
619     this.type = type;
620   }
621   
622 }
623
Popular Tags