KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > core > rtti > ClassItem


1 /*
2   Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>,
3                           Laurent Martelli <laurent@aopsys.com>
4   
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU Lesser General Public License as
7   published by the Free Software Foundation; either version 2 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   GNU Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */

18
19 package org.objectweb.jac.core.rtti;
20
21 import gnu.regexp.RE;
22 import java.lang.reflect.InvocationTargetException JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Hashtable JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Vector JavaDoc;
29 import org.apache.log4j.Logger;
30 import java.lang.reflect.Constructor JavaDoc;
31
32 /**
33  * This class defines a meta item that corresponds to the
34  * <code>java.lang.reflect.Class</code> meta element.<p>
35  *
36  * @author Renaud Pawlak
37  * @author Laurent Martelli
38  */

39
40 public class ClassItem extends MetaItemDelegate {
41
42     static Logger logger = Logger.getLogger("rtti.class");
43
44     static Class JavaDoc wrappeeClass = ClassRepository.wrappeeClass;
45
46     /**
47      * Default contructor to create a new class item object.<p>
48      *
49      * @param delegate the <code>java.lang.reflect.Class</code> actual
50      * meta item
51      */

52     public ClassItem(Class JavaDoc delegate) throws InvalidDelegateException {
53         super(delegate);
54         Class JavaDoc superClass = delegate.getSuperclass();
55         if (superClass!=Object JavaDoc.class && superClass!=null) {
56             try {
57                 setSuperClass(ClassRepository.get().getClass(superClass));
58             } catch(Exception JavaDoc e) {
59                 logger.error("ClassItem("+delegate.getName()+"): "+e);
60             }
61         }
62     }
63
64     private int collsCount = 0;
65     private int refsCount = 0;
66     private int primsCount = 0;
67     private int methodsCount = 0;
68     private int constructorsCount = 0;
69
70     /**
71      * Stores the fields of the class.<p> */

72
73     protected Hashtable JavaDoc fields = new Hashtable JavaDoc();
74
75     /**
76      * Stores the methods of the class. */

77
78     protected Hashtable JavaDoc methods = new Hashtable JavaDoc();
79
80     boolean rttiBuilt = false;
81
82     protected void buildFieldInfo() {
83         if (!rttiBuilt) {
84             rttiBuilt = true;
85             ClassRepository.get().buildDefaultFieldRTTI(this);
86         }
87     }
88
89     /**
90      * Gets a field from its name. Throw an exception if the field does
91      * not exist.<p>
92      *
93      * @param name the field name. If name contains a dot, it is
94      * considered an expression field and is automatically created if
95      * it does not exist yet.
96      * @return a <code>FieldItem</code> instance
97      * @see #getFieldNoError(String)
98      */

99     public FieldItem getField(String JavaDoc name) {
100         buildFieldInfo();
101         FieldItem ret = getFieldNoError(name);
102         if (ret==null) {
103             throw new NoSuchFieldException JavaDoc(this,name);
104         }
105         return ret;
106     }
107
108     /**
109      * Gets a field from its name. Don't throw an exception if the
110      * field does not exist.<p>
111      *
112      * @param name the field name
113      * @return a <code>FieldItem</code> instance, or null if no such
114      * field exists in the class
115      * @see #getField(String)
116      */

117     public FieldItem getFieldNoError(String JavaDoc name) {
118         buildFieldInfo();
119         FieldItem field = (FieldItem)fields.get(name);
120         if (field==null) {
121             if (name.indexOf('.')!=-1) {
122                 try {
123                     List JavaDoc path = parseExpression(name);
124                     if (path.get(path.size()-1) instanceof CollectionItem) {
125                         field = new CollectionItem(name,path,this);
126                     } else {
127                         field = new FieldItem(name,path,this);
128                     }
129                     addField(field);
130                 } catch (Exception JavaDoc e) {
131                     field = null;
132                     logger.error("Expression field "+this.getName()+"."+name+
133                                  " instanciation failed: "+e);
134                     e.printStackTrace();
135                 }
136             }
137         }
138         if (field==null && superClass!=null) {
139             field = superClass.getFieldNoError(name);
140             if (field!=null) {
141                 field = field.clone(this);
142                 addField(field);
143             }
144         }
145         return field;
146     }
147
148     /**
149      * Parse the expression and computes its type.
150      * @param expression the expression (<field>.<field>.<field>...)
151      */

152     public List JavaDoc parseExpression(String JavaDoc expression) {
153         Vector JavaDoc path = new Vector JavaDoc();
154         String JavaDoc current = expression;
155         ClassItem curClass = this;
156         int dot = current.indexOf('.');
157         FieldItem field = null;
158         while (dot!=-1) {
159             String JavaDoc fieldName = current.substring(0,dot);
160             field = curClass.getField(fieldName);
161             path.add(field);
162             if (field instanceof CollectionItem)
163                 curClass = ((CollectionItem)field).getComponentType();
164             else
165                 curClass = field.getTypeItem();
166             current = current.substring(dot+1);
167             dot = current.indexOf('.');
168         }
169
170         field = curClass.getField(current);
171         path.add(field);
172       
173         /*
174           if (field!=null) {
175           type = field.getType();
176           setter = field.getSetter();
177           }
178         */

179
180         return path;
181     }
182
183     /**
184      * Gets a collection from its name.<p>
185      *
186      * @param name the collection name
187      * @return a <code>CollectionItem</code> instance
188      */

189
190     public CollectionItem getCollection(String JavaDoc name) {
191         buildFieldInfo();
192         return (CollectionItem) fields.get(name);
193     }
194
195     /**
196      * Gets all the fields for this class item.<p>
197      *
198      * @return an array of field items
199      */

200
201     public FieldItem[] getFields() {
202         buildFieldInfo();
203         Collection JavaDoc c = fields.values();
204         FieldItem[] res = new FieldItem[c.size()];
205         Iterator JavaDoc it = c.iterator();
206         int i = 0;
207         while (it.hasNext()) {
208             res[i] = (FieldItem) it.next();
209             i++;
210         }
211         return res;
212     }
213
214     /**
215      * Gets all the fields for this class item
216      *
217      * @return the fields as a collection
218      */

219
220     public Collection JavaDoc getAllFields() {
221         buildFieldInfo();
222         return fields.values();
223     }
224
225     /**
226      * Gets all the current class item's fields that are tagged or not
227      * tagged with the given attribute.<p>
228      *
229      * @param attribute the attribute
230      * @param not if true, returns fields not tagged with attribute
231      *
232      * @return a collection of field items
233      */

234     public Collection JavaDoc getTaggedFields(String JavaDoc attribute, boolean not) {
235         buildFieldInfo();
236         Collection JavaDoc c = fields.values();
237         Iterator JavaDoc it = c.iterator();
238         Vector JavaDoc result = new Vector JavaDoc();
239         int i = 0;
240         while (it.hasNext()) {
241             FieldItem field = (FieldItem) it.next();
242             if (field.getAttribute(attribute)!=null ^ not) {
243                 result.add(field);
244             }
245         }
246         return result;
247     }
248
249     /**
250      * @param expression something like [!](static|transient|private|public|protected)
251      */

252     public Collection JavaDoc filterFields(String JavaDoc expression) {
253         logger.debug(this+".filterFields "+expression);
254         String JavaDoc keyword;
255         boolean not = false;
256         if (expression.charAt(0)=='!') {
257             not = true;
258             expression = expression.substring(1,expression.length());
259         }
260         int filter = 0;
261         if (expression.equals("static"))
262             filter = Modifier.STATIC;
263         else if (expression.equals("public"))
264             filter = Modifier.PUBLIC;
265         else if (expression.equals("transient"))
266             filter = Modifier.TRANSIENT;
267         else if (expression.equals("private"))
268             filter = Modifier.PRIVATE;
269         else if (expression.equals("protected"))
270             filter = Modifier.PROTECTED;
271         Vector JavaDoc result = new Vector JavaDoc();
272         Iterator JavaDoc it = fields.values().iterator();
273         while (it.hasNext()) {
274             FieldItem field = (FieldItem) it.next();
275             if (((field.getModifiers() & filter) != 0) ^ not) {
276                 result.add(field);
277             }
278         }
279         logger.debug(" -> "+result);
280         return result;
281     }
282
283     public Collection JavaDoc getTaggedMethods(String JavaDoc attribute, boolean not) {
284         Collection JavaDoc c = getAllMethods();
285         Iterator JavaDoc it = c.iterator();
286         Vector JavaDoc result = new Vector JavaDoc();
287         int i = 0;
288         while (it.hasNext()) {
289             AbstractMethodItem method = (AbstractMethodItem) it.next();
290             if (method.getAttribute(attribute)!=null ^ not) {
291                 result.add(method);
292             }
293         }
294         return result;
295     }
296
297     public Collection JavaDoc getTaggedMembers(String JavaDoc attribute, boolean not) {
298         Collection JavaDoc result = getTaggedMethods(attribute,not);
299         result.addAll(getTaggedFields(attribute,not));
300         return result;
301     }
302
303     /**
304      * Returns the members that are tagged by a given attribute that
305      * has a given value.
306      *
307      * @param attribute the attribute id
308      * @param value the value of the attribute (must not be null!!) */

309
310     public Collection JavaDoc getTaggedMembers(String JavaDoc attribute, Object JavaDoc value) {
311         Collection JavaDoc result = getAllMembers();
312         Collection JavaDoc result2 = new Vector JavaDoc();
313         Iterator JavaDoc it = result.iterator();
314         while (it.hasNext()) {
315             MemberItem member = (MemberItem)it.next();
316             if (member.getAttribute(attribute)!=null
317                 && value.equals(member.getAttribute(attribute)))
318             {
319                 result2.add(member);
320             }
321         }
322         return result2;
323     }
324
325     /**
326      * Returns the member (method or field) of this class with the
327      * specified name.
328      * @param name the name of the member that you want.
329      * @return the member with the specified name
330      * @see #getMembers(String[]) */

331     public MemberItem getMember(String JavaDoc name) {
332         MemberItem result = null;
333         try {
334             if (name.indexOf('(')!=-1)
335                 result = getAbstractMethod(name);
336             else
337                 result = getField(name);
338         } catch(NoSuchFieldException JavaDoc e) {
339             if (result==null) {
340                 try {
341                     result = getAbstractMethod(name);
342                 } catch (NoSuchMethodException JavaDoc e2) {
343                     throw new NoSuchMemberException(this,name);
344                 }
345             }
346         }
347         return result;
348     }
349
350     /**
351      * Returns a MemberItem array.
352      *
353      * @param names the names of the members
354      * @return a MemberItem array members such as
355      * members[i].getName().equals(names[i]). If no member with a given
356      * name is found, it is ignored (but a warning is issued).
357      * @see #getMember(String)
358      */

359     public MemberItem[] getMembers(String JavaDoc[] names) {
360         MemberItem[] tmp = new MemberItem[names.length];
361         int j = 0;
362         for(int i=0; i<names.length; i++) {
363             try {
364                 tmp[j] = getMember(names[i]);
365                 j++;
366             } catch (NoSuchMemberException e) {
367                 logger.warn(e);
368             }
369         }
370         MemberItem[] members = new MemberItem[j];
371         System.arraycopy(tmp,0,members,0,j);
372         return members;
373     }
374
375     public Collection JavaDoc getAllMembers() {
376         Collection JavaDoc result = getAllMethods();
377         result.addAll(getAllFields());
378         return result;
379     }
380
381     /**
382      * Returns a FieldItem array.
383      *
384      * @param names the names of the members
385      * @return a FieldItem array fields such as
386      * fields[i].getName().equals(names[i]). If no field with a given
387      * name is found, it is ignored (but a warning is issued).
388      * @see #getField(String) */

389     public FieldItem[] getFields(String JavaDoc[] names) {
390         FieldItem[] tmp = new FieldItem[names.length];
391         int j=0;
392         for(int i=0;i<names.length;i++) {
393             try {
394                 tmp[j] = getField(names[i]);
395                 j++;
396             } catch (NoSuchFieldException JavaDoc e) {
397                 logger.warn(e);
398             }
399         }
400         FieldItem[] fields = new FieldItem[j];
401         System.arraycopy(tmp,0,fields,0,j);
402         return fields;
403     }
404
405
406     /**
407      * Returns a MethodItem array.
408      *
409      * @param names the names of the members
410      * @return a MethodItem array methods such as
411      * methods[i].getName().equals(names[i]). If no method with a given
412      * name is found, it is ignored (but a warning is issued).
413      * @see #getMethod(String)
414      */

415     public MethodItem[] getMethods(String JavaDoc[] names) {
416         MethodItem[] tmp = new MethodItem[names.length];
417         int j=0;
418         for(int i=0;i<names.length;i++) {
419             try {
420                 tmp[j] = getMethod(names[i]);
421                 j++;
422             } catch (NoSuchMethodException JavaDoc e) {
423                 logger.warn(e);
424             }
425         }
426         MethodItem[] methods = new MethodItem[j];
427         System.arraycopy(tmp,0,methods,0,j);
428         return methods;
429     }
430
431     /**
432      * Gets all the primitive fields for this class item.<p>
433      *
434      * @return an array of field items
435      */

436
437     public FieldItem[] getPrimitiveFields() {
438         buildFieldInfo();
439         Collection JavaDoc c = fields.values();
440         FieldItem[] res = new FieldItem[primsCount];
441         Iterator JavaDoc it = c.iterator();
442         int i = 0;
443         while ( it.hasNext() ) {
444             FieldItem field = (FieldItem) it.next();
445             if (field.isPrimitive()) {
446                 res[i] = field;
447                 i++;
448             }
449         }
450         return res;
451     }
452
453     /**
454      * Gets all the references for this class item.<p>
455      *
456      * @return an array of field items
457      */

458
459     public FieldItem[] getReferences() {
460         buildFieldInfo();
461         Collection JavaDoc c = fields.values();
462         FieldItem[] res = new FieldItem[refsCount];
463         Iterator JavaDoc it = c.iterator();
464         int i = 0;
465         while ( it.hasNext() ) {
466             FieldItem field = (FieldItem) it.next();
467             if (field.isReference()) {
468                 res[i] = field;
469                 i++;
470             }
471         }
472         return res;
473     }
474
475     /**
476      * Gets all the references and collections that match the
477      * expression for this class item.<p>
478      *
479      * @return a vector of field items */

480
481     public Collection JavaDoc getMatchingRelations(String JavaDoc expr) {
482         buildFieldInfo();
483         Vector JavaDoc res = new Vector JavaDoc();
484         Collection JavaDoc c = fields.values();
485         Iterator JavaDoc it = c.iterator();
486         RE re;
487         try {
488             re = new RE(expr);
489         } catch (Exception JavaDoc e) {
490             logger.error("getMatchingRelations "+expr,e);
491             return null;
492         }
493         while (it.hasNext()) {
494             FieldItem field = (FieldItem) it.next();
495             if (field.isReference() || (field instanceof CollectionItem)) {
496                 if (re.isMatch(field.getName())) {
497                     res.add( field );
498                 }
499             }
500         }
501         return res;
502     }
503
504     /**
505      * Gets all the collections for this class item.<p>
506      *
507      * @return an array of field items
508      */

509
510     public CollectionItem[] getCollections() {
511         buildFieldInfo();
512         Collection JavaDoc c = fields.values();
513         CollectionItem[] res = new CollectionItem[collsCount];
514         Iterator JavaDoc it = c.iterator();
515         int i = 0;
516         while ( it.hasNext() ) {
517             FieldItem field = (FieldItem) it.next();
518             if (field instanceof CollectionItem) {
519                 res[i] = (CollectionItem)field;
520                 i++;
521             }
522         }
523         return res;
524     }
525
526     /**
527      * Add a field item to this class item. The parent of the field is
528      * set to this.<p>
529      *
530      * @param field the new field
531      */

532     void addField(FieldItem field) {
533         buildFieldInfo();
534         boolean override = fields.containsKey(field.getName());
535         if (override) {
536             logger.warn("Overriding field "+fields.get(field.getName())+
537                         " with "+field);
538         }
539         try {
540             field.setParent(this);
541         } catch(Exception JavaDoc e) {
542             logger.error("addField "+field.getName(),e);
543             return;
544         }
545         fields.put(field.getName(), field);
546         if (field instanceof CollectionItem && !override) {
547             collsCount++;
548         } else if (field.isReference() && !override) {
549             refsCount++;
550         } else if (!override) {
551             primsCount++;
552         }
553     }
554    
555     /**
556      * Gets a set of homonym methods (including constructors) from
557      * their names.<p>
558      *
559      * @param name the name of the methods
560      * @return a <code>MethodItem</code> instance
561      */

562     public AbstractMethodItem[] getAbstractMethods(String JavaDoc name)
563         throws NoSuchMethodException JavaDoc
564     {
565         if (name.startsWith("<init>"))
566             name = getShortName()+name.substring(6);
567         //Log.trace("rtti.method","getAbstractMethods("+name+")");
568
AbstractMethodItem[] res=null;
569         if (name.endsWith(")")) {
570             String JavaDoc name2 = name.substring(0,name.indexOf("("));
571             //Log.trace("rtti.method","name2 : "+name2);
572
AbstractMethodItem[] meths =
573                 (AbstractMethodItem[])methods.get(name2);
574             if (meths!=null) {
575                 for (int i=0; i<meths.length; i++) {
576                     //Log.trace("rtti.method","trying "+meths[i].getFullName());
577
if (meths[i].getFullName().endsWith(name)) {
578                         res = new AbstractMethodItem[] {meths[i]};
579                     }
580                 }
581             }
582         } else {
583             res = (AbstractMethodItem[])methods.get(name);
584         }
585         if (res == null) {
586             throw new NoSuchMethodException JavaDoc(
587                 "ClassItem.getAbstractMethods: no such method "+name+" in "+this);
588         }
589         return res;
590     }
591
592     Hashtable JavaDoc methodCache = new Hashtable JavaDoc();
593
594     /**
595      * Gets an abstract method from its name.
596      *
597      * <p>If this method has homonym(s), then an
598      * <code>AmbiguousMethodNameException</code> is thrown.
599      *
600      * <p>An abstract method can be a static, an instance method or a
601      * constructor.
602      *
603      * @param name the name of the method to search
604      * @return the corresponding method if found
605      * @see #getMethod(String)
606      * @see #getAbstractMethods(String)
607      */

608     public AbstractMethodItem getAbstractMethod(String JavaDoc name)
609         throws NoSuchMethodException JavaDoc, AmbiguousMethodNameException
610     {
611         AbstractMethodItem cachedMethod =
612             (AbstractMethodItem)methodCache.get(name);
613         if (cachedMethod!=null) {
614             return cachedMethod;
615         } else {
616             AbstractMethodItem[] res = getAbstractMethods(name);
617             if (res.length>1) {
618                 throw new NoSuchMethodException JavaDoc("Ambiguous method name "+
619                                                 this+"."+name);
620             }
621             methodCache.put(name,res[0]);
622             return res[0];
623         }
624     }
625
626     /**
627      * Tells wether the class contains a method. All methods of the
628      * class are examined (even the private and protected ones wich
629      * are not regisered as MethodItem)
630      *
631      * @param name name of the searched method
632      * @param paramTypes types of the parameters of the searched method
633      * @see Class#getDeclaredMethod(String,Class[])
634      */

635     public boolean hasMethod(String JavaDoc name, Class JavaDoc[] paramTypes) {
636         try {
637             ((Class JavaDoc)delegate).getDeclaredMethod(name,paramTypes);
638             return true;
639         } catch(java.lang.NoSuchMethodException JavaDoc e) {
640             return false;
641         }
642     }
643
644     /**
645      * Strip the arguments part from a full method name.
646      * aMethod(aType) -> aMethod
647      * aMethod -> aMethod
648      * @param name a method name
649      */

650     static String JavaDoc stripArgs(String JavaDoc name) {
651         int index = name.indexOf("(");
652         if (index!=-1)
653             return name.substring(0,index);
654         else
655             return name;
656     }
657
658     /**
659      * Gets a set of homonym methods (excluding constructors) from
660      * their names.<p>
661      *
662      * @param name the name of the methods
663      * @return a <code>MethodItem</code> instance */

664
665     public MethodItem[] getMethods(String JavaDoc name)
666         throws NoSuchMethodException JavaDoc
667     {
668         MethodItem[] res = null;
669
670         //Log.trace("rtti.method","getMethods("+name+")");
671
if (name.endsWith(")")) {
672             String JavaDoc name2 = name.substring(0,name.indexOf("("));
673             //Log.trace("rtti.method","name2 : "+name2);
674
MethodItem[] meths = (MethodItem[])methods.get(name2);
675             if (meths!=null) {
676                 for (int i=0; i<meths.length; i++) {
677                     //Log.trace("rtti.method","trying "+meths[i].getFullName());
678
if (meths[i].getFullName().endsWith(name)) {
679                         res = new MethodItem[] {meths[i]};
680                     }
681                 }
682             }
683         } else {
684             res = (MethodItem[])methods.get(name);
685         }
686
687         /*ClassItem superClass=getSuperclass();
688      
689           if(res==null && superClass!=null) {
690           res=superClass.getMethods(name);
691           }*/

692
693         if (res == null) {
694             //Log.trace("rtti.method",2,"methods = "+methods);
695
throw new NoSuchMethodException JavaDoc(
696                 "ClassItem.getMethods: no such method "+name+" in class "+this);
697         }
698         return res;
699     }
700
701     /**
702      * Gets a method from its name.
703      *
704      * <p>If this method has homonym(s), then an
705      * <code>AmbiguousMethodNameException</code> is thrown.
706      *
707      * <p>A method can be a static or instance method but not a
708      * constructor.
709      *
710      * @param name the name of the method to search
711      * @return the corresponding method if found
712      * @see #getAbstractMethod(String)
713      * @see #getMethods(String) */

714
715     public MethodItem getMethod(String JavaDoc name)
716         throws NoSuchMethodException JavaDoc, AmbiguousMethodNameException
717     {
718         //Log.trace("rtti.method","getMethod("+name+")");
719
MethodItem[] res= getMethods(name);
720         if (res.length>1) {
721             throw new AmbiguousMethodNameException(
722                 "Ambiguous method name "+this+"."+name+" choice is:"
723                 +java.util.Arrays.asList(res));
724         }
725         return res[0];
726     }
727
728     /**
729      * Gets a set of arrays containing all the method items for this
730      * class item.
731      *
732      * <p>Each arrays contains the methods of the same name.
733      *
734      * @return a collection containing the methods
735      */

736     public Collection JavaDoc getMethods() {
737         Collection JavaDoc c = methods.values();
738         Vector JavaDoc res = new Vector JavaDoc();
739         Iterator JavaDoc it = c.iterator();
740         while (it.hasNext()) {
741             Object JavaDoc[] methods = (Object JavaDoc[]) it.next();
742             if (methods[0] instanceof MethodItem) {
743                 res.add(methods);
744             }
745         }
746         return res;
747     }
748
749     /**
750      * Gets all the method items for this class item.<p>
751      *
752      * @return a collection containing the methods
753      */

754     public Collection JavaDoc getAllMethods() {
755         Collection JavaDoc c = methods.values();
756         Vector JavaDoc res = new Vector JavaDoc();
757         Iterator JavaDoc it = c.iterator();
758         while (it.hasNext()) {
759             Object JavaDoc[] methods = (Object JavaDoc[]) it.next();
760             for (int i=0; i<methods.length; i++) {
761                 if ( methods[i] instanceof MethodItem &&
762                      !ClassRepository.isJacMethod(
763                          ((MethodItem)methods[i]).getName()) ) {
764                     res.add(methods[i]);
765                 }
766             }
767         }
768         return res;
769     }
770
771     /**
772      * Gets all the method items for this class item.<p>
773      *
774      * @return a collection containing the methods
775      */

776     public Collection JavaDoc getMixinMethods() {
777         Collection