001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>, 
003                              Laurent Martelli <laurent@aopsys.com>
004      
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful,
011      but WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013      GNU Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public License
016      along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
018    
019    package org.objectweb.jac.core.rtti;
020    
021    import java.lang.reflect.Constructor;
022    import java.lang.reflect.Field;
023    import java.lang.reflect.InvocationTargetException;
024    import java.lang.reflect.Method;
025    import java.lang.reflect.Modifier;
026    import java.util.Arrays;
027    import java.util.Collection;
028    import java.util.Enumeration;
029    import java.util.Hashtable;
030    import java.util.Iterator;
031    import java.util.Map;
032    import java.util.Vector;
033    import org.apache.log4j.Logger;
034    import org.objectweb.jac.util.Classes;
035    import org.objectweb.jac.util.Strings;
036    
037    /**
038     * This class defines the class repository of the rtti aspect.<p>
039     *
040     * It contains class items that are field and method items
041     * agregates.<p>
042     *
043     * @see ClassItem
044     * @see MethodItem
045     * @see FieldItem
046     * @see CollectionItem
047     *
048     * @author Renaud Pawlak
049     * @author Laurent Martelli
050     */
051    
052    public class ClassRepository implements LoadtimeRTTI {
053        static Logger logger = Logger.getLogger("rtti.repository");
054        static Logger loggerlt = Logger.getLogger("rtti.lt");
055    
056        Map ltClassInfos = new Hashtable();
057    
058        public ClassInfo getClassInfo(String className) {
059            ClassInfo classinfo=(ClassInfo)ltClassInfos.get(className);
060            if (classinfo==null) {
061                classinfo=new ClassInfo();
062                ltClassInfos.put(className,classinfo);
063            }
064            return classinfo;
065        }
066    
067        public void setClassInfo(String className, ClassInfo classInfo) {
068            ltClassInfos.put(className,classInfo);
069        }
070    
071        public MethodInfo getMethodInfo(String className, String method) {
072            return getClassInfo(className).getMethodInfo(method);
073        }
074    
075        /**
076         * Adds a modified field for a given method.
077         *
078         * <p>This method is automatically called at load-time by the JAC
079         * classloader through bytecode analysis.
080         *
081         * @param methodSign the method signature of the form
082         * <code>packagepath.classname.methodname</code>
083         * @param fieldName the name of the field that is modified by the
084         * method */
085    
086        public void addltModifiedField(String className, String methodSign, 
087                                       String fieldName) {
088            getClassInfo(className).addModifiedField(methodSign,fieldName);
089        }
090    
091        public void addltSetField(String className, String methodSign, 
092                                  String fieldName) {
093            getClassInfo(className).addSetField(methodSign,fieldName);
094        }
095    
096        /**
097         * Adds an accessed field for a given method.
098         *
099         * <p>This method is automatically called at load-time by the JAC
100         * classloader through bytecode analysis.
101         *
102         * @param methodSign the method signature of the form
103         * <code>packagepath.classname.methodname</code>
104         * @param fieldName the name of the field that is accessed by the
105         * method */
106    
107        public void addltAccessedField(String className, String methodSign, 
108                                       String fieldName) {
109            getClassInfo(className).addAccessedField(methodSign,fieldName);
110        }
111    
112        public void addltReturnedField(String className, String methodSign, 
113                                       String fieldName) {
114            getClassInfo(className).setReturnedField(methodSign,fieldName);
115        }
116    
117        public void setltIsGetter(String className, String methodSign, 
118                                  boolean isGetter) {
119            getClassInfo(className).setIsGetter(methodSign,isGetter);
120        }
121    
122        public void addltAddedCollection(String className, String methodSign, 
123                                         String fieldName) {
124            getClassInfo(className).addAddedCollection(methodSign,fieldName);
125        }
126    
127        public void addltRemovedCollection(String className, String methodSign, 
128                                           String fieldName) {
129            getClassInfo(className).addRemovedCollection(methodSign,fieldName);
130        }
131    
132        public void addltModifiedCollection(String className, String methodSign, 
133                                            String fieldName) {
134            getClassInfo(className).addModifiedCollection(methodSign,fieldName);
135        }
136    
137        public void setCollectionIndexArgument(String className, String methodSign, 
138                                               int argument) {
139            getClassInfo(className).setCollectionIndexArgument(methodSign,argument);
140        }
141    
142        public void setCollectionItemArgument(String className, String methodSign, 
143                                              int argument) {
144            getClassInfo(className).setCollectionItemArgument(methodSign,argument);
145        }
146    
147        public void addInvokedMethod(String className, String methodSign, 
148                                     InvokeInfo invokeInfo) {
149            getClassInfo(className).addInvokedMethod(methodSign,invokeInfo);
150        }
151    
152        public void setCallSuper(String className, String method) {
153            getMethodInfo(className,method).callSuper = true;
154        }
155    
156        /**
157         * Get the sole instance of class repository.<p>
158         * 
159         * @return the class repository */
160       
161        public static ClassRepository get() {
162            if ( classRepository == null ) {
163                classRepository = new ClassRepository();
164            }
165            return classRepository;
166        }
167       
168        /**
169         * Store the sole instance of class repository. */
170    
171        protected static transient ClassRepository classRepository = null;
172    
173        /** Stores the Jac objects root class. */
174        //public static Class jacObjectClass;
175        /** Stores the Wrappee class. */
176        public static Class wrappeeClass;
177        static {
178            try {
179                //jacObjectClass = Class.forName( "org.objectweb.jac.core.JacObject" );
180                wrappeeClass = Class.forName("org.objectweb.jac.core.Wrappee");
181            } catch(Exception e) {
182                e.printStackTrace();
183                System.exit( -1 );
184            }
185        }
186    
187        /**
188         * Store direct access to the methods of all the classes by name on
189         * optimization purpose. */
190        transient static Hashtable directMethodAccess = new Hashtable();
191    
192        /**
193         * Store direct access to the fields of all the classes by name on
194         * optimization purpose. */
195        transient static Hashtable directFieldAccess = new Hashtable();
196    
197        /**
198         * Fill the direct access to methods hashtable for a given class.
199         *
200         * <p>This is used to optimize the method calls when you only have
201         * the method name.
202         *
203         * @param cl the class to treat.
204         *
205         * @see #getDirectMethodAccess(Class,String) */
206    
207        public static Hashtable fillDirectMethodAccess(final Class cl) {
208            if( cl == null ) return null;
209            if ( directMethodAccess == null ) directMethodAccess = new Hashtable();
210            Hashtable methods = new Hashtable();
211            final Method[] meths = cl.getMethods();
212            //      AccessibleObject.setAccessible( meths, true );
213            for (int i=meths.length-1; i>=0; i--) {
214                meths[i].setAccessible(true);
215                if ( methods.containsKey(meths[i].getName()) ) {
216                    Method[] oldms = (Method[]) methods.get( meths[i].getName() );
217                    Method[] newms = new Method[oldms.length+1];
218                    System.arraycopy( oldms, 0, newms, 0, oldms.length );
219                    newms[oldms.length] = meths[i];
220                    methods.remove( meths[i].getName() );
221                    methods.put( meths[i].getName(), newms );
222                } else {
223                    methods.put( meths[i].getName(), new Method[] { meths[i] } );
224                }
225            }
226            directMethodAccess.put(cl,methods);
227            return methods;
228        }
229    
230        /**
231         * Fill the direct access to fields hashtable for a given class.
232         *
233         * <p>This is used to optimize the access of protected and private
234         * fields.
235         *
236         * @param cl the class to treat. */
237    
238        public static void fillDirectFieldAccess(final Class cl) {
239            if (cl==null || cl==Object.class) 
240                return;
241            if (directFieldAccess == null )
242                directFieldAccess = new Hashtable();
243            if (directFieldAccess.containsKey(cl)) 
244                return;
245            Hashtable fields = new Hashtable();
246            Class superClass = cl.getSuperclass();
247            if (superClass!=Object.class && superClass!=null) {
248                fillDirectFieldAccess(superClass);
249                Map inheritedFields = (Map)directFieldAccess.get(superClass);
250                if (inheritedFields!=null)
251                    fields.putAll(inheritedFields);
252            }
253            final Field[] fs = cl.getDeclaredFields();
254            //      AccessibleObject.setAccessible( fs, true );
255            for (int i = 0; i<fs.length; i++) {
256                if (!isJacField(fs[i].getName()) && !isSystemField(fs[i].getName())) {
257                    fs[i].setAccessible(true);
258                    fields.put(fs[i].getName(),fs[i]);
259                }
260            }
261            directFieldAccess.put(cl,fields);
262        }
263    
264        /**
265         * Returns a Hashtable that maps fields with their names.
266         *
267         * <p>For efficiency, the programmer should use this method instead
268         * of using the Java refection API to retrives values of protected
269         * or private fields within a class and all its superclasses.
270         * 
271         * @param cl the class
272         * @return a Map (String(field name) -> Field)
273         * 
274         * @see #fillDirectFieldAccess(Class) */
275    
276        public static Hashtable getDirectFieldAccess(Class cl) {
277            fillDirectFieldAccess(cl);
278            return ((Hashtable)directFieldAccess.get(cl));
279        }
280    
281        /**
282         * Call a directly acceded method.
283         *
284         * @param cl the class that supports the method
285         * @param methodName the method to call
286         * @param o the object to call the method on
287         * @param params the arguments
288         *
289         * @see #getDirectMethodAccess(Class,String)
290         */
291        public static Object invokeDirect(Class cl, 
292                                          String methodName, 
293                                          Object o, 
294                                          Object[] params) 
295            throws java.lang.NoSuchMethodException, 
296            InvocationTargetException, IllegalAccessException
297        {      
298            Method[] directMethods = getDirectMethodAccess(cl,methodName);
299            logger.debug("direct invocation of "+methodName+" => "+
300                         java.util.Arrays.asList(directMethods));
301            boolean ok = false;
302            boolean lengthOk = false;
303            Object ret = null;
304            for (int i=0; i<directMethods.length; i++) {
305                if (directMethods[i].getParameterTypes().length==params.length) {
306                    lengthOk = true;
307                    try {
308                        ret = directMethods[i].invoke(o, params);
309                        ok = true;
310                    } catch (IllegalArgumentException e1) {
311                    }
312                }
313            }
314            if (!ok) {
315                if (lengthOk) {
316                    logger.error("No such method "+cl.getName()+"."+methodName+Arrays.asList(params));
317                } else {
318                    logger.error("Wrong number of parameters for "+cl.getName()+"."+methodName+Arrays.asList(params));
319                }
320                throw new java.lang.NoSuchMethodException();
321            }
322            return ret;
323        }
324    
325        /**
326         * Return an array of methods that correspond to the given class
327         * and given method name.
328         *
329         * <p>For efficiency, the programmer should use this method instead
330         * of using the Java refection API.
331         * 
332         * @param cl the class where the method is supposed to be
333         * @param name the method name
334         * @return an array containing the matching methods (homonyms), a
335         * one array element with <code>null</code> inside if none method
336         * matched (this ugly result was introduced for backward
337         * compatibility)
338         * 
339         * @see #fillDirectMethodAccess(Class) 
340         */
341        public static Method[] getDirectMethodAccess(Class cl, String name) {
342            Hashtable methods = (Hashtable)directMethodAccess.get(cl);
343            if (methods==null)
344                methods = fillDirectMethodAccess(cl);
345            Method[] ret = (Method[]) methods.get(name);
346            if (ret == null ) {
347                return new Method[] { };
348            } else {
349                return ret;
350            }
351        }
352    
353        /**
354         * Returns true is this class defautlt constructor (the one with no
355         * parameter) has been added by JAC at class load-time.
356         *
357         * @param cl the class to test
358         * @return true if added, false if programmer defined 
359         */
360        public static boolean isDefaultConstructorAdded(Class cl) {
361            try {
362                cl.getField("__JAC_ADDED_DEFAULT_CONSTRUCTOR");
363            } catch (Exception e) {
364                return false;
365            }
366            return true;
367        }
368    
369        /**
370         * Return true if the method is added by JAC at class load-time.
371         *
372         * @param methodName the method to check
373         * @return true if added at load-time 
374         */
375        public static boolean isJacMethod (String methodName) {
376            if (methodName.startsWith("_"))
377                return true;
378            return false;
379        }
380    
381        /**
382         * Return true if the field is added by JAC at class load-time. 
383         * @param fieldName name of the field
384         */
385        public static boolean isJacField (String fieldName) {
386            if (fieldName.startsWith("__JAC_")) {
387                return true;
388            }
389            return false;
390        }
391    
392        /**
393         * Tells if the field is a "system" field, such as a field added by
394         * the compiler for class objects.
395         * @param fieldName name of the field */
396        public static boolean isSystemField(String fieldName) {
397            return fieldName.indexOf('$')!=-1;
398        }
399    
400        /**
401         * Gets the method names of a class
402         *
403         * @param cl the class
404         * @return the method names as an array of strings
405         */   
406        public static String[] getMethodsName(Class cl) {
407       
408            String[] methodNames = null;
409            Vector tmp = new Vector();
410          
411            try {
412          
413                Method[] methods = cl.getMethods();
414    
415                for (int i=0; i<methods.length; i++) {
416                    if ( ! ( Modifier.isStatic(methods[i].getModifiers()) ||
417                             isJacMethod(methods[i].getName()) ) ) {
418                        tmp.add (methods[i].getName());
419                    }
420                }
421    
422                methodNames = new String[tmp.size()];
423    
424                for (int i=0 ; i<tmp.size(); i++) {
425                    methodNames[i] = (String)tmp.get(i);
426                }
427    
428            } catch(Exception e) { 
429                e.printStackTrace(); 
430            }
431          
432            return methodNames;
433        }
434    
435        /**
436         * Get modifiers names.
437         *
438         * @param cl the class
439         * @return the modifiers method names as an array of strings
440         */
441       
442        public static String[] getModifiersNames(Class cl) {
443       
444            ClassItem cli = ClassRepository.get().getClass(cl);
445            Collection modifiers = cli.getAllModifiers();
446          
447            String[] methodNames = new String[modifiers.size()];
448    
449            Iterator it = modifiers.iterator();
450            for( int i=0; i<methodNames.length; i++ ) {
451                methodNames[i]=((MethodItem)it.next()).getName();
452            }
453            return methodNames;
454        }
455    
456        /**
457         * Get getter names.
458         *
459         * @param cl the class
460         * @return the modifiers names as an array of strings
461         */
462       
463        public static String[] getGettersNames(Class cl) {
464       
465            ClassItem cli = ClassRepository.get().getClass(cl);
466            Collection getters = cli.getAllGetters();
467          
468            String[] methodNames = new String[getters.size()];
469    
470            Iterator it = getters.iterator();
471            for( int i=0; i<methodNames.length; i++ ) {
472                methodNames[i]=((MethodItem)it.next()).getName();
473            }
474            return methodNames;
475        }
476    
477    
478        /**
479         * Adds a set of written fields to a method.<p>
480         *
481         * This method shortcuts the writting of RTTI aspects for
482         * programs. Equivalent effects can be achieved by using the RTTI
483         * aspect API.<p>
484         *
485         * @param cl the involved class
486         * @param methodName the method name
487         * @param fieldNames the set of fields that are written by this
488         * method
489         * @see MethodItem#addWrittenField(FieldItem)
490         */
491    
492        public static void addWrittenFields(Class cl, 
493                                            String methodName, 
494                                            String[] fieldNames)
495        {
496            ClassItem cli = ((ClassRepository)ClassRepository.get()).getClass(cl);
497            MethodItem[] mis = cli.getMethods(methodName);
498    
499            if ( mis != null ) {
500                for ( int i = 0; i < mis.length; i++ ) {
501                    for ( int j = 0; j < fieldNames.length; j++ ) {
502                        FieldItem fi = cli.getField( fieldNames[j] );
503                        if ( fi != null ) {
504                            mis[i].addWrittenField(fi);
505                        }
506                    }
507                }
508            }
509          
510        }  
511    
512        /**
513         * Adds a set of accessed fields to a method.<p>
514         *
515         * This method shortcuts the writting of RTTI aspects for
516         * programs. Equivalent effects can be achieved by using the RTTI
517         * aspect API.<p>
518         *
519         * @param cl the involved class
520         * @param methodName the method name
521         * @param fieldNames the set of fields that are accessed by this
522         * method
523         * @see MethodItem#addAccessedField(FieldItem)
524         */
525    
526        public static void addAccessedFields(Class cl, 
527                                             String methodName, 
528                                             String[] fieldNames) {
529          
530            ClassItem cli = ClassRepository.get().getClass(cl);
531            MethodItem[] mis = cli.getMethods(methodName);
532    
533            if ( mis != null ) {
534                for ( int i = 0; i < mis.length; i++ ) {
535                    for ( int j = 0; j < fieldNames.length; j++ ) {
536                        FieldItem fi = cli.getField( fieldNames[j] );
537                        if ( fi != null ) {
538                            mis[i].addAccessedField(fi);
539                        }
540                    }
541                }
542            }
543          
544        }  
545    
546        /**
547         * Adds a set of added collections to a method.<p>
548         *
549         * This method shortcuts the writting of RTTI aspects for
550         * programs. Equivalent effects can be achieved by using the RTTI
551         * aspect API.<p>
552         *
553         * @param cl the involved class
554         * @param methodName the method name
555         * @param collectionNames the set of collections that are added by this
556         * method
557         * @see MethodItem#addAddedCollection(CollectionItem) */
558    
559        public static void addAddedCollections(Class cl, 
560                                               String methodName, 
561                                               String[] collectionNames) {
562          
563            ClassItem cli = ClassRepository.get().getClass(cl);
564            MethodItem[] mis = cli.getMethods(methodName);
565    
566            if ( mis != null ) {
567                for ( int i = 0; i < mis.length; i++ ) {
568                    for ( int j = 0; j < collectionNames.length; j++ ) {
569                        CollectionItem ci = (CollectionItem)cli.getField(collectionNames[j]);
570                        if ( ci != null ) {
571                            mis[i].addAddedCollection(ci);
572                        }
573                    }
574                }
575            }
576          
577        }  
578    
579        /**
580         * Adds a set of removed collections to a method.<p>
581         *
582         * This method shortcuts the writting of RTTI aspects for
583         * programs. Equivalent effects can be achieved by using the RTTI
584         * aspect API.<p>
585         *
586         * @param cl the involved class
587         * @param methodName the method name
588         * @param collectionNames the set of collections that are removed by this
589         * method
590         * @see MethodItem#addRemovedCollection(CollectionItem) 
591         */
592        public static void addRemovedCollections(Class cl, 
593                                                 String methodName, 
594                                                 String[] collectionNames) {
595          
596            ClassItem cli = ClassRepository.get().getClass(cl);
597            MethodItem[] mis = cli.getMethods(methodName);
598    
599            if (mis != null) {
600                for (int i=0; i<mis.length; i++) {
601                    for (int j=0; j<collectionNames.length; j++) {
602                        CollectionItem ci = (CollectionItem)cli.getField(collectionNames[j]);
603                        if (ci != null) {
604                            mis[i].addRemovedCollection(ci);
605                        }
606                    }
607                }
608            }
609          
610        }
611    
612        /**
613         * The default constructor will set the classRepository field to
614         * the right value (singleton pattern). */
615    
616        public ClassRepository () {
617            classRepository = this;
618        }
619    
620        /**
621         * This method returns an existing class from its name.
622         *
623         * <p>If the class is not registered yet, the class repository
624         * automatically builds default runtime informations (using naming
625         * conventions), and seamlessly registers the new
626         * <code>ClassItem</code> instance into the class repository.
627         *
628         * <p>Note: in case of manual class registrations, a class must be
629         * registered with its full name to avoid name conflicts.<p>
630         *
631         * @param name the name of the class to get
632         * @return the class if exist, null otherwise 
633         *
634         * @see #getVirtualClass(String)
635         */
636        public ClassItem getClass(String name)
637        {
638            String wrappedName = Classes.getPrimitiveTypeWrapper(name);
639            MetaItem res = (MetaItem)getObject(wrappedName);
640            if (res == null) {
641                try {
642                    res = getClass(Class.forName(name));
643                } catch (ClassNotFoundException e) {
644                    throw new NoSuchClassException(name);
645                }
646            }
647            if (res!=null && res instanceof ClassItem)
648                return (ClassItem)res;
649            else
650                throw new NoSuchClassException(name);
651        }
652    
653        /**
654         * Returns a ClassItem or a VirtualClassItem from its name. It
655         * first tries to find a ClassItem, and if it fails, it returns a
656         * VirtualClassItem.
657         *
658         * @param name the name of the class to find
659         * @return a ClassItem or a VirtualClassItem 
660         *
661         * @see #getClass(String)
662         * @see #getVirtualClassStrict(String)
663         */
664    
665        public MetaItem getVirtualClass(String name) 
666        {
667            MetaItem ret;
668            try {
669                ret = getClass(name);
670            } catch (NoSuchClassException e) {
671                ret = (MetaItem)getObject(name);
672                if (ret == null)
673                    throw e;
674            }
675            return ret;
676        }
677    
678        /**
679         * Returns a VirtualClassItem from its name.
680         * @param name the name of the class to find
681         * @return the VirtualClassItem with the requested name
682         * @see #getClass(String)
683         * @see #getVirtualClass(String)
684         */
685        public VirtualClassItem getVirtualClassStrict(String name) 
686        {
687            MetaItem ret;
688            ret = (MetaItem)getObject(name);
689            if (ret == null || !(ret instanceof VirtualClassItem))
690                throw new NoSuchClassException(name);
691            return (VirtualClassItem)ret;
692        }
693    
694        /**
695         * This method returns the class item that corresponds to the given
696         * object class.<p>
697         *
698         * @param object the object to get the class item
699         * @return the class item
700         * @see #getClass(String) */
701    
702        public ClassItem getClass(Object object) {
703            return getNonPrimitiveClass(object.getClass().getName());
704        }
705        /**
706         * This method returns the class item that corresponds to the given
707         * class.<p>
708         *
709         * @param cl the class to get the class item of
710         * @return the class item
711         * @see #getClass(String) 
712         */
713        public ClassItem getClass(Class cl) {
714            String wrappedName = Classes.getPrimitiveTypeWrapper(cl.getName());
715            MetaItem res = (MetaItem)getObject(wrappedName);
716            if (res == null) {
717                res = buildDefaultRTTI(cl);
718                if (res != null) 
719                    register(wrappedName, res);
720            }
721            if (res!=null && res instanceof ClassItem)
722                return (ClassItem)res;
723            else
724                throw new NoSuchClassException(cl.getName());
725            /*      return getClass(cl.getName()); */
726        }
727    
728        public ClassItem getNonPrimitiveClass(String className) {
729            try {
730                // !!! Class.forName() can trigger the instantiation of
731                // !!! the ClassItem
732                Class cl = Class.forName(className);
733                MetaItem res = (MetaItem)getObject(className);
734                if (res == null) {
735                    try {
736                        res = buildDefaultRTTI(cl);
737                    } catch(Exception e) {
738                        throw new NoSuchClassException(className);
739                    }
740                    if (res != null) {
741                        register(className, res);
742                        return (ClassItem)res;
743                    }
744                } else {
745                    return (ClassItem)res;
746                }
747                throw new NoSuchClassException(className);
748            } catch(ClassNotFoundException e) {
749                throw new NoSuchClassException(className);
750            }
751        }
752    
753        String ignoreFields = null;
754        public void ignoreFields(String packageExpr) {
755            ignoreFields = packageExpr;
756        }
757    
758        /**
759         * This method builds the default RTTI for a given class name and
760         * returns a corresponding class item.<p>
761         *
762         * @param cl the class to build
763         * @return the corresponding class item, null if the given name is
764         * not a class name 
765         */
766        public ClassItem buildDefaultRTTI(Class cl) {
767            ClassItem cli = null;
768            logger.debug( ">>> Constructing RTTI infos for class "+cl.getName()+Strings.hash(cl));
769            try {
770    
771                cli = new ClassItem(cl);
772    
773                // methods
774                Method[] meths = cl.getMethods();
775                //Log.trace("rtti.methods","adding methods for "+cli);
776                for(int i=0; i<meths.length; i++) {
777                    if (meths[i].getDeclaringClass() == Object.class 
778                        /*|| meths[i].getName().startsWith("_")*/ ) continue;
779                    Logger.getLogger("rtti."+cl.getName()).debug("    adding method "+meths[i]);
780                    MethodItem mi = new MethodItem(meths[i],cli);
781                    cli.addMethod(mi);
782                }
783    
784                // constructors
785                Constructor[] constructors = cl.getConstructors();
786                for(int i=0; i<constructors.length; i++) {
787                    if ( ! ( isDefaultConstructorAdded(cl) && 
788                             constructors[i].getParameterTypes().length == 0 ) ) {
789                        cli.addConstructor(new ConstructorItem(constructors[i],cli));
790                    }
791                }
792    
793            } catch (InvalidDelegateException e) {
794                logger.error("buildDefaultRTTI("+cl.getName()+"): "+e);
795                return null;
796            }
797            logger.debug( ">>> DONE Constructing RTTI infos for class "+cl.getName()+Strings.hash(cl)+
798                          " -> "+cli+Strings.hash(cli));
799            return cli;
800        }
801    
802        public void buildDefaultFieldRTTI(ClassItem cli) {
803            loggerlt.debug("constructing fields info for "+cli.getName());
804            Class cl = cli.getActualClass();
805            Collection fields = getDirectFieldAccess(cl).values();
806            Iterator it = fields.iterator();
807            while (it.hasNext()) {
808                Field f = (Field) it.next();
809                //logger.debug( "constructing " + f );
810                Class t = f.getType();
811                FieldItem fi = null;
812                MethodItem mis[] = null;
813                Method m = null;
814                try {
815                    if (RttiAC.isCollectionType(t))
816                    {
817                        logger.debug(t.getName()+" -> "+f.getName()+ " is a collection");
818                        fi = new CollectionItem(f,cli);
819                    } else {
820                        logger.debug(f.getName()+ " is a field");
821                        fi = new FieldItem(f,cli);
822                    }
823                } catch (InvalidDelegateException e) {
824                    e.printStackTrace();
825                }
826                cli.addField(fi);
827            }
828            Logger logger = Logger.getLogger("rtti."+cli.getName());
829            logger.debug("extracting bytecoded info for "+cli.getName());
830            logger.debug("methods "+cli.getAllMethods());
831            Iterator methods = cli.getAllMethods().iterator();
832            while (methods.hasNext()) {
833                MethodItem method = (MethodItem)methods.next();
834                try {
835                    logger.debug("    "+method.getFullName());
836                    AbstractMethodItem realMethod = method.getConcreteMethod();
837                    String key = realMethod.getOwningClass().getName()+"."+realMethod.getFullName();
838                    ClassInfo classinfo = getClassInfo(realMethod.getOwningClass().getName());
839                    MethodInfo methodinfo = classinfo.getMethodInfo(key);
840                    if (methodinfo.accessedFields.size()>0) {
841                        logger.debug("        accessed fields: "+methodinfo.accessedFields);
842                        it = methodinfo.accessedFields.iterator();
843                        while (it.hasNext()) {
844                            String fieldName = (String)it.next();
845                            FieldItem fi = cli.getField(fieldName);
846                            method.addAccessedField(fi);
847                            fi.addAccessingMethod(method);
848                        }
849                    }
850                    if (methodinfo.returnedField!=null && methodinfo.isGetter) {
851                        logger.debug("        returned field: "+
852                                     methodinfo.returnedField);
853                        FieldItem fi = cli.getField(methodinfo.returnedField);
854                        method.setReturnedField(fi);
855                        fi.setGetter(method);
856                    }
857                    if (methodinfo.collectionIndexArgument!=-1) {
858                        logger.debug("        collectionIndexArgument : "+
859                                     methodinfo.collectionIndexArgument);
860                        method.setCollectionIndexArgument(methodinfo.collectionIndexArgument);
861                    }
862                    if (methodinfo.collectionItemArgument!=-1) {
863                        logger.debug("        collectionItemArgument : "+
864                                     methodinfo.collectionItemArgument);
865                        method.setCollectionItemArgument(methodinfo.collectionItemArgument);
866                    }
867                    if (methodinfo.modifiedFields.size()>0) {
868                        logger.debug("        modified fields: "+methodinfo.modifiedFields);
869                        it = methodinfo.modifiedFields.iterator();
870                        while(it.hasNext()) {
871                            String fieldName = (String)it.next();
872                            try {
873                                FieldItem fi = cli.getField(fieldName);
874                                method.addWrittenField(fi);
875                                fi.addWritingMethod(method);
876                            } catch (NoSuchFieldException e) {
877                                // On Windows, JDK 1.4.0_02, static private fields
878                                // used for <MyClass>.class are not found here
879                            }
880                        }
881                    }
882                    if (methodinfo.setFields.size()==1) {
883                        logger.debug("         set fields: "+methodinfo.setFields);
884                        it = methodinfo.setFields.iterator();
885                        while(it.hasNext()) {
886                            String fieldName = (String)it.next();
887                            FieldItem fi = cli.getField(fieldName);
888                            method.setSetField(fi);
889                            fi.setSetter(method);
890                        }
891                    } else if (methodinfo.setFields.size()>1) {
892                        logger.warn(
893                            cli.getName()+" sets more than one field: "+
894                            methodinfo.setFields);
895                    }
896                    if (methodinfo.addedCollections.size()>0) {
897                        logger.debug("        added collections: "+
898                                     methodinfo.addedCollections);
899                        it = methodinfo.addedCollections.iterator();
900                        while(it.hasNext()) {
901                            String fieldName = (String)it.next();
902                            CollectionItem ci = cli.getCollection(fieldName);
903                            method.addAddedCollection(ci);
904                            ci.addAddingMethod(method);
905                        }
906                    }
907                    if (methodinfo.removedCollections.size()>0) {
908                        logger.debug("        removed collections: "+
909                                     methodinfo.removedCollections);
910                        it = methodinfo.removedCollections.iterator();
911                        while(it.hasNext()) {
912                            String fieldName = (String)it.next();
913                            CollectionItem ci = cli.getCollection(fieldName);
914                            method.addRemovedCollection(ci);
915                            ci.addRemovingMethod(method);
916                        }
917                    }
918                    if (methodinfo.modifiedCollections.size()>0) {
919                        logger.debug("        modified collections: "+
920                                     methodinfo.modifiedCollections);
921                        it = methodinfo.modifiedCollections.iterator();
922                        while(it.hasNext()) {
923                            String fieldName = (String)it.next();
924                            CollectionItem ci = cli.getCollection(fieldName);
925                            method.addModifiedCollection(ci);
926                            ci.addWritingMethod(method);
927                        }
928                    }
929                    if (method.getType()!=void.class &&
930                        methodinfo.invokedMethods.size()>0) {
931                        logger.debug("        invoked methods: "+methodinfo.invokedMethods);
932                        it = methodinfo.invokedMethods.iterator();
933                        while(it.hasNext()) {
934                            InvokeInfo invokeInfo = (InvokeInfo)it.next();
935                            ClassItem invokedClass = getClass(invokeInfo.className);
936                            try {
937                                MethodItem invokedMethod =
938                                    invokedClass.getMethod(invokeInfo.method);
939                                if (invokedMethod.getType()!=void.class)
940                                    invokedMethod.addDependentMethod(method);
941                            } catch(Exception e) {
942                            }
943                        }               
944                    }
945                    if (methodinfo.callSuper) {
946                        ClassItem superClass = cli.getSuperclass();
947                        logger.debug("        calls super");
948                        if (superClass!=null) {
949                            try {
950                                MethodItem superMethod = 
951                                    superClass.getMethod(method.getFullName());
952                                if (superMethod.isAdder()) {
953                                    CollectionItem[] addedCollections = 
954                                        superMethod.getAddedCollections();
955                                    logger.debug("        added collections: "+
956                                                 Arrays.asList(addedCollections));
957                                    for (int i=0; i<addedCollections.length; i++) {
958                                        CollectionItem collection = 
959                                            cli.getCollection(addedCollections[i].getName());
960                                        method.addAddedCollection(collection);
961                                        collection.addAddingMethod(method);
962                                    }
963                                }
964                                if (superMethod.isRemover()) {
965                                    CollectionItem[] removedCollections = 
966                                        superMethod.getRemovedCollections();
967                                    logger.debug("        removed collections: "+
968                                                 Arrays.asList(removedCollections));
969                                    for (int i=0; i<removedCollections.length; i++) {
970                                        CollectionItem collection = 
971                                            cli.getCollection(removedCollections[i].getName());
972                                        method.addRemovedCollection(collection);
973                                        collection.addRemovingMethod(method);
974                                    }
975                                }
976                                if (superMethod.hasModifiedCollections()) {
977                                    CollectionItem[] modifiedCollections = 
978                                        method.getModifiedCollections();
979                                    logger.debug("        modified collections: "+
980                                                 Arrays.asList(modifiedCollections));
981                                    for (int i=0; i<modifiedCollections.length; i++) {
982                                        CollectionItem collection = 
983                                            cli.getCollection(modifiedCollections[i].getName());
984                                        method.addModifiedCollection(collection);
985                                        collection.addWritingMethod(method);
986                                    }
987                                }
988                                if (superMethod.isSetter()) {
989                                    FieldItem field = 
990                                        cli.getField(superMethod.getSetField().getName());
991                                    method.setSetField(field);
992                                    field.setSetter(method);
993                                    logger.debug("        set field: "+field);
994                                }
995                                if (superMethod.hasWrittenFields()) {
996                                    FieldItem[] writtenFields = 
997                                        superMethod.getWrittenFields();
998                                    for (int i=0; i<writtenFields.length; i++) {
999                                        FieldItem field = cli.getField(writtenFields[i].getName());
1000                                        method.addWrittenField(field);
1001                                        field.addWritingMethod(method);                        
1002                                    }
1003                                }
1004                            } catch (NoSuchMethodException e) {
1005                                // The superclass' method may be
1006                                // protected, and we shouldn't issue a
1007                                // warning in this case
1008                                if (!superClass.hasMethod(method.getName(),method.getParameterTypes()))
1009                                    logger.warn(
1010                                        "Method "+method.getFullName()+
1011                                        " calls super but super class "+superClass.getName()+
1012                                        " does not have a public method with that name");
1013                            }
1014                        } else {
1015                            logger.warn("Method "+method.getFullName()+
1016                                        " calls super but class does not have a super class");
1017                        }
1018                    }
1019                } catch (Exception e) {
1020                    logger.error("Failed RTTI build for "+method,e);
1021                }
1022            }
1023    
1024            // Inherited mixin methods
1025            ClassItem superClass = cli.getSuperclass();
1026            if (superClass!=null) {
1027                Iterator i = superClass.getMixinMethods().iterator();
1028                while (i.hasNext()) {
1029                    MixinMethodItem method = (MixinMethodItem)i.next();
1030                    try {
1031                        cli.addMethod(new MixinMethodItem((Method)method.getDelegate(),cli));
1032                        logger.debug("Adding inherited mixin method "+method.getFullName());
1033                    } catch(InvalidDelegateException e) {
1034                        logger.error("Failed to add inherited mixin method "+
1035                                     method.getFullName(),e);
1036                    }
1037                }
1038            }
1039        }
1040    
1041        /**
1042         * Link class items to their names */
1043        public Hashtable objects = new Hashtable();
1044    
1045        /**
1046         * Reverse hashtable to find an class item from its name */
1047        public Hashtable names = new Hashtable();
1048    
1049        /**
1050         * Register a new class into the class repository.
1051         *
1052         * @param logicalName the key that allows to find the class
1053         * @param object the class to register
1054         * @return true if the class registered, false if already
1055         * registered
1056         *
1057         * @see #unregister(String) 
1058         */
1059        public void register(String logicalName, Object object) {
1060            Object o = objects.get(logicalName);
1061            boolean result = true;
1062            if (o != null) {
1063                throw new RuntimeException("Class "+logicalName+" is already registered: "+o);
1064            }
1065            objects.put(logicalName, object);      
1066            names.put(object, logicalName);
1067        }
1068    
1069        /**
1070         * Unregister a class from the repository.
1071         *
1072         * @param logicalName the class name
1073         *
1074         * @see #register(String,Object) 
1075         */
1076        public void unregister(String logicalName) {
1077            if (objects.get(logicalName) == null) {
1078                return;
1079            }
1080            names.remove(objects.get(logicalName));
1081            objects.remove(logicalName);
1082        }
1083    
1084        /**
1085         * Returns true if a class is registered with this name.
1086         *
1087         * @param logicalName the key that allows to find the class
1088         *
1089         * @see #register(String,Object) 
1090         */
1091        public boolean isRegistered(String logicalName) {
1092            if (names.contains(logicalName)) {
1093                return true;
1094            }
1095            return false;
1096        }
1097    
1098        /**
1099         * Return all the registered classes as an array.
1100         *
1101         * <p>Reverse operation is <code>getNames()</code>.
1102         *
1103         * @return the registered classes in this repository
1104         *
1105         * @see #register(String,Object)
1106         * @see #getNames() 
1107         */
1108        public Object[] getClasses() {
1109            return objects.values().toArray();
1110        }
1111    
1112        /**
1113         * Return the names of the registered classes as an array.    
1114         *
1115         * @return the registered classes names in this repository
1116         *
1117         * @see #register(String,Object) 
1118         */
1119        public Object[] getNames() {
1120            return names.values().toArray();
1121        }
1122    
1123        /**
1124         * Return a registered classes for a given logical name.
1125         * 
1126         * <p>Return <code>null</code> if the name does not correspond to
1127         * any registered class or if <code>logicalName</code> is null.
1128         *
1129         * <p>Reverse operation is <code>getName(Object)</code>.
1130         *
1131         * @param logicalName the key that allows to find the class
1132         * @return the corresponding object, null if not registered
1133         *
1134         * @see #register(String,Object)
1135         * @see #getName(Object) 
1136         */
1137        public Object getObject(String logicalName) {
1138            if (logicalName == null) return null;
1139            return objects.get(logicalName);
1140        }
1141    
1142        /**
1143         * Returns the name of a registered class. Null if not
1144         * registered.
1145         *
1146         * <p>Reverse operation is <code>getObject(String)</code>.
1147         *
1148         * @param object the class to get the name of
1149         * @return the class name, null if not registered
1150         *
1151         * @see #register(String,Object) 
1152         * @see #getObject(String)
1153         */
1154        public String getName (Object object) {
1155            if (object == null) {
1156                return null;
1157            }
1158            return (String)names.get(object);
1159        }
1160    
1161        /**
1162         * Returns a field whose owning class is the class of an object
1163         *
1164         * @param substance the object whose class to user
1165         * @param field the field to return
1166         */
1167        public FieldItem getActualField(Object substance, FieldItem field) {
1168            return getClass(substance).getField(field.getName());
1169        }
1170    
1171        /**
1172         * Dump all the registered classes in this class repository. 
1173         */
1174        public void dump() {
1175            Enumeration keys = objects.keys();
1176          
1177            System.out.println ( getClass().getName() + " dump:");
1178            while (keys.hasMoreElements()) {
1179                String key = (String) keys.nextElement();
1180                System.out.println(" - " + key + " : " + objects.get( key ) );
1181            }
1182        }
1183    
1184        /* instantiate an object from a string */
1185        public static Object instantiate(Class type, String value) {
1186            if (type==int.class) {
1187                type = Integer.class;
1188            } else if (type==float.class) {
1189                type = Float.class;
1190            } else if (type==double.class) {
1191                type = Double.class;
1192            } else if (type==boolean.class) {
1193                type = Boolean.class;
1194            } else if (type==byte.class) {
1195                type = Byte.class;
1196            } else if (type==char.class) {
1197                type = Character.class;
1198            } else if (type==long.class) {
1199                type = Long.class;
1200            } else if (type==short.class) {
1201                type = Short.class;
1202            }
1203            try {
1204                Constructor constructor = type.getConstructor(new Class[] {String.class});
1205                return constructor.newInstance(new Object[] {value});
1206            } catch (Exception e) {
1207                e.printStackTrace();
1208                return null;
1209            }
1210        }
1211    }