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.InvocationTargetException; 022 import java.util.Collection; 023 import java.util.HashSet; 024 import java.util.Hashtable; 025 import java.util.Map; 026 import java.util.Set; 027 import org.apache.log4j.Logger; 028 import org.objectweb.jac.core.AspectComponent; 029 import org.objectweb.jac.core.Wrappee; 030 import java.lang.reflect.Method; 031 032 /** 033 * This class defines the rtti aspect. 034 * 035 * <p>It allows the programmer to add some runtime type informations on 036 * the classes of its applications. 037 * 038 * @see ClassItem 039 * @see MethodItem 040 * @see FieldItem 041 * @see CollectionItem 042 * 043 * @author Renaud Pawlak 044 * @author Laurent Martelli 045 */ 046 047 public class RttiAC extends AspectComponent implements RttiConf { 048 static Logger logger = Logger.getLogger("rtti"); 049 050 public static final String OPPOSITE_ROLE = "RttiAC.OPPOSITE_ROLE"; 051 public static final String FIELD_TYPE = "RttiAC.FIELD_TYPE"; 052 public static final String DYNAMIC_FIELD_TYPE = "RttiAC.DYNAMIC_FIELD_TYPE"; 053 public static final String PARAMETER_TYPES = "RttiAC.PARAMETERS_TYPES"; 054 public static final String CLONED_FIELDS = "RttiAC.CLONED_FIELDS"; 055 public static final String REPOSITORY_NAME = "RttiAC.REPOSITORY_NAME"; 056 public static final String REPOSITORY_COLLECTION = "RttiAC.REPOSITORY_COLLECTION"; 057 public static final String NULL_ALLOWED_PARAMETERS = "RttiAC.NULL_ALOWED_PARAMETERS"; 058 public static final String NULL_ALLOWED = "RttiAC.NULL_ALLOWED"; // Boolean 059 public static final String IS_INDEX = "RttiAC.IS_INDEX"; // Boolean 060 public static final String INDEXED_FIELD = "RttiAC.INDEXED_FIELD"; // FieldItem 061 public static final String AUTHORIZED_VALUES = "RttiAC.AUTHORIZED_VALUES"; 062 public static final String FORBIDDEN_VALUES = "RttiAC.FORBIDDEN_VALUES"; 063 public static final String CONSTRAINTS = "RttiAC.CONSTRAINTS"; 064 public static final String PARAMETERS_FIELDS = "RttiAC.PARAMETERS_FIELDS"; // FieldItem[] 065 066 public static final String PRIMARY_KEY = "RttiAC.PRIMARY_KEY"; 067 068 public void addWrittenFields(AbstractMethodItem method, 069 String[] writtenFields) { 070 ClassItem cl = method.getClassItem(); 071 for( int i=0; i<writtenFields.length; i++ ) { 072 FieldItem fi = cl.getField(writtenFields[i]); 073 method.addWrittenField(fi); 074 } 075 } 076 077 public void declareCalculatedField(ClassItem cl, String fieldName, 078 String getterName) 079 { 080 MethodItem getter = cl.getMethod(getterName); 081 FieldItem calculatedField; 082 if (RttiAC.isCollectionType(getter.getType())) 083 calculatedField = new CollectionItem(fieldName,getter,cl); 084 else 085 calculatedField = new FieldItem(fieldName,getter,cl); 086 cl.addField(calculatedField); 087 FieldItem[] fields = getter.getAccessedFields(); 088 for (int i=0;i<fields.length;i++) { 089 if (fields[i]!=calculatedField) { 090 logger.debug(calculatedField.getLongName()+" depends on "+fields[i]); 091 fields[i].addDependentField(calculatedField); 092 } 093 } 094 } 095 096 public void setSetter(FieldItem field, String setterName) { 097 MethodItem setter = field.getClassItem().getMethod(setterName); 098 setter.setSetField(field); 099 field.setSetter(setter); 100 } 101 102 public void setGetter(FieldItem field, String getterName) { 103 MethodItem getter = field.getClassItem().getMethod(getterName); 104 getter.setReturnedField(field); 105 field.setGetter(getter); 106 } 107 108 public void addDependentField(FieldItem field, String dependentField) { 109 field.getClassItem().getField(dependentField).addDependentField(field); 110 } 111 112 public void addFieldDependency(FieldItem field, FieldItem dependentField) { 113 field.addDependentField(dependentField); 114 } 115 116 public void addAdder(CollectionItem collection, String methodName) { 117 MethodItem method = collection.getClassItem().getMethod(methodName); 118 collection.addAddingMethod(method); 119 method.addAddedCollection(collection); 120 } 121 122 public void setAdder(CollectionItem collection, String methodName) { 123 MethodItem method = collection.getClassItem().getMethod(methodName); 124 collection.setAdder(method); 125 method.addAddedCollection(collection); 126 } 127 128 public void addRemover(CollectionItem collection,String methodName) { 129 MethodItem method = collection.getClassItem().getMethod(methodName); 130 collection.addRemovingMethod(method); 131 method.addRemovedCollection(collection); 132 } 133 134 public void setRemover(CollectionItem collection,String methodName) { 135 MethodItem method = collection.getClassItem().getMethod(methodName); 136 collection.setRemover(method); 137 method.addRemovedCollection(collection); 138 } 139 140 public void addAccessedFields(MethodItem method, 141 String[] accessedFields) { 142 ClassItem cl = method.getClassItem(); 143 for(int i=0; i<accessedFields.length; i++) { 144 FieldItem fi = cl.getField(accessedFields[i]); 145 method.addAccessedField(fi); 146 } 147 } 148 149 public void setFieldType(FieldItem field, String type) 150 { 151 Object cl = ClassRepository.get().getObject(type); 152 153 if (cl == null) 154 throw new RuntimeException("no such type "+type); 155 156 field.setAttribute(FIELD_TYPE, cl); 157 } 158 159 public void setDynamicFieldType(FieldItem field, MethodItem method) { 160 if (!method.isStatic()) { 161 error("Method must be static"); 162 } if (!(method.getType()==String.class 163 || method.getType()==Object.class 164 || MetaItem.class.isAssignableFrom(method.getType()))) { 165 error("Method must return a String, a MetaItem or an Object"); 166 } else { 167 field.setAttribute(DYNAMIC_FIELD_TYPE, method); 168 } 169 } 170 171 public void setComponentType(CollectionItem collection, String type) { 172 collection.setComponentType(currentImports.getClass(type)); 173 } 174 175 /** 176 * Gets the type of a field. May return a ClassItem, a 177 * VirtualClassItem or a MethodItem. 178 * @param field a field 179 */ 180 public static MetaItem getFieldType(FieldItem field) { 181 return (MetaItem)field.getAttribute(FIELD_TYPE); 182 } 183 184 /** 185 * Gets the type of a field for a given object. May return a 186 * ClassItem, a VirtualClassItem. 187 * @param field a field 188 * @param substance the object holding the field 189 */ 190 public static MetaItem getFieldType(FieldItem field, Object substance) { 191 MethodItem dynType = (MethodItem)field.getAttribute(DYNAMIC_FIELD_TYPE); 192 if (dynType!=null) { 193 Object type = dynType.invokeStatic(new Object[] {field,substance}); 194 if (type instanceof String) 195 return cr.getVirtualClass((String)type); 196 else 197 return (MetaItem)type; 198 } else { 199 return getFieldType(field); 200 } 201 } 202 203 public void setParametersType(AbstractMethodItem method, 204 String[] types) 205 { 206 ClassRepository cr = ClassRepository.get(); 207 MetaItem[] metaItems = new MetaItem[types.length]; 208 for (int i=0; i<types.length;i++) { 209 metaItems[i] = (MetaItem)cr.getObject(types[i]); 210 } 211 method.setAttribute(PARAMETER_TYPES,metaItems); 212 } 213 214 public void newVirtualClass(String className, ClassItem actualType) 215 { 216 logger.info("newVirtualClass("+className+")"); 217 ClassRepository.get().register(className, 218 new VirtualClassItem(className,actualType)); 219 } 220 221 public void defineRepository(ClassItem type, 222 String repositoryName, 223 CollectionItem repositoryCollection) 224 { 225 type.setAttribute(REPOSITORY_NAME, repositoryName); 226 type.setAttribute(REPOSITORY_COLLECTION, repositoryCollection); 227 } 228 229 public void setClonedFields(String className, String[] fields) { 230 ClassRepository.get().getClass(className) 231 .setAttribute(CLONED_FIELDS,fields); 232 } 233 234 public void whenClone(Wrappee cloned, Wrappee clone) { 235 236 ClassItem cli = ClassRepository.get().getClass(cloned.getClass()); 237 String[] clonedFields = (String[])cli.getAttribute(CLONED_FIELDS); 238 239 if( clonedFields != null ) { 240 for( int i=0; i<clonedFields.length; i++ ) { 241 FieldItem fi = cli.getField( clonedFields[i] ); 242 logger.debug("cloning field "+clonedFields[i]); 243 /* 244 try { 245 fi.set(clone,((Wrappee)fi.get(cloned)).clone()); 246 } catch( Exception e ) {} 247 */ 248 } 249 } 250 } 251 252 public void ignoreFields(String packageExpr) { 253 logger.info("ignoreFields"+packageExpr); 254 ClassRepository.get().ignoreFields(packageExpr); 255 } 256 257 void setItemClass(MetaItem item, String className, ClassItem actualType) { 258 MetaItem virtualClass; 259 ClassRepository cr = ClassRepository.get(); 260 try { 261 virtualClass = cr.getVirtualClass(className); 262 } catch (NoSuchClassException e) { 263 virtualClass = new VirtualClassItem(className,actualType); 264 cr.register(className,virtualClass); 265 } 266 item.setItemClass(virtualClass); 267 } 268 269 public void setClass(MemberItem member, String className) { 270 setItemClass(member,className,member.getTypeItem()); 271 } 272 273 public void setClass(ClassItem cli, String className) { 274 setItemClass(cli,className,cli); 275 } 276 277 /* 278 public void introduce(ClassItem target,ClassItem roleType, 279 String memeberType,String memberName) { 280 } 281 */ 282 283 public void setParametersFields(AbstractMethodItem method, 284 FieldItem[] fields) { 285 method.setAttribute(PARAMETERS_FIELDS, fields); 286 } 287 288 public void setNullAllowed(FieldItem field) { 289 setNullAllowed(field,true); 290 } 291 292 public void setNullAllowed(FieldItem field, boolean allowed) { 293 field.setAttribute(NULL_ALLOWED, allowed ? Boolean.TRUE : Boolean.FALSE); 294 logger.info("setNullAllowed("+field.getName()+")"); 295 } 296 297 public static boolean isNullAllowed(FieldItem field) { 298 Boolean result = (Boolean) field.getAttribute(NULL_ALLOWED); 299 if (result == null) 300 return false; 301 return result.booleanValue(); 302 } 303 304 public void setNullAllowedParameters(AbstractMethodItem method, 305 boolean[] nulls) { 306 method.setAttribute(NULL_ALLOWED_PARAMETERS, nulls); 307 } 308 309 public static boolean isNullAllowedParameter(AbstractMethodItem method, 310 int i) { 311 //System.out.println("is null allowed "+method+" "+i); 312 boolean[] nulls=(boolean[])method.getAttribute(NULL_ALLOWED_PARAMETERS); 313 if(nulls==null) return false; 314 //System.out.println("=> "+nulls[i]); 315 return nulls[i]; 316 } 317 318 public void setAggregation(FieldItem field, boolean isAggregation) { 319 field.setAggregation(isAggregation); 320 } 321 322 public void setIndexedField(CollectionItem collection, 323 FieldItem indexedField) { 324 setIsIndex(collection,true); 325 collection.setAttribute(INDEXED_FIELD, indexedField); 326 } 327 328 public void setIsIndex(CollectionItem collection, 329 boolean isIndex) { 330 collection.setAttribute(IS_INDEX, isIndex?Boolean.TRUE:Boolean.FALSE); 331 } 332 333 public static FieldItem getIndexFied(CollectionItem collection) { 334 return (FieldItem)collection.getAttribute(INDEXED_FIELD); 335 } 336 337 public static boolean isIndex(CollectionItem collection) { 338 Boolean result = (Boolean) collection.getAttribute(IS_INDEX); 339 if (result == null) 340 return false; 341 return result.booleanValue(); 342 } 343 344 public String[] getDefaultConfigs() { 345 return new String[] {"org/objectweb/jac/core/rtti/rtti.acc", 346 "org/objectweb/jac/aspects/user/rtti.acc"}; 347 } 348 349 public void definePrimaryKey(CollectionItem collection, 350 String[] fields) { 351 collection.setAttribute(PRIMARY_KEY, fields); 352 } 353 354 /** 355 * Tells wether a given type represents a collection 356 */ 357 public static boolean isCollectionType(Class type) { 358 return Collection.class.isAssignableFrom(type) || 359 Map.class.isAssignableFrom(type) || 360 (type.isArray() && type.getComponentType()!=byte.class); 361 } 362 363 static Hashtable allowedCasts = new Hashtable(); 364 public void addAllowedCast(ClassItem src, ClassItem dest) { 365 Set casts = (Set)allowedCasts.get(src); 366 if (casts == null) { 367 casts = new HashSet(); 368 allowedCasts.put(src,casts); 369 } 370 casts.add(dest); 371 } 372 373 public static boolean isCastAllowed(ClassItem src, ClassItem dest) { 374 Set casts = (Set)allowedCasts.get(src); 375 return casts!=null && casts.contains(dest); 376 } 377 378 public static boolean isCastAllowed(Class src, Class dest) { 379 ClassRepository cr = ClassRepository.get(); 380 return isCastAllowed(cr.getClass(src),cr.getClass(dest)); 381 } 382 383 HashSet classesWithAssociations = new HashSet(); 384 public Set getClassesWithAssociations() { 385 return classesWithAssociations; 386 } 387 388 public void setOppositeRole(FieldItem field, FieldItem oppositeRole) { 389 field.setOppositeRole(oppositeRole); 390 classesWithAssociations.add(field.getClassItem()); 391 } 392 393 public void declareAssociation(FieldItem roleA, FieldItem roleB) { 394 roleA.setOppositeRole(roleB); 395 roleB.setOppositeRole(roleA); 396 classesWithAssociations.add(roleA.getClassItem()); 397 classesWithAssociations.add(roleB.getClassItem()); 398 } 399 400 /** 401 * Tries to convert an object into a given type 402 * 403 * <p>If type is 404 * String, toString() is called on value. If type Integer or Long 405 * (or int or long), and the value is numeric type 406 * (float,Float,double or Double) an Integer or Long is 407 * returned. Otherwise, isCastAllowed() is called, and if it 408 * returns true, we try to invoke a constructor take value as a 409 * parameter.</p> 410 * 411 * @param value the value to convert 412 * @param type the type to convert the value into 413 * @return an instance of type built from value, or value 414 * @see #isCastAllowed(Class,Class) 415 */ 416 public static Object convert(Object value, Class type) 417 throws InstantiationException, IllegalAccessException, 418 InvocationTargetException, java.lang.NoSuchMethodException 419 { 420 Class valueType = value.getClass(); 421 if ((type==int.class || type==Integer.class) 422 && (valueType==float.class || valueType==Float.class 423 || valueType==double.class || valueType==Double.class)) { 424 return new Integer(((Number)value).intValue()); 425 } else if ((type==long.class || type==Long.class) 426 && (valueType==float.class || valueType==Float.class 427 || valueType==double.class || valueType==Double.class)) { 428 return new Long(((Number)value).longValue()); 429 } else if (type==String.class) { 430 return value.toString(); 431 } else { 432 if (RttiAC.isCastAllowed(valueType,type)) { 433 return 434 type.getConstructor(new Class[] {valueType}) 435 .newInstance(new Object[] {value}); 436 } 437 } 438 return value; 439 } 440 441 public void addMixinMethod(ClassItem cli, MethodItem method) 442 throws InvalidDelegateException 443 { 444 cli.addMethod(new MixinMethodItem((Method)method.getDelegate(),cli)); 445 } 446 447 }