1 15 package org.apache.tapestry.enhance; 16 17 import java.beans.BeanInfo ; 18 import java.beans.IntrospectionException ; 19 import java.beans.Introspector ; 20 import java.beans.PropertyDescriptor ; 21 import java.lang.reflect.Constructor ; 22 import java.lang.reflect.Method ; 23 import java.lang.reflect.Modifier ; 24 import java.util.ArrayList ; 25 import java.util.HashMap ; 26 import java.util.HashSet ; 27 import java.util.IdentityHashMap ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.Set ; 32 33 import org.apache.hivemind.ApplicationRuntimeException; 34 import org.apache.hivemind.ClassResolver; 35 import org.apache.hivemind.HiveMind; 36 import org.apache.hivemind.service.BodyBuilder; 37 import org.apache.hivemind.service.ClassFab; 38 import org.apache.hivemind.service.ClassFactory; 39 import org.apache.hivemind.service.MethodSignature; 40 import org.apache.hivemind.util.Defense; 41 import org.apache.hivemind.util.ToStringBuilder; 42 import org.apache.tapestry.services.ComponentConstructor; 43 import org.apache.tapestry.spec.IComponentSpecification; 44 import org.apache.tapestry.util.IdAllocator; 45 46 54 public class EnhancementOperationImpl implements EnhancementOperation 55 { 56 private ClassResolver _resolver; 57 58 private IComponentSpecification _specification; 59 60 private Class _baseClass; 61 62 private ClassFab _classFab; 63 64 private final Set _claimedProperties = new HashSet (); 65 66 private final JavaClassMapping _javaClassMapping = new JavaClassMapping(); 67 68 private final List _constructorTypes = new ArrayList (); 69 70 private final List _constructorArguments = new ArrayList (); 71 72 private final Map _finalFields = new IdentityHashMap (); 73 74 77 78 private Set _addedInterfaces = new HashSet (); 79 80 83 84 private Map _incompleteMethods = new HashMap (); 85 86 89 90 private Map _properties = new HashMap (); 91 92 95 96 private BodyBuilder _constructorBuilder; 97 98 101 102 private final IdAllocator _idAllocator = new IdAllocator(); 103 104 public EnhancementOperationImpl(ClassResolver classResolver, 105 IComponentSpecification specification, Class baseClass, ClassFactory classFactory) 106 { 107 Defense.notNull(classResolver, "classResolver"); 108 Defense.notNull(specification, "specification"); 109 Defense.notNull(baseClass, "baseClass"); 110 Defense.notNull(classFactory, "classFactory"); 111 112 _resolver = classResolver; 113 _specification = specification; 114 _baseClass = baseClass; 115 116 introspectBaseClass(); 117 118 String name = newClassName(); 119 120 _classFab = classFactory.newClass(name, _baseClass); 121 } 122 123 public String toString() 124 { 125 ToStringBuilder builder = new ToStringBuilder(this); 126 127 builder.append("baseClass", _baseClass.getName()); 128 builder.append("claimedProperties", _claimedProperties); 129 builder.append("classFab", _classFab); 130 131 return builder.toString(); 132 } 133 134 143 private void introspectBaseClass() 144 { 145 try 146 { 147 synchronized (HiveMind.INTROSPECTOR_MUTEX) 148 { 149 addPropertiesDeclaredInBaseClass(); 150 } 151 } 152 catch (IntrospectionException ex) 153 { 154 throw new ApplicationRuntimeException(EnhanceMessages.unabelToIntrospectClass( 155 _baseClass, 156 ex), ex); 157 } 158 159 } 160 161 private void addPropertiesDeclaredInBaseClass() throws IntrospectionException 162 { 163 Class introspectClass = _baseClass; 164 165 addPropertiesDeclaredInClass(introspectClass); 166 167 List interfaceQueue = new ArrayList (); 168 169 while (introspectClass != null) 170 { 171 addInterfacesToQueue(introspectClass, interfaceQueue); 172 173 introspectClass = introspectClass.getSuperclass(); 174 } 175 176 while (!interfaceQueue.isEmpty()) 177 { 178 Class interfaceClass = (Class ) interfaceQueue.remove(0); 179 180 addPropertiesDeclaredInClass(interfaceClass); 181 182 addInterfacesToQueue(interfaceClass, interfaceQueue); 183 } 184 } 185 186 private void addInterfacesToQueue(Class introspectClass, List interfaceQueue) 187 { 188 Class [] interfaces = introspectClass.getInterfaces(); 189 190 for (int i = 0; i < interfaces.length; i++) 191 interfaceQueue.add(interfaces[i]); 192 } 193 194 private void addPropertiesDeclaredInClass(Class introspectClass) throws IntrospectionException 195 { 196 BeanInfo bi = Introspector.getBeanInfo(introspectClass); 197 198 PropertyDescriptor [] pds = bi.getPropertyDescriptors(); 199 200 for (int i = 0; i < pds.length; i++) 201 { 202 PropertyDescriptor pd = pds[i]; 203 204 String name = pd.getName(); 205 206 if (!_properties.containsKey(name)) 207 _properties.put(name, pd); 208 } 209 } 210 211 215 216 EnhancementOperationImpl() 217 { 218 } 219 220 public void claimProperty(String propertyName) 221 { 222 Defense.notNull(propertyName, "propertyName"); 223 224 if (_claimedProperties.contains(propertyName)) 225 throw new ApplicationRuntimeException(EnhanceMessages.claimedProperty(propertyName)); 226 227 _claimedProperties.add(propertyName); 228 } 229 230 public void addField(String name, Class type) 231 { 232 _classFab.addField(name, type); 233 } 234 235 public String addInjectedField(String fieldName, Class fieldType, Object value) 236 { 237 Defense.notNull(fieldName, "fieldName"); 238 Defense.notNull(fieldType, "fieldType"); 239 Defense.notNull(value, "value"); 240 241 String existing = (String ) _finalFields.get(value); 242 243 245 if (existing != null) 246 return existing; 247 248 250 253 String uniqueName = _idAllocator.allocateId(fieldName); 254 255 258 _classFab.addField(uniqueName, fieldType); 259 260 int parameterIndex = addConstructorParameter(fieldType, value); 261 262 constructorBuilder().addln("{0} = ${1};", uniqueName, Integer.toString(parameterIndex)); 263 264 266 _finalFields.put(value, uniqueName); 267 268 return uniqueName; 269 } 270 271 public Class convertTypeName(String type) 272 { 273 Defense.notNull(type, "type"); 274 275 Class result = _javaClassMapping.getType(type); 276 277 if (result == null) 278 { 279 result = _resolver.findClass(type); 280 281 _javaClassMapping.recordType(type, result); 282 } 283 284 return result; 285 } 286 287 public Class getPropertyType(String name) 288 { 289 Defense.notNull(name, "name"); 290 291 PropertyDescriptor pd = getPropertyDescriptor(name); 292 293 return pd == null ? null : pd.getPropertyType(); 294 } 295 296 public void validateProperty(String name, Class expectedType) 297 { 298 Defense.notNull(name, "name"); 299 Defense.notNull(expectedType, "expectedType"); 300 301 PropertyDescriptor pd = getPropertyDescriptor(name); 302 303 if (pd == null) 304 return; 305 306 Class propertyType = pd.getPropertyType(); 307 308 if (propertyType.equals(expectedType)) 309 return; 310 311 throw new ApplicationRuntimeException(EnhanceMessages.propertyTypeMismatch( 312 _baseClass, 313 name, 314 propertyType, 315 expectedType)); 316 } 317 318 private PropertyDescriptor getPropertyDescriptor(String name) 319 { 320 return (PropertyDescriptor ) _properties.get(name); 321 } 322 323 public String getAccessorMethodName(String propertyName) 324 { 325 Defense.notNull(propertyName, "propertyName"); 326 327 PropertyDescriptor pd = getPropertyDescriptor(propertyName); 328 329 if (pd != null && pd.getReadMethod() != null) 330 return pd.getReadMethod().getName(); 331 332 return EnhanceUtils.createAccessorMethodName(propertyName); 333 } 334 335 public void addMethod(int modifier, MethodSignature sig, String methodBody) 336 { 337 _classFab.addMethod(modifier, sig, methodBody); 338 } 339 340 public Class getBaseClass() 341 { 342 return _baseClass; 343 } 344 345 public String getClassReference(Class clazz) 346 { 347 Defense.notNull(clazz, "clazz"); 348 349 String result = (String ) _finalFields.get(clazz); 350 351 if (result == null) 352 result = addClassReference(clazz); 353 354 return result; 355 } 356 357 private String addClassReference(Class clazz) 358 { 359 StringBuffer buffer = new StringBuffer ("_class$"); 360 361 Class c = clazz; 362 363 while (c.isArray()) 364 { 365 buffer.append("array$"); 366 c = c.getComponentType(); 367 } 368 369 buffer.append(c.getName().replace('.', '$')); 370 371 String fieldName = buffer.toString(); 372 373 return addInjectedField(fieldName, Class .class, clazz); 374 } 375 376 380 381 private int addConstructorParameter(Class type, Object value) 382 { 383 _constructorTypes.add(type); 384 _constructorArguments.add(value); 385 386 return _constructorArguments.size(); 387 } 388 389 private BodyBuilder constructorBuilder() 390 { 391 if (_constructorBuilder == null) 392 { 393 _constructorBuilder = new BodyBuilder(); 394 _constructorBuilder.begin(); 395 } 396 397 return _constructorBuilder; 398 } 399 400 404 405 public ComponentConstructor getConstructor() 406 { 407 try 408 { 409 finalizeEnhancedClass(); 410 411 Constructor c = findConstructor(); 412 413 Object [] params = _constructorArguments.toArray(); 414 415 return new ComponentConstructorImpl(c, params, _classFab.toString(), _specification 416 .getLocation()); 417 } 418 catch (Throwable t) 419 { 420 throw new ApplicationRuntimeException(EnhanceMessages.classEnhancementFailure( 421 _baseClass, 422 t), _classFab, null, t); 423 } 424 } 425 426 void finalizeEnhancedClass() 427 { 428 finalizeIncompleteMethods(); 429 430 if (_constructorBuilder != null) 431 { 432 _constructorBuilder.end(); 433 434 Class [] types = (Class []) _constructorTypes 435 .toArray(new Class [_constructorTypes.size()]); 436 437 _classFab.addConstructor(types, null, _constructorBuilder.toString()); 438 } 439 } 440 441 private void finalizeIncompleteMethods() 442 { 443 Iterator i = _incompleteMethods.entrySet().iterator(); 444 while (i.hasNext()) 445 { 446 Map.Entry e = (Map.Entry ) i.next(); 447 MethodSignature sig = (MethodSignature) e.getKey(); 448 BodyBuilder builder = (BodyBuilder) e.getValue(); 449 450 453 builder.end(); 454 455 _classFab.addMethod(Modifier.PUBLIC, sig, builder.toString()); 456 } 457 } 458 459 private Constructor findConstructor() 460 { 461 Class componentClass = _classFab.createClass(); 462 463 465 return componentClass.getConstructors()[0]; 466 } 467 468 static int _uid = 0; 469 470 private String newClassName() 471 { 472 String baseName = _baseClass.getName(); 473 int dotx = baseName.lastIndexOf('.'); 474 475 return "$" + baseName.substring(dotx + 1) + "_" + _uid++; 476 } 477 478 public void extendMethodImplementation(Class interfaceClass, MethodSignature methodSignature, 479 String code) 480 { 481 addInterfaceIfNeeded(interfaceClass); 482 483 BodyBuilder builder = (BodyBuilder) _incompleteMethods.get(methodSignature); 484 485 if (builder == null) 486 { 487 builder = createIncompleteMethod(methodSignature); 488 489 _incompleteMethods.put(methodSignature, builder); 490 } 491 492 builder.addln(code); 493 } 494 495 private void addInterfaceIfNeeded(Class interfaceClass) 496 { 497 if (implementsInterface(interfaceClass)) 498 return; 499 500 _classFab.addInterface(interfaceClass); 501 _addedInterfaces.add(interfaceClass); 502 } 503 504 public boolean implementsInterface(Class interfaceClass) 505 { 506 if (interfaceClass.isAssignableFrom(_baseClass)) 507 return true; 508 509 Iterator i = _addedInterfaces.iterator(); 510 while (i.hasNext()) 511 { 512 Class addedInterface = (Class ) i.next(); 513 514 if (interfaceClass.isAssignableFrom(addedInterface)) 515 return true; 516 } 517 518 return false; 519 } 520 521 private BodyBuilder createIncompleteMethod(MethodSignature sig) 522 { 523 BodyBuilder result = new BodyBuilder(); 524 525 527 result.begin(); 528 529 if (existingImplementation(sig)) 530 result.addln("super.{0}($$);", sig.getName()); 531 532 return result; 533 } 534 535 539 540 private boolean existingImplementation(MethodSignature sig) 541 { 542 Method m = findMethod(sig); 543 544 return m != null && !Modifier.isAbstract(m.getModifiers()); 545 } 546 547 550 private Method findMethod(MethodSignature sig) 551 { 552 554 try 555 { 556 return _baseClass.getMethod(sig.getName(), sig.getParameterTypes()); 557 558 } 559 catch (NoSuchMethodException ex) 560 { 561 } 563 564 Class c = _baseClass; 565 566 while (c != Object .class) 567 { 568 try 569 { 570 return c.getDeclaredMethod(sig.getName(), sig.getParameterTypes()); 571 } 572 catch (NoSuchMethodException ex) 573 { 574 } 576 577 c = c.getSuperclass(); 578 } 579 580 return null; 581 } 582 583 public List findUnclaimedAbstractProperties() 584 { 585 List result = new ArrayList (); 586 587 Iterator i = _properties.values().iterator(); 588 589 while (i.hasNext()) 590 { 591 PropertyDescriptor pd = (PropertyDescriptor ) i.next(); 592 593 String name = pd.getName(); 594 595 if (_claimedProperties.contains(name)) 596 continue; 597 598 if (isAbstractProperty(pd)) 599 result.add(name); 600 } 601 602 return result; 603 } 604 605 610 private boolean isAbstractProperty(PropertyDescriptor pd) 611 { 612 return isExistingAbstractMethod(pd.getReadMethod()) 613 || isExistingAbstractMethod(pd.getWriteMethod()); 614 } 615 616 private boolean isExistingAbstractMethod(Method m) 617 { 618 return m != null && Modifier.isAbstract(m.getModifiers()); 619 } 620 } | Popular Tags |