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 gnu.regexp.RE; 022 import java.lang.reflect.InvocationTargetException; 023 import java.lang.reflect.Modifier; 024 import java.util.Collection; 025 import java.util.Hashtable; 026 import java.util.Iterator; 027 import java.util.List; 028 import java.util.Vector; 029 import org.apache.log4j.Logger; 030 import java.lang.reflect.Constructor; 031 032 /** 033 * This class defines a meta item that corresponds to the 034 * <code>java.lang.reflect.Class</code> meta element.<p> 035 * 036 * @author Renaud Pawlak 037 * @author Laurent Martelli 038 */ 039 040 public class ClassItem extends MetaItemDelegate { 041 042 static Logger logger = Logger.getLogger("rtti.class"); 043 044 static Class wrappeeClass = ClassRepository.wrappeeClass; 045 046 /** 047 * Default contructor to create a new class item object.<p> 048 * 049 * @param delegate the <code>java.lang.reflect.Class</code> actual 050 * meta item 051 */ 052 public ClassItem(Class delegate) throws InvalidDelegateException { 053 super(delegate); 054 Class superClass = delegate.getSuperclass(); 055 if (superClass!=Object.class && superClass!=null) { 056 setSuperClass(ClassRepository.get().getClass(superClass)); 057 } 058 } 059 060 private int collsCount = 0; 061 private int refsCount = 0; 062 private int primsCount = 0; 063 private int methodsCount = 0; 064 private int constructorsCount = 0; 065 066 /** 067 * Stores the fields of the class.<p> */ 068 069 protected Hashtable fields = new Hashtable(); 070 071 /** 072 * Stores the methods of the class. */ 073 074 protected Hashtable methods = new Hashtable(); 075 076 boolean rttiBuilt = false; 077 078 protected void buildFieldInfo() { 079 if (!rttiBuilt) { 080 rttiBuilt = true; 081 ClassRepository.get().buildDefaultFieldRTTI(this); 082 } 083 } 084 085 /** 086 * Gets a field from its name. Throw an exception if the field does 087 * not exist.<p> 088 * 089 * @param name the field name. If name contains a dot, it is 090 * considered an expression field and is automatically created if 091 * it does not exist yet. 092 * @return a <code>FieldItem</code> instance 093 * @see #getFieldNoError(String) 094 */ 095 public FieldItem getField(String name) { 096 buildFieldInfo(); 097 FieldItem ret = getFieldNoError(name); 098 if (ret==null) { 099 throw new NoSuchFieldException(this,name); 100 } 101 return ret; 102 } 103 104 /** 105 * Gets a field from its name. Don't throw an exception if the 106 * field does not exist.<p> 107 * 108 * @param name the field name 109 * @return a <code>FieldItem</code> instance, or null if no such 110 * field exists in the class 111 * @see #getField(String) 112 */ 113 public FieldItem getFieldNoError(String name) { 114 buildFieldInfo(); 115 FieldItem field = (FieldItem)fields.get(name); 116 if (field==null) { 117 if (name.indexOf('.')!=-1) { 118 try { 119 List path = parseExpression(name); 120 if (path.get(path.size()-1) instanceof CollectionItem) { 121 field = new CollectionItem(name,path,this); 122 } else { 123 field = new FieldItem(name,path,this); 124 } 125 addField(field); 126 } catch (Exception e) { 127 field = null; 128 logger.error("Expression field "+this.getName()+"."+name+ 129 " instanciation failed: "+e); 130 e.printStackTrace(); 131 } 132 } 133 } 134 if (field==null && superClass!=null) { 135 field = superClass.getFieldNoError(name); 136 if (field!=null) { 137 field = field.clone(this); 138 addField(field); 139 } 140 } 141 return field; 142 } 143 144 /** 145 * Parse the expression and computes its type. 146 * @param expression the expression (<field>.<field>.<field>...) 147 */ 148 public List parseExpression(String expression) { 149 Vector path = new Vector(); 150 String current = expression; 151 ClassItem curClass = this; 152 int dot = current.indexOf('.'); 153 FieldItem field = null; 154 while (dot!=-1) { 155 String fieldName = current.substring(0,dot); 156 field = curClass.getField(fieldName); 157 path.add(field); 158 if (field instanceof CollectionItem) 159 curClass = ((CollectionItem)field).getComponentType(); 160 else 161 curClass = field.getTypeItem(); 162 current = current.substring(dot+1); 163 dot = current.indexOf('.'); 164 } 165 166 field = curClass.getField(current); 167 path.add(field); 168 169 /* 170 if (field!=null) { 171 type = field.getType(); 172 setter = field.getSetter(); 173 } 174 */ 175 176 return path; 177 } 178 179 /** 180 * Gets a collection from its name.<p> 181 * 182 * @param name the collection name 183 * @return a <code>CollectionItem</code> instance 184 */ 185 186 public CollectionItem getCollection(String name) { 187 buildFieldInfo(); 188 return (CollectionItem) fields.get(name); 189 } 190 191 /** 192 * Gets all the fields for this class item.<p> 193 * 194 * @return an array of field items 195 */ 196 197 public FieldItem[] getFields() { 198 buildFieldInfo(); 199 Collection c = fields.values(); 200 FieldItem[] res = new FieldItem[c.size()]; 201 Iterator it = c.iterator(); 202 int i = 0; 203 while (it.hasNext()) { 204 res[i] = (FieldItem) it.next(); 205 i++; 206 } 207 return res; 208 } 209 210 /** 211 * Gets all the fields for this class item 212 * 213 * @return the fields as a collection 214 */ 215 216 public Collection getAllFields() { 217 buildFieldInfo(); 218 return fields.values(); 219 } 220 221 /** 222 * Gets all the current class item's fields that are tagged or not 223 * tagged with the given attribute.<p> 224 * 225 * @param attribute the attribute 226 * @param not if true, returns fields not tagged with attribute 227 * 228 * @return a collection of field items 229 */ 230 public Collection getTaggedFields(String attribute, boolean not) { 231 buildFieldInfo(); 232 Collection c = fields.values(); 233 Iterator it = c.iterator(); 234 Vector result = new Vector(); 235 int i = 0; 236 while (it.hasNext()) { 237 FieldItem field = (FieldItem) it.next(); 238 if (field.getAttribute(attribute)!=null ^ not) { 239 result.add(field); 240 } 241 } 242 return result; 243 } 244 245 /** 246 * @param expression something like [!](static|transient|private|public|protected) 247 */ 248 public Collection filterFields(String expression) { 249 logger.debug(this+".filterFields "+expression); 250 String keyword; 251 boolean not = false; 252 if (expression.charAt(0)=='!') { 253 not = true; 254 expression = expression.substring(1,expression.length()); 255 } 256 int filter = 0; 257 if (expression.equals("static")) 258 filter = Modifier.STATIC; 259 else if (expression.equals("public")) 260 filter = Modifier.PUBLIC; 261 else if (expression.equals("transient")) 262 filter = Modifier.TRANSIENT; 263 else if (expression.equals("private")) 264 filter = Modifier.PRIVATE; 265 else if (expression.equals("protected")) 266 filter = Modifier.PROTECTED; 267 Vector result = new Vector(); 268 Iterator it = fields.values().iterator(); 269 while (it.hasNext()) { 270 FieldItem field = (FieldItem) it.next(); 271 if (((field.getModifiers() & filter) != 0) ^ not) { 272 result.add(field); 273 } 274 } 275 logger.debug(" -> "+result); 276 return result; 277 } 278 279 public Collection getTaggedMethods(String attribute, boolean not) { 280 Collection c = getAllMethods(); 281 Iterator it = c.iterator(); 282 Vector result = new Vector(); 283 int i = 0; 284 while (it.hasNext()) { 285 AbstractMethodItem method = (AbstractMethodItem) it.next(); 286 if (method.getAttribute(attribute)!=null ^ not) { 287 result.add(method); 288 } 289 } 290 return result; 291 } 292 293 public Collection getTaggedMembers(String attribute, boolean not) { 294 Collection result = getTaggedMethods(attribute,not); 295 result.addAll(getTaggedFields(attribute,not)); 296 return result; 297 } 298 299 /** 300 * Returns the members that are tagged by a given attribute that 301 * has a given value. 302 * 303 * @param attribute the attribute id 304 * @param value the value of the attribute (must not be null!!) */ 305 306 public Collection getTaggedMembers(String attribute, Object value) { 307 Collection result = getAllMembers(); 308 Collection result2 = new Vector(); 309 Iterator it = result.iterator(); 310 while (it.hasNext()) { 311 MemberItem member = (MemberItem)it.next(); 312 if (member.getAttribute(attribute)!=null 313 && value.equals(member.getAttribute(attribute))) 314 { 315 result2.add(member); 316 } 317 } 318 return result2; 319 } 320 321 /** 322 * Returns the member (method or field) of this class with the 323 * specified name. 324 * @param name the name of the member that you want. 325 * @return the member with the specified name 326 * @see #getMembers(String[]) */ 327 public MemberItem getMember(String name) { 328 MemberItem result = null; 329 try { 330 if (name.indexOf('(')!=-1) 331 result = getAbstractMethod(name); 332 else 333 result = getField(name); 334 } catch(NoSuchFieldException e) { 335 if (result==null) { 336 try { 337 result = getAbstractMethod(name); 338 } catch (NoSuchMethodException e2) { 339 throw new NoSuchMemberException(this,name); 340 } 341 } 342 } 343 return result; 344 } 345 346 /** 347 * Returns a MemberItem array. 348 * 349 * @param names the names of the members 350 * @return a MemberItem array members such as 351 * members[i].getName().equals(names[i]). If no member with a given 352 * name is found, it is ignored (but a warning is issued). 353 * @see #getMember(String) 354 */ 355 public MemberItem[] getMembers(String[] names) { 356 MemberItem[] tmp = new MemberItem[names.length]; 357 int j = 0; 358 for(int i=0; i<names.length; i++) { 359 try { 360 tmp[j] = getMember(names[i]); 361 j++; 362 } catch (NoSuchMemberException e) { 363 logger.warn(e); 364 } 365 } 366 MemberItem[] members = new MemberItem[j]; 367 System.arraycopy(tmp,0,members,0,j); 368 return members; 369 } 370 371 public Collection getAllMembers() { 372 Collection result = getAllMethods(); 373 result.addAll(getAllFields()); 374 return result; 375 } 376 377 /** 378 * Returns a FieldItem array. 379 * 380 * @param names the names of the members 381 * @return a FieldItem array fields such as 382 * fields[i].getName().equals(names[i]). If no field with a given 383 * name is found, it is ignored (but a warning is issued). 384 * @see #getField(String) */ 385 public FieldItem[] getFields(String[] names) { 386 FieldItem[] tmp = new FieldItem[names.length]; 387 int j=0; 388 for(int i=0;i<names.length;i++) { 389 try { 390 tmp[j] = getField(names[i]); 391 j++; 392 } catch (NoSuchFieldException e) { 393 logger.warn(e); 394 } 395 } 396 FieldItem[] fields = new FieldItem[j]; 397 System.arraycopy(tmp,0,fields,0,j); 398 return fields; 399 } 400 401 402 /** 403 * Returns a MethodItem array. 404 * 405 * @param names the names of the members 406 * @return a MethodItem array methods such as 407 * methods[i].getName().equals(names[i]). If no method with a given 408 * name is found, it is ignored (but a warning is issued). 409 * @see #getMethod(String) 410 */ 411 public MethodItem[] getMethods(String[] names) { 412 MethodItem[] tmp = new MethodItem[names.length]; 413 int j=0; 414 for(int i=0;i<names.length;i++) { 415 try { 416 tmp[j] = getMethod(names[i]); 417 j++; 418 } catch (NoSuchMethodException e) { 419 logger.warn(e); 420 } 421 } 422 MethodItem[] methods = new MethodItem[j]; 423 System.arraycopy(tmp,0,methods,0,j); 424 return methods; 425 } 426 427 /** 428 * Gets all the primitive fields for this class item.<p> 429 * 430 * @return an array of field items 431 */ 432 433 public FieldItem[] getPrimitiveFields() { 434 buildFieldInfo(); 435 Collection c = fields.values(); 436 FieldItem[] res = new FieldItem[primsCount]; 437 Iterator it = c.iterator(); 438 int i = 0; 439 while ( it.hasNext() ) { 440 FieldItem field = (FieldItem) it.next(); 441 if (field.isPrimitive()) { 442 res[i] = field; 443 i++; 444 } 445 } 446 return res; 447 } 448 449 /** 450 * Gets all the references for this class item.<p> 451 * 452 * @return an array of field items 453 */ 454 455 public FieldItem[] getReferences() { 456 buildFieldInfo(); 457 Collection c = fields.values(); 458 FieldItem[] res = new FieldItem[refsCount]; 459 Iterator it = c.iterator(); 460 int i = 0; 461 while ( it.hasNext() ) { 462 FieldItem field = (FieldItem) it.next(); 463 if (field.isReference()) { 464 res[i] = field; 465 i++; 466 } 467 } 468 return res; 469 } 470 471 /** 472 * Gets all the references and collections that match the 473 * expression for this class item.<p> 474 * 475 * @return a vector of field items */ 476 477 public Collection getMatchingRelations(String expr) { 478 buildFieldInfo(); 479 Vector res = new Vector(); 480 Collection c = fields.values(); 481 Iterator it = c.iterator(); 482 RE re; 483 try { 484 re = new RE(expr); 485 } catch (Exception e) { 486 logger.error("getMatchingRelations "+expr,e); 487 return null; 488 } 489 while (it.hasNext()) { 490 FieldItem field = (FieldItem) it.next(); 491 if (field.isReference() || (field instanceof CollectionItem)) { 492 if (re.isMatch(field.getName())) { 493 res.add( field ); 494 } 495 } 496 } 497 return res; 498 } 499 500 /** 501 * Gets all the collections for this class item.<p> 502 * 503 * @return an array of field items 504 */ 505 506 public CollectionItem[] getCollections() { 507 buildFieldInfo(); 508 Collection c = fields.values(); 509 CollectionItem[] res = new CollectionItem[collsCount]; 510 Iterator it = c.iterator(); 511 int i = 0; 512 while ( it.hasNext() ) { 513 FieldItem field = (FieldItem) it.next(); 514 if (field instanceof CollectionItem) { 515 res[i] = (CollectionItem)field; 516 i++; 517 } 518 } 519 return res; 520 } 521 522 /** 523 * Add a field item to this class item. The parent of the field is 524 * set to this.<p> 525 * 526 * @param field the new field 527 */ 528 void addField(FieldItem field) { 529 buildFieldInfo(); 530 boolean override = fields.containsKey(field.getName()); 531 if (override) { 532 logger.warn("Overriding field "+fields.get(field.getName())+ 533 " with "+field); 534 } 535 try { 536 field.setParent(this); 537 } catch(Exception e) { 538 logger.error("addField "+field.getName(),e); 539 return; 540 } 541 fields.put(field.getName(), field); 542 if (field instanceof CollectionItem && !override) { 543 collsCount++; 544 } else if (field.isReference() && !override) { 545 refsCount++; 546 } else if (!override) { 547 primsCount++; 548 } 549 } 550 551 /** 552 * Gets a set of homonym methods (including constructors) from 553 * their names.<p> 554 * 555 * @param name the name of the methods 556 * @return a <code>MethodItem</code> instance 557 */ 558 public AbstractMethodItem[] getAbstractMethods(String name) 559 throws NoSuchMethodException 560 { 561 if (name.startsWith("<init>")) 562 name = getShortName()+name.substring(6); 563 //Log.trace("rtti.method","getAbstractMethods("+name+")"); 564 AbstractMethodItem[] res=null; 565 if (name.endsWith(")")) { 566 String name2 = name.substring(0,name.indexOf("(")); 567 //Log.trace("rtti.method","name2 : "+name2); 568 AbstractMethodItem[] meths = 569 (AbstractMethodItem[])methods.get(name2); 570 if (meths!=null) { 571 for (int i=0; i<meths.length; i++) { 572 //Log.trace("rtti.method","trying "+meths[i].getFullName()); 573 if (meths[i].getFullName().endsWith(name)) { 574 res = new AbstractMethodItem[] {meths[i]}; 575 } 576 } 577 } 578 } else { 579 res = (AbstractMethodItem[])methods.get(name); 580 } 581 if (res == null) { 582 throw new NoSuchMethodException( 583 "ClassItem.getAbstractMethods: no such method "+name+" in "+this); 584 } 585 return res; 586 } 587 588 Hashtable methodCache = new Hashtable(); 589 590 /** 591 * Gets an abstract method from its name. 592 * 593 * <p>If this method has homonym(s), then an 594 * <code>AmbiguousMethodNameException</code> is thrown. 595 * 596 * <p>An abstract method can be a static, an instance method or a 597 * constructor. 598 * 599 * @param name the name of the method to search 600 * @return the corresponding method if found 601 * @see #getMethod(String) 602 * @see #getAbstractMethods(String) 603 */ 604 public AbstractMethodItem getAbstractMethod(String name) 605 throws NoSuchMethodException, AmbiguousMethodNameException 606 { 607 AbstractMethodItem cachedMethod = 608 (AbstractMethodItem)methodCache.get(name); 609 if (cachedMethod!=null) { 610 return cachedMethod; 611 } else { 612 AbstractMethodItem[] res = getAbstractMethods(name); 613 if (res.length>1) { 614 throw new NoSuchMethodException("Ambiguous method name "+ 615 this+"."+name); 616 } 617 methodCache.put(name,res[0]); 618 return res[0]; 619 } 620 } 621 622 /** 623 * Tells wether the class contains a method. All methods of the 624 * class are examined (even the private and protected ones wich 625 * are not regisered as MethodItem) 626 * 627 * @param name name of the searched method 628 * @param paramTypes types of the parameters of the searched method 629 * @see Class#getDeclaredMethod(String,Class[]) 630 */ 631 public boolean hasMethod(String name, Class[] paramTypes) { 632 try { 633 ((Class)delegate).getDeclaredMethod(name,paramTypes); 634 return true; 635 } catch(java.lang.NoSuchMethodException e) { 636 return false; 637 } 638 } 639 640 /** 641 * Strip the arguments part from a full method name. 642 * aMethod(aType) -> aMethod 643 * aMethod -> aMethod 644 * @param name a method name 645 */ 646 static String stripArgs(String name) { 647 int index = name.indexOf("("); 648 if (index!=-1) 649 return name.substring(0,index); 650 else 651 return name; 652 } 653 654 /** 655 * Gets a set of homonym methods (excluding constructors) from 656 * their names.<p> 657 * 658 * @param name the name of the methods 659 * @return a <code>MethodItem</code> instance */ 660 661 public MethodItem[] getMethods(String name) 662 throws NoSuchMethodException 663 { 664 MethodItem[] res = null; 665 666 //Log.trace("rtti.method","getMethods("+name+")"); 667 if (name.endsWith(")")) { 668 String name2 = name.substring(0,name.indexOf("(")); 669 //Log.trace("rtti.method","name2 : "+name2); 670 MethodItem[] meths = (MethodItem[])methods.get(name2); 671 if (meths!=null) { 672 for (int i=0; i<meths.length; i++) { 673 //Log.trace("rtti.method","trying "+meths[i].getFullName()); 674 if (meths[i].getFullName().endsWith(name)) { 675 res = new MethodItem[] {meths[i]}; 676 } 677 } 678 } 679 } else { 680 res = (MethodItem[])methods.get(name); 681 } 682 683 /*ClassItem superClass=getSuperclass(); 684 685 if(res==null && superClass!=null) { 686 res=superClass.getMethods(name); 687 }*/ 688 689 if (res == null) { 690 //Log.trace("rtti.method",2,"methods = "+methods); 691 throw new NoSuchMethodException( 692 "ClassItem.getMethods: no such method "+name+" in class "+this); 693 } 694 return res; 695 } 696 697 /** 698 * Gets a method from its name. 699 * 700 * <p>If this method has homonym(s), then an 701 * <code>AmbiguousMethodNameException</code> is thrown. 702 * 703 * <p>A method can be a static or instance method but not a 704 * constructor. 705 * 706 * @param name the name of the method to search 707 * @return the corresponding method if found 708 * @see #getAbstractMethod(String) 709 * @see #getMethods(String) */ 710 711 public MethodItem getMethod(String name) 712 throws NoSuchMethodException, AmbiguousMethodNameException 713 { 714 //Log.trace("rtti.method","getMethod("+name+")"); 715 MethodItem[] res= getMethods(name); 716 if (res.length>1) { 717 throw new AmbiguousMethodNameException( 718 "Ambiguous method name "+this+"."+name+" choice is:" 719 +java.util.Arrays.asList(res)); 720 } 721 return res[0]; 722 } 723 724 /** 725 * Gets a set of arrays containing all the method items for this 726 * class item. 727 * 728 * <p>Each arrays contains the methods of the same name. 729 * 730 * @return a collection containing the methods 731 */ 732 public Collection getMethods() { 733 Collection c = methods.values(); 734 Vector res = new Vector(); 735 Iterator it = c.iterator(); 736 while (it.hasNext()) { 737 Object[] methods = (Object[]) it.next(); 738 if (methods[0] instanceof MethodItem) { 739 res.add(methods); 740 } 741 } 742 return res; 743 } 744 745 /** 746 * Gets all the method items for this class item.<p> 747 * 748 * @return a collection containing the methods 749 */ 750 public Collection getAllMethods() { 751 Collection c = methods.values(); 752 Vector res = new Vector(); 753 Iterator it = c.iterator(); 754 while (it.hasNext()) { 755 Object[] methods = (Object[]) it.next(); 756 for (int i=0; i<methods.length; i++) { 757 if ( methods[i] instanceof MethodItem && 758 !ClassRepository.isJacMethod( 759 ((MethodItem)methods[i]).getName()) ) { 760 res.add(methods[i]); 761 } 762 } 763 } 764 return res; 765 } 766 767 /** 768 * Gets all the method items for this class item.<p> 769 * 770 * @return a collection containing the methods 771 */ 772 public Collection getMixinMethods() { 773 Collection c = methods.values(); 774 Vector res = new Vector(); 775 Iterator it = c.iterator(); 776 while (it.hasNext()) { 777 Object[] methods = (Object[]) it.next(); 778 for (int i=0; i<methods.length; i++) { 779 if ( methods[i] instanceof MixinMethodItem && 780 !ClassRepository.isJacMethod( 781 ((MethodItem)methods[i]).getName()) ) { 782 res.add(methods[i]); 783 } 784 } 785 } 786 return res; 787 } 788 789 /** 790 * @return a collection of static MethodItem 791 */ 792 public Collection getAllStaticMethods() { 793 Collection c = methods.values(); 794 Vector res = new Vector(); 795 Iterator it = c.iterator(); 796 while (it.hasNext()) { 797 Object[] methods = (Object[]) it.next(); 798 //Log.trace("rtti.static","getStatic checks "+methods[0]); 799 for (int i=0; i<methods.length; i++) { 800 if ( methods[i] instanceof MethodItem && 801 !ClassRepository.isJacMethod( 802 ((MethodItem)methods[i]).getName()) && 803 ((MethodItem)methods[i]).isStatic()) { 804 //Log.trace("rtti.static","getStatic adds "+methods[0]); 805 res.add(methods[i]); 806 } 807 } 808 } 809 return res; 810 } 811 812 /** 813 * @return a collection of non static MethodItem 814 */ 815 public Collection getAllInstanceMethods() { 816 Collection c = methods.values(); 817 Vector res = new Vector(); 818 Iterator it = c.iterator(); 819 while (it.hasNext()) { 820 Object[] methods = (Object[]) it.next(); 821 for (int i=0; i<methods.length; i++) { 822 if ( methods[i] instanceof MethodItem && 823 !ClassRepository.isJacMethod( 824 ((MethodItem)methods[i]).getName()) && 825 !((MethodItem)methods[i]).isStatic()) { 826 res.add(methods[i]); 827 } 828 } 829 } 830 return res; 831 } 832 833 /** 834 * Gets all the method items that modify the state of the instances 835 * for this class item. 836 * 837 * @return a collection of MethodItem containing the modifiers 838 */ 839 public Collection getAllModifiers() { 840 Iterator it = getAllMethods().iterator(); 841 Vector modifiers = new Vector(); 842 while(it.hasNext()) { 843 MethodItem method = (MethodItem)it.next(); 844 if (method.isModifier()) { 845 modifiers.add(method); 846 } 847 } 848 return modifiers; 849 } 850 851 852 /** 853 * Gets all the method items that modify the state of the instances 854 * for this class item. 855 * 856 * @return a collection of MethodItem containing the modifiers 857 */ 858 public Collection getAllSetters() { 859 Iterator it = getAllMethods().iterator(); 860 Vector modifiers = new Vector(); 861 while(it.hasNext()) { 862 MethodItem method = (MethodItem)it.next(); 863 if (method.isSetter()) { 864 modifiers.add(method); 865 } 866 } 867 return modifiers; 868 } 869 870 /** 871 * Gets all the method items that modify a field of the instances 872 * for this class item. 873 * 874 * @return a collection of MethodItem containing the writers 875 */ 876 public Collection getAllWriters() { 877 Iterator it = getAllMethods().iterator(); 878 Vector modifiers = new Vector(); 879 while(it.hasNext()) { 880 MethodItem method = (MethodItem)it.next(); 881 if (method.hasWrittenFields()) { 882 modifiers.add(method); 883 } 884 } 885 return modifiers; 886 } 887 888 /** 889 * Gets all the getter methods of the class. 890 * 891 * @return a collection containing the getters 892 * @see MethodItem#isGetter() 893 */ 894 public Collection getAllGetters() { 895 Vector modifiers = new Vector(); 896 Iterator it = getAllMethods().iterator(); 897 while(it.hasNext()) { 898 MethodItem method = (MethodItem)it.next(); 899 if (method.isGetter()) { 900 modifiers.add(method); 901 } 902 } 903 return modifiers; 904 } 905 906 /** 907 * Gets all the method items that access the state of the instances 908 * for this class item. 909 * 910 * @return a collection containing the accessors 911 */ 912 public Collection getAllAccessors() { 913 Vector accessors = new Vector(); 914 Iterator it = getAllMethods().iterator(); 915 while(it.hasNext()) { 916 MethodItem method = (MethodItem)it.next(); 917 if (method.isAccessor()) { 918 accessors.add(method); 919 } 920 } 921 return accessors; 922 } 923 924 925 /** 926 * Gets all the method items that removes an object from a 927 * collection of this class item. 928 * 929 * @return a collection containing the modifiers 930 */ 931 public Collection getAllRemovers() { 932 Vector removers = new Vector(); 933 Iterator it = getAllMethods().iterator(); 934 while(it.hasNext()) { 935 MethodItem method = (MethodItem)it.next(); 936 if (method.isRemover()) { 937 removers.add(method); 938 } 939 } 940 return removers; 941 } 942 943 /** 944 * Gets all the method items that adds an object from a 945 * collection of this class item. 946 * 947 * @return a collection containing the modifiers 948 */ 949 public Collection getAllAdders() { 950 Collection methods = getAllMethods(); 951 Iterator it = methods.iterator(); 952 Vector adders = new Vector(); 953 954 while(it.hasNext()) { 955 MethodItem method = (MethodItem)it.next(); 956 if (method.getAddedCollections() != null && 957 method.getAddedCollections().length>0) { 958 adders.add(method); 959 } 960 } 961 return adders; 962 } 963 964 /** 965 * Gets all the constructors items for this class item.<p> 966 * 967 * @return a collection containing the constructors 968 */ 969 public Collection getConstructors() { 970 Collection c = methods.values(); 971 Vector res = new Vector(); 972 Iterator it = c.iterator(); 973 while ( it.hasNext() ) { 974 Object[] methods = (Object[]) it.next(); 975 //Log.trace("rtti.constructor","checking "+methods[0]); 976 if (methods[0] instanceof ConstructorItem) { 977 //Log.trace("rtti.constructor","adding "+methods[0]); 978 res.addAll(java.util.Arrays.asList(methods)); 979 } 980 } 981 return res; 982 } 983 984 public ConstructorItem getConstructor(Class[] parameterTypes) 985 throws NoSuchMethodException 986 { 987 Iterator i = getConstructors().iterator(); 988 while (i.hasNext()) { 989 ConstructorItem constructor = (ConstructorItem)i.next(); 990 if (java.util.Arrays.equals(constructor.getParameterTypes(), 991 parameterTypes)) { 992 return constructor; 993 } 994 } 995 return null; 996 } 997 998 /** 999 * Get a constructor with given parameter types 1000 * @param parameterTypes the types of the constructor 1001 * parameters. For instance "(java.lang.Object,java.lang.String)". 1002 */ 1003 public ConstructorItem getConstructor(String parameterTypes) { 1004 return (ConstructorItem)getAbstractMethod(getShortName()+parameterTypes); 1005 } 1006 1007 /** 1008 * Gets the constructor item of this class item that matches the 1009 * given <code>java.lang.reflect.Constructor</code>. 1010 * 1011 * @return the corresponding constructor item 1012 */ 1013 public ConstructorItem getConstructor(Constructor constructor) { 1014 //Log.trace("rtti","getConstructor("+constructor+")"); 1015 Collection constructors = getConstructors(); 1016 Iterator it = constructors.iterator(); 1017 while ( it.hasNext() ) { 1018 ConstructorItem[] current = (ConstructorItem[])it.next(); 1019 for (int i=0; i<current.length; i++) { 1020 //Log.trace("rtti","compare with "+current[i]); 1021 if (current[i].getActualConstructor().toString().equals(constructor.toString())) { 1022 return current[i]; 1023 } 1024 } 1025 } 1026 return null; 1027 } 1028 1029 /** 1030 * Creates a new instance of this class item by using the default 1031 * constructor (the one with no parameters). 1032 * 1033 * @return the newly created instance */ 1034 1035 public Object newInstance() 1036 throws InstantiationException, IllegalAccessException { 1037 return getActualClass().newInstance(); 1038 } 1039 1040 /** 1041 * Creates a new instance of this class item by using the 1042 * constructor that matches the given parameter types. 1043 * 1044 * @param types the types of the constructor arguments 1045 * @param values the arguments values 1046 * @return the newly created instance */ 1047 1048 public Object newInstance(Class[] types,Object[] values) 1049 throws InstantiationException, IllegalAccessException, 1050 java.lang.NoSuchMethodException, InvocationTargetException { 1051 Constructor c = getActualClass().getConstructor(types); 1052 return c.newInstance(values); 1053 } 1054 1055 /** 1056 * Create a new instance of the class, using the first constructor 1057 * suitable for the given parameters. 1058 * 1059 * @param parameters parameters of the constructor */ 1060 public Object newInstance(Object[] parameters) 1061 throws InstantiationException, IllegalAccessException, 1062 java.lang.NoSuchMethodException, InvocationTargetException 1063 { 1064 Iterator i = getConstructors().iterator(); 1065 ConstructorItem constructor = null; 1066 while (i.hasNext()) { 1067 constructor = (ConstructorItem)i.next(); 1068 Class[] parameterTypes = constructor.getParameterTypes(); 1069 if (parameterTypes.length==parameters.length) { 1070 int j; 1071 for (j=0; j<parameters.length; j++) { 1072 // WARNING: this test does not work for primitive typed 1073 // parameters 1074 if (!parameterTypes[j] 1075 .isAssignableFrom(parameters[j].getClass())) { 1076 break; 1077 } 1078 } 1079 if (j==parameters.length) { 1080 return constructor.newInstance(parameters); 1081 } 1082 } 1083 } 1084 throw new InstantiationException( 1085 "Could not find a suitable constructor for class "+getName()+ 1086 " with arguments "+java.util.Arrays.asList(parameters)); 1087 } 1088 1089 /** 1090 * Gets the class represented by this class item.<p> 1091 * 1092 * @return the actual class 1093 * @see #getType() 1094 */ 1095 1096 public Class getActualClass() { 1097 return (Class)delegate; 1098 } 1099 1100 ClassItem[] interfaceItems = null; 1101 /** 1102 * Returns the interfaces implemented by this class. 1103 */ 1104 public ClassItem[] getInterfaceItems() { 1105 if (interfaceItems==null) { 1106 Class[] interfaces = getActualClass().getInterfaces(); 1107 interfaceItems = new ClassItem[interfaces.length]; 1108 ClassRepository cr = ClassRepository.get(); 1109 for (int i=0; i<interfaces.length; i++) { 1110 interfaceItems[i] = cr.getClass(interfaces[i]); 1111 } 1112 } 1113 return interfaceItems; 1114 } 1115 1116 /** the super class */ 1117 ClassItem superClass; 1118 1119 /** 1120 * Set the super class of this class and updates the children of 1121 * the super class. 1122 * @param superClass the super class 1123 */ 1124 protected void setSuperClass(ClassItem superClass) { 1125 this.superClass = superClass; 1126 superClass.addChild(this); 1127 } 1128 1129 /** 1130 * Gets the superclass of this class item.<p> 1131 * 1132 * @return the superclass, null if the superclass is Object 1133 */ 1134 public ClassItem getSuperclass() { 1135 return superClass; 1136 } 1137 1138 /** A list of ClassItem whose super class is this class */ 1139 Vector children = new Vector(); 1140 1141 public Collection getChildren() { 1142 return children; 1143 } 1144 1145 public void addChild(ClassItem child) { 1146 children.add(child); 1147 } 1148 1149 /** 1150 * Tells wether the class inherits from a subclass whose name 1151 * matches a regular expression. 1152 * @param classNameRE the regular expression 1153 */ 1154 public boolean isSubClassOf(RE classNameRE) { 1155 if (classNameRE.isMatch(getName())) { 1156 return true; 1157 } else if (superClass!=null) { 1158 return superClass.isSubClassOf(classNameRE); 1159 } else { 1160 ClassItem[] interfaces = getInterfaceItems(); 1161 for (int i=0; i<interfaces.length; i++) { 1162 if (interfaces[i].isSubClassOf(classNameRE)) 1163 return true; 1164 } 1165 return false; 1166 } 1167 } 1168 1169 /** 1170 * Tells wether the class inherits from a subclass 1171 * 1172 * @param cl the regular expression 1173 */ 1174 public boolean isSubClassOf(ClassItem cl) { 1175 if (cl==this) { 1176 return true; 1177 } else if (superClass!=null) { 1178 return superClass.isSubClassOf(cl); 1179 } else { 1180 ClassItem[] interfaces = getInterfaceItems(); 1181 for (int i=0; i<interfaces.length; i++) { 1182 if (interfaces[i].isSubClassOf(cl)) 1183 return true; 1184 } 1185 return false; 1186 } 1187 } 1188 1189 public int getModifiers() { 1190 return ((Class)delegate).getModifiers(); 1191 } 1192 1193 /** 1194 * Synonym of <code>getActualClass</code>. 1195 * 1196 * @return the actual class 1197 * @see #getActualClass() 1198 */ 1199 1200 public Class getType() { 1201 return getActualClass(); 1202 } 1203 1204 /** 1205 * Add a method item to this class item. 1206 * 1207 * @param method the new method 1208 */ 1209 public void addMethod(MethodItem method) { 1210 String name = method.getName(); 1211 //Log.trace("rtti.method","addMethod: "+name+" -> "+getName()+"."+method); 1212 if (!methods.containsKey(name)) { 1213 methods.put(name, new MethodItem[] { method } ); 1214 } else { 1215 MethodItem[] meths = (MethodItem[])methods.get(name); 1216 MethodItem[] newMeths = new MethodItem[meths.length + 1]; 1217 System.arraycopy(meths, 0, newMeths, 0, meths.length); 1218 newMeths[meths.length] = method; 1219 methods.remove(name); 1220 methods.put(name, newMeths); 1221 } 1222 methodsCount++; 1223 if (method instanceof MixinMethodItem) { 1224 Iterator i = children.iterator(); 1225 while (i.hasNext()) { 1226 ClassItem subclass = (ClassItem)i.next(); 1227 subclass.addMethod(method); 1228 } 1229 } 1230 try { 1231 method.setParent(this); 1232 } catch(Exception e) { 1233 logger.error("addMethod "+method.getFullName()+ 1234 ": could not set the parent of the method",e); 1235 } 1236 } 1237 1238 Vector mixinMethods = new Vector(); 1239 1240 /** 1241 * Add a constructor item to this class item.<p> 1242 * 1243 * @param constructor the new constructor 1244 */ 1245 public void addConstructor(ConstructorItem constructor) { 1246 String name = NamingConventions.getShortConstructorName(constructor.getActualConstructor()); 1247 //Log.trace("rtti.method","addConstructor: "+name+" -> "+constructor); 1248 if ( ! methods.containsKey( name )) { 1249 methods.put( name, new ConstructorItem[] { constructor } ); 1250 } else { 1251 ConstructorItem[] constructors = 1252 (ConstructorItem[]) methods.get( name ); 1253 ConstructorItem[] newConstructors = 1254 new ConstructorItem[constructors.length + 1]; 1255 System.arraycopy(constructors, 0, newConstructors, 0, constructors.length); 1256 newConstructors[constructors.length] = constructor; 1257 methods.remove(constructor.getName()); 1258 methods.put(name, newConstructors); 1259 } 1260 constructorsCount++; 1261 try { 1262 constructor.setParent(this); 1263 } catch( Exception e ) { 1264 logger.error("addConstructor "+constructor.getFullName(),e); 1265 } 1266 } 1267 1268 /** 1269 * Tests if a method exist in this class item.<p> 1270 * 1271 * @return true if exist 1272 */ 1273 public boolean hasMethod(String name) { 1274 try { 1275 getAbstractMethod(name); 1276 return true; 1277 } catch (NoSuchMethodException e) { 1278 return false; 1279 } catch (AmbiguousMethodNameException e) { 1280 return true; 1281 } 1282 } 1283 1284 public boolean hasMethod(MethodItem method) { 1285 return method.parent==this; 1286 } 1287 1288 /** 1289 * Tests if a field exist in this class item.<p> 1290 * 1291 * @return true if exist 1292 */ 1293 1294 public boolean hasField( String name ) { 1295 buildFieldInfo(); 1296 return (name!=null) && fields.containsKey( name ); 1297 } 1298 1299 public String getName() { 1300 return ((Class)delegate).getName(); 1301 } 1302 1303 public String getShortName() { 1304 String name = getName(); 1305 int index = name.lastIndexOf('.'); 1306 return index==-1 ? name : name.substring(index+1); 1307 } 1308 1309 /** 1310 * A shortcut to invoke a method (avoid to get the method item). 1311 * 1312 * @param object the object to perform the invocation on (may be 1313 * null for a static method) 1314 * @param methodName the name of the method to invoke 1315 * @param parameters the parameters passed to the method 1316 * @see MethodItem#invoke(Object,Object[]) */ 1317 1318 public Object invoke(Object object, String methodName, Object[] parameters) 1319 throws IllegalAccessException, InvocationTargetException 1320 { 1321 return getMethod(methodName).invoke(object, parameters); 1322 } 1323 1324 /** 1325 * Tells wether this class is an inner class of some other class 1326 * 1327 * @return true if the class is an inner class 1328 */ 1329 public boolean isInner() { 1330 return getName().indexOf('$')!=-1; 1331 } 1332 1333 public boolean isAbstract() { 1334 return Modifier.isAbstract(getModifiers()); 1335 } 1336 1337 /** 1338 * Gets the class this class is an inner class of. 1339 * 1340 * @return the ClassItem this class is an inner class of, or null. 1341 */ 1342 public ClassItem getOwnerClassItem() { 1343 int index = getName().indexOf('$'); 1344 if (index==-1) { 1345 return null; 1346 } else { 1347 return ClassRepository.get().getClass(getName().substring(0,index)); 1348 } 1349 } 1350 1351 /** 1352 * Finds the collection of this class item that contains the given 1353 * object. 1354 * 1355 * @param substance the instance of the current class item to seek 1356 * in 1357 * @param object the object to find 1358 * @return the collection item, null if not found */ 1359 1360 public CollectionItem findCollectionFor(Object substance,Object object) { 1361 CollectionItem[] collections=getCollections(); 1362 for(int i=0;i<collections.length;i++) { 1363 Collection cur=collections[i].getActualCollection(substance); 1364 if(cur.contains(object)) { 1365 return collections[i]; 1366 } 1367 } 1368 return null; 1369 } 1370 1371 /** 1372 * Get an attribute by searching recursively through all super classes. 1373 */ 1374 public Object getAttribute(String name) { 1375 ClassItem cur = this; 1376 Object result = null; 1377 while (cur!=null && result==null) { 1378 result = cur.superGetAttribute(name); 1379 cur = cur.getSuperclass(); 1380 } 1381 return result; 1382 } 1383 1384 public Object superGetAttribute(String name) { 1385 return super.getAttribute(name); 1386 } 1387 1388 Vector constraints; 1389 /** 1390 * Return all field and collection items whose component type is 1391 * this class. 1392 * @return a collection of FieldItem 1393 */ 1394 public Collection getConstraints() { 1395 if (constraints==null) { 1396 constraints = new Vector(); 1397 Object[] classes = ClassRepository.get().getClasses(); 1398 for (int i=0; i<classes.length;i++) { 1399 if (classes[i] instanceof ClassItem) { 1400 ClassItem cl = (ClassItem)classes[i]; 1401 FieldItem[] fields = cl.getFields(); 1402 for (int j=0; j<fields.length; j++) { 1403 if (fields[j] instanceof CollectionItem) { 1404 if (((CollectionItem)fields[j]).getComponentType()==this) 1405 constraints.add(fields[j]); 1406 } else if (fields[j].getTypeItem()==this) { 1407 constraints.add(fields[j]); 1408 } 1409 } 1410 } 1411 } 1412 } 1413 return constraints; 1414 } 1415 1416 /** 1417 * The exception that is thrown when the accessed method has some 1418 * synonymes (methods with same names but different parameter 1419 * types). */ 1420 public static class AmbiguousMethodNameException extends RuntimeException { 1421 public AmbiguousMethodNameException(String msg) { super(msg); } 1422 public AmbiguousMethodNameException() { super(); } 1423 } 1424 }