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.*; 022 import java.util.Arrays; 023 import java.util.Vector; 024 025 /** 026 * This class defines a meta item that corresponds to the 027 * <code>java.lang.reflect.Method</code> and to the 028 * <code>java.lang.reflect.Constructor</code> meta elements. 029 * 030 * <p>It appears that methods and contructors can be seen as 031 * semantically similar (a constructor is nothing else that a special 032 * static method that returns an object that is of the owner class 033 * type). Thus, we decide to wrap those two meta item into a unique 034 * one (on contrary to the Java choice)<p> 035 * 036 * @author Laurent Martelli 037 * @author Renaud Pawlak 038 */ 039 040 public abstract class AbstractMethodItem extends MemberItem { 041 042 /** 043 * Default contructor to create a new abstract method item.<p> 044 * 045 * @param delegate the <code>java.lang.reflect</code> actual 046 * meta item */ 047 048 public AbstractMethodItem(Object delegate, ClassItem parent) 049 throws InvalidDelegateException 050 { 051 super(delegate,parent); 052 isStatic = Modifier.isStatic(((Member)delegate).getModifiers()); 053 } 054 055 /** 056 * If the field does not have a value for the request attribute, 057 * tries on the superclass. 058 */ 059 public final Object getAttribute(String name) { 060 Object value = super.getAttribute(name); 061 if (value==null) { 062 ClassItem parent = ((ClassItem)getParent()).getSuperclass(); 063 if (parent!=null) { 064 try { 065 AbstractMethodItem parentMethod = parent.getAbstractMethod(getFullName()); 066 value = parentMethod.getAttribute(name); 067 } catch (NoSuchMethodException e) { 068 ClassItem[] interfaces = parent.getInterfaceItems(); 069 for (int i=0; i<interfaces.length && value==null;i++) { 070 if (parent.hasMethod(getFullName())) { 071 AbstractMethodItem parentMethod = parent.getAbstractMethod(getFullName()); 072 value = parentMethod.getAttribute(name); 073 } 074 } 075 } 076 } 077 } 078 return value; 079 } 080 081 /** 082 * Gets the parameter types of this abstract method item.<p> 083 * 084 * @return the actual method parameter types 085 */ 086 public abstract Class[] getParameterTypes(); 087 088 public void setParameter(Object[] params, int i, Object value) { 089 params[i] = value; 090 } 091 092 public Object getParameter(Object[] params, int i) { 093 return params[i]; 094 } 095 096 /** 097 * Get the ClassItem of the type of a parameter of the method. 098 * @param n the number of the parameter 099 * @return the ClassItem of the type of the parameter 100 */ 101 public ClassItem getParameterTypeItem(int n) { 102 return ClassRepository.get().getClass(getParameterTypes()[n]); 103 } 104 105 /** 106 * Gets the number of parameters 107 * 108 * @return the number of parameters. 109 */ 110 public int getParameterCount() { 111 return getParameterTypes().length; 112 } 113 114 boolean isStatic = false; 115 116 public final boolean isStatic() { 117 return isStatic; 118 } 119 120 private String fullName; 121 /** 122 * Return the full method name, ie with parameter types. 123 * 124 * @return The full method name. For instance myMethod(java.lang.String,int) 125 * @see #getCompactFullName() 126 */ 127 public String getFullName() { 128 if (fullName==null) { 129 fullName = getFullName(getName(),getParameterTypes()); 130 } 131 return fullName; 132 } 133 134 String realFullName; 135 /** 136 * Return the full method name, ie with parameter types. 137 * 138 * @return The full method name. For instance myMethod(java.lang.String,int) 139 * @see #getCompactFullName() 140 */ 141 public String getRealFullName() { 142 if (realFullName==null) { 143 realFullName = getFullName(getName(),((Method)delegate).getParameterTypes()); 144 } 145 return realFullName; 146 } 147 148 public static String getFullName(String name, Class[] pts) { 149 StringBuffer ret = new StringBuffer(name.length()+2+pts.length*15); 150 ret.append(name); 151 ret.append('('); 152 for (int i=0; i<pts.length; i++) { 153 ret.append(NamingConventions.getStandardClassName(pts[i])); 154 if (i < pts.length-1) 155 ret.append(','); 156 } 157 ret.append(')'); 158 return ret.toString(); 159 } 160 161 /** 162 * Return the full method name, ie with short parameter types. 163 * 164 * @return The full method name. For instance "myMethod(String,int)" 165 * @see #getFullName() 166 */ 167 public String getCompactFullName() { 168 //.substring(0, getName().lastIndexOf('.')) 169 Class[] pts = getParameterTypes(); 170 StringBuffer ret = new StringBuffer(getName().length()+2+pts.length*15); 171 ret.append(getName()); 172 ret.append('('); 173 for (int i=0; i<pts.length; i++) { 174 ret.append(NamingConventions.getShortClassName(pts[i])); 175 if (i < pts.length-1) 176 ret.append(','); 177 } 178 ret.append(')'); 179 return ret.toString(); 180 } 181 182 private String longName; 183 public String getLongName() { 184 if (longName==null) 185 longName = parent.getName()+"."+getFullName(); 186 return longName; 187 } 188 189 public Object invoke(Object object, Object[] parameters) 190 { 191 if (true) { 192 throw new RuntimeException( 193 "wrong invocation on an abstract method "+this); 194 } 195 return null; 196 } 197 198 AbstractMethodItem concreteMethod; 199 200 /** 201 * Returns the method item who really holds the byte code. 202 */ 203 public AbstractMethodItem getConcreteMethod() { 204 if (concreteMethod==null) { 205 Member m = (Member)delegate; 206 if (parent.getDelegate() == m.getDeclaringClass()) { 207 return this; 208 } 209 concreteMethod = ClassRepository.get().getClass(m.getDeclaringClass()). 210 getAbstractMethod(getRealFullName()); 211 } 212 return concreteMethod; 213 } 214 215 /** 216 * Returns the owning class of this method. 217 */ 218 public ClassItem getOwningClass() { 219 return (ClassItem)parent; 220 } 221 222 public abstract boolean isAdder(); 223 public abstract CollectionItem[] getAddedCollections(); 224 /** 225 * Get the value of the collection that is added by this 226 * method (the method is the unique adder of the collection).<p> 227 * 228 * @return value of addedCollection. 229 */ 230 public final CollectionItem getAddedCollection() { 231 CollectionItem[] colls = getAddedCollections(); 232 return ((colls != null) ? colls[0] : null); 233 } 234 235 public abstract boolean isRemover(); 236 public abstract CollectionItem[] getRemovedCollections(); 237 238 public abstract boolean isAccessor(); 239 public abstract boolean isWriter(); 240 public abstract boolean isGetter(); 241 public abstract boolean isSetter(); 242 243 public abstract boolean isCollectionGetter(); 244 public abstract boolean isCollectionAccessor(); 245 public abstract boolean isCollectionSetter(); 246 247 public abstract boolean isFieldGetter(); 248 public abstract boolean isFieldSetter(); 249 250 public abstract boolean isReferenceGetter(); 251 public abstract boolean isReferenceSetter(); 252 public abstract boolean isReferenceAccessor(); 253 254 public abstract FieldItem getSetField(); 255 256 /** Stores the fields that are written by this method.<p> */ 257 FieldItem[] writtenFields = null; 258 259 /** 260 * Gets the value of the fields that are written by this method.<p> 261 * 262 * @return value of writtenFields. */ 263 264 public final FieldItem[] getWrittenFields() { 265 ((ClassItem)parent).buildFieldInfo(); 266 return writtenFields; 267 } 268 269 public final boolean hasWrittenFields() { 270 ((ClassItem)parent).buildFieldInfo(); 271 return writtenFields!=null && writtenFields.length>0; 272 } 273 274 /** 275 * Sets the value of the fields that are written by this method.<p> 276 * 277 * @param writtenFields value to assign to writtenFields 278 * @see #addWrittenField(FieldItem) 279 */ 280 281 public final void setWrittenFields(FieldItem[] writtenFields) { 282 this.writtenFields = writtenFields; 283 } 284 285 /** 286 * Adds a new written field for this method.<p> 287 * 288 * @param writtenField the field to add 289 * @see #setWrittenFields(FieldItem[]) 290 * @see #removeWrittenField(FieldItem) 291 */ 292 public final void addWrittenField(FieldItem writtenField) { 293 if (writtenFields == null) { 294 writtenFields = new FieldItem[] { writtenField }; 295 } else { 296 FieldItem[] tmp = new FieldItem[writtenFields.length + 1]; 297 System.arraycopy(writtenFields, 0, tmp, 0, writtenFields.length); 298 tmp[writtenFields.length] = writtenField; 299 writtenFields = tmp; 300 } 301 } 302 303 /** 304 * Removes a new written field for this method.<p> 305 * 306 * @param field the field to remove 307 * @see #addWrittenField(FieldItem) 308 */ 309 public final void removeWrittenField(FieldItem field) { 310 if (writtenFields != null) { 311 Vector v = new Vector(Arrays.asList(writtenFields)); 312 v.remove(field); 313 writtenFields = new FieldItem[v.size()]; 314 System.arraycopy(v.toArray(),0,writtenFields,0,v.size()); 315 } 316 } 317 318 /** Stores the number of references read by this method */ 319 int numAccessedReferences = 0; 320 321 /** Stores the number of collections read by this method */ 322 int numAccessedCollections = 0; 323 324 /** Stores the collections that are modified by this method.<p> */ 325 CollectionItem[] modifiedCollections = null; 326 327 /** 328 * Gets the value of the collections that are modified by this 329 * method.<p> 330 * 331 * @return value of removedCollections. */ 332 333 public final CollectionItem[] getModifiedCollections() { 334 ((ClassItem)parent).buildFieldInfo(); 335 return modifiedCollections; 336 } 337 338 /** 339 * Returns true if the method has at least one modified collection 340 */ 341 342 public final boolean hasModifiedCollections() { 343 ((ClassItem)parent).buildFieldInfo(); 344 return modifiedCollections!=null && modifiedCollections.length>0; 345 } 346 347 /** 348 * Adds a new modified collection for this method.<p> 349 * 350 * @param modifiedCollection the collection to add 351 */ 352 353 public final void addModifiedCollection(CollectionItem modifiedCollection) { 354 if ( modifiedCollections == null ) { 355 modifiedCollections = new CollectionItem[] { modifiedCollection }; 356 } else { 357 CollectionItem[] tmp = new CollectionItem[modifiedCollections.length + 1]; 358 System.arraycopy( modifiedCollections, 0, tmp, 0, modifiedCollections.length ); 359 tmp[modifiedCollections.length] = modifiedCollection; 360 modifiedCollections = tmp; 361 } 362 } 363 364 /** Stores the fields that are read by this method.<p> */ 365 FieldItem[] accessedFields = null; 366 367 /** 368 * Gets the value of the fields that are written by this method.<p> 369 * 370 * @return value of accessedFields. */ 371 372 public final FieldItem[] getAccessedFields() { 373 ((ClassItem)parent).buildFieldInfo(); 374 return accessedFields; 375 } 376 377 /** 378 * Sets the value of the fields that are read by this method.<p> 379 * 380 * @param accessedFields value to assign to accessedFields 381 * @see #addAccessedField(FieldItem) 382 */ 383 384 public void setAccessedFields(FieldItem[] accessedFields) { 385 this.accessedFields = accessedFields; 386 numAccessedReferences = 0; 387 numAccessedCollections = 0; 388 if (accessedFields!=null) { 389 for(int i=0; i<accessedFields.length; i++) { 390 if (accessedFields[i].isReference()) 391 numAccessedReferences++; 392 else if (accessedFields[i] instanceof CollectionItem) 393 numAccessedCollections++; 394 } 395 } 396 } 397 398 /** 399 * Adds a new accessed field for this method.<p> 400 * 401 * @param accessedField the field to add 402 * @see #setAccessedFields(FieldItem[]) 403 * @see #removeAccessedField(FieldItem) 404 */ 405 406 public void addAccessedField(FieldItem accessedField) { 407 if (accessedField.isReference()) 408 numAccessedReferences++; 409 else if (accessedField instanceof CollectionItem) 410 numAccessedCollections++; 411 if (accessedFields == null) { 412 accessedFields = new FieldItem[] { accessedField }; 413 } else { 414 FieldItem[] tmp = new FieldItem[accessedFields.length + 1]; 415 System.arraycopy( accessedFields, 0, tmp, 0, accessedFields.length ); 416 tmp[accessedFields.length] = accessedField; 417 accessedFields = tmp; 418 } 419 } 420 421 /** 422 * Removes an accessed field for this method.<p> 423 * 424 * @param field the field to remove 425 * @see #addAccessedField(FieldItem) 426 */ 427 public void removeAccessedField(FieldItem field) { 428 if (accessedFields != null) { 429 Vector v = new Vector(Arrays.asList(accessedFields)); 430 if (v.remove(field)) { 431 if (field.isReference()) 432 numAccessedReferences--; 433 else if (field instanceof CollectionItem) 434 numAccessedCollections--; 435 accessedFields = new FieldItem[v.size()]; 436 System.arraycopy(v.toArray(),0,accessedFields,0,v.size()); 437 } 438 } 439 } 440 441 /** 442 * Gets the value of the fields that are written by this method.<p> 443 * 444 * @return value of accessedFields. 445 */ 446 public final FieldItem[] getAccessedReferences() { 447 ((ClassItem)parent).buildFieldInfo(); 448 FieldItem[] refs = new FieldItem[numAccessedReferences]; 449 int j=0; 450 for (int i=0; i<accessedFields.length; i++) { 451 if (accessedFields[i].isReference()) 452 refs[j++] = accessedFields[i]; 453 } 454 return refs; 455 } 456 457 public final CollectionItem[] getAccessedCollections() { 458 ((ClassItem)parent).buildFieldInfo(); 459 CollectionItem[] colls = new CollectionItem[numAccessedCollections]; 460 int j=0; 461 for (int i=0; i<accessedFields.length; i++) { 462 if (accessedFields[i] instanceof CollectionItem) 463 colls[j++] = (CollectionItem)accessedFields[i]; 464 } 465 return colls; 466 } 467 468 /** Tells wether the method modifies the state of the object */ 469 public final boolean isModifier() { 470 ((ClassItem)parent).buildFieldInfo(); 471 return (writtenFields!=null && writtenFields.length>0) 472 || (modifiedCollections!=null && modifiedCollections.length>0) 473 || isAdder() || isRemover(); 474 } 475 476 public String toString() { 477 return getLongName(); 478 } 479 }