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 }