001 // Copyright 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.internal.transform; 016 017 import org.apache.tapestry5.ComponentResources; 018 import org.apache.tapestry5.func.F; 019 import org.apache.tapestry5.func.Mapper; 020 import org.apache.tapestry5.func.Predicate; 021 import org.apache.tapestry5.internal.plastic.PlasticInternalUtils; 022 import org.apache.tapestry5.ioc.services.FieldValueConduit; 023 import org.apache.tapestry5.model.MutableComponentModel; 024 import org.apache.tapestry5.plastic.*; 025 import org.apache.tapestry5.runtime.Component; 026 import org.apache.tapestry5.services.*; 027 import org.apache.tapestry5.services.MethodInvocationResult; 028 import org.apache.tapestry5.services.transform.TransformationSupport; 029 import org.slf4j.Logger; 030 031 import java.lang.annotation.Annotation; 032 import java.lang.reflect.Method; 033 import java.util.List; 034 035 /** 036 * A re-implementation of {@link ClassTransformation} around an instance of {@link PlasticClass}, acting as a bridge 037 * for code written against the 5.2 and earlier APIs to work with the 5.3 API. 038 * 039 * @since 5.3 040 */ 041 @SuppressWarnings("deprecation") 042 public class BridgeClassTransformation implements ClassTransformation 043 { 044 private final PlasticClass plasticClass; 045 046 private final TransformationSupport support; 047 048 private final MutableComponentModel model; 049 050 private static final class WrapMethodHandleAsMethodAccess implements MethodAccess 051 { 052 private final MethodHandle handle; 053 054 private WrapMethodHandleAsMethodAccess(MethodHandle handle) 055 { 056 this.handle = handle; 057 } 058 059 public MethodInvocationResult invoke(Object target, Object... arguments) 060 { 061 final org.apache.tapestry5.plastic.MethodInvocationResult plasticResult = handle.invoke(target, arguments); 062 063 return new MethodInvocationResult() 064 { 065 public void rethrow() 066 { 067 plasticResult.rethrow(); 068 } 069 070 public boolean isFail() 071 { 072 return plasticResult.didThrowCheckedException(); 073 } 074 075 public <T extends Throwable> T getThrown(Class<T> throwableClass) 076 { 077 return plasticResult.getCheckedException(throwableClass); 078 } 079 080 public Object getReturnValue() 081 { 082 return plasticResult.getReturnValue(); 083 } 084 }; 085 } 086 } 087 088 private static <T> ComputedValue<T> toComputedValue(final ComponentValueProvider<T> provider) 089 { 090 return new ComputedValue<T>() 091 { 092 public T get(InstanceContext context) 093 { 094 ComponentResources resources = context.get(ComponentResources.class); 095 096 return provider.get(resources); 097 } 098 }; 099 } 100 101 private static FieldConduit<Object> toFieldConduit(final FieldValueConduit fieldValueConduit) 102 { 103 return new FieldConduit<Object>() 104 { 105 public Object get(Object instance, InstanceContext context) 106 { 107 return fieldValueConduit.get(); 108 } 109 110 public void set(Object instance, InstanceContext context, Object newValue) 111 { 112 fieldValueConduit.set(newValue); 113 } 114 }; 115 } 116 117 private static TransformMethodSignature toMethodSignature(MethodDescription description) 118 { 119 return new TransformMethodSignature(description.modifiers, description.returnType, description.methodName, 120 description.argumentTypes, description.checkedExceptionTypes); 121 } 122 123 private static MethodDescription toMethodDescription(TransformMethodSignature signature) 124 { 125 return new MethodDescription(signature.getModifiers(), signature.getReturnType(), signature.getMethodName(), 126 signature.getParameterTypes(), signature.getSignature(), signature.getExceptionTypes()); 127 } 128 129 private static class BridgeTransformField implements TransformField 130 { 131 private static final class WrapFieldHandleAsFieldAccess implements FieldAccess 132 { 133 private final FieldHandle handle; 134 135 private WrapFieldHandleAsFieldAccess(FieldHandle handle) 136 { 137 this.handle = handle; 138 } 139 140 public void write(Object instance, Object value) 141 { 142 handle.set(instance, value); 143 } 144 145 public Object read(Object instance) 146 { 147 return handle.get(instance); 148 } 149 } 150 151 private static final class WrapFieldValueConduitAsFieldConduit implements FieldConduit 152 { 153 private final FieldValueConduit conduit; 154 155 private WrapFieldValueConduitAsFieldConduit(FieldValueConduit conduit) 156 { 157 this.conduit = conduit; 158 } 159 160 public Object get(Object instance, InstanceContext context) 161 { 162 return conduit.get(); 163 } 164 165 public void set(Object instance, InstanceContext context, Object newValue) 166 { 167 conduit.set(newValue); 168 } 169 } 170 171 private static final class WrapFieldHandleForFieldValueConduitAsFieldConduit implements FieldConduit<Object> 172 { 173 private final FieldHandle conduitHandle; 174 175 private WrapFieldHandleForFieldValueConduitAsFieldConduit(FieldHandle conduitHandle) 176 { 177 this.conduitHandle = conduitHandle; 178 } 179 180 private FieldValueConduit conduit(Object instance) 181 { 182 return (FieldValueConduit) conduitHandle.get(instance); 183 } 184 185 public Object get(Object instance, InstanceContext context) 186 { 187 return conduit(instance).get(); 188 } 189 190 public void set(Object instance, InstanceContext context, Object newValue) 191 { 192 conduit(instance).set(newValue); 193 } 194 } 195 196 private static final class WrapCVP_FieldValueConduit_as_CV_FieldConduit implements 197 ComputedValue<FieldConduit<Object>> 198 { 199 private final ComponentValueProvider<FieldValueConduit> conduitProvider; 200 201 private WrapCVP_FieldValueConduit_as_CV_FieldConduit( 202 ComponentValueProvider<FieldValueConduit> conduitProvider) 203 { 204 this.conduitProvider = conduitProvider; 205 } 206 207 public FieldConduit<Object> get(InstanceContext context) 208 { 209 ComponentResources resources = context.get(ComponentResources.class); 210 211 FieldValueConduit fieldValueConduit = conduitProvider.get(resources); 212 213 return toFieldConduit(fieldValueConduit); 214 } 215 } 216 217 private final PlasticField plasticField; 218 219 public BridgeTransformField(PlasticField plasticField) 220 { 221 this.plasticField = plasticField; 222 } 223 224 public int compareTo(TransformField o) 225 { 226 throw new IllegalStateException("compareTo() not yet implemented."); 227 } 228 229 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 230 { 231 return plasticField.getAnnotation(annotationClass); 232 } 233 234 public String getName() 235 { 236 return plasticField.getName(); 237 } 238 239 public String getType() 240 { 241 return plasticField.getTypeName(); 242 } 243 244 public String getSignature() 245 { 246 return plasticField.getGenericSignature(); 247 } 248 249 public void claim(Object tag) 250 { 251 plasticField.claim(tag); 252 } 253 254 public void replaceAccess(final ComponentValueProvider<FieldValueConduit> conduitProvider) 255 { 256 plasticField.setComputedConduit(new WrapCVP_FieldValueConduit_as_CV_FieldConduit(conduitProvider)); 257 } 258 259 /** 260 * We assume that the conduit field contains a {@link FieldValueConduit}, and that the field 261 * was introduced through this instance of BridgeClassTransformation. 262 */ 263 public void replaceAccess(TransformField conduitField) 264 { 265 // Ugly: 266 PlasticField conduitFieldPlastic = ((BridgeTransformField) conduitField).plasticField; 267 268 final FieldHandle conduitHandle = conduitFieldPlastic.getHandle(); 269 270 plasticField.setConduit(new WrapFieldHandleForFieldValueConduitAsFieldConduit(conduitHandle)); 271 } 272 273 public void replaceAccess(final FieldValueConduit conduit) 274 { 275 plasticField.setConduit(new WrapFieldValueConduitAsFieldConduit(conduit)); 276 } 277 278 public int getModifiers() 279 { 280 return plasticField.getModifiers(); 281 } 282 283 public void inject(Object value) 284 { 285 plasticField.inject(value); 286 } 287 288 public <T> void injectIndirect(ComponentValueProvider<T> provider) 289 { 290 plasticField.injectComputed(toComputedValue(provider)); 291 } 292 293 public FieldAccess getAccess() 294 { 295 final FieldHandle handle = plasticField.getHandle(); 296 297 return new WrapFieldHandleAsFieldAccess(handle); 298 } 299 } 300 301 private static BridgeTransformField toTransformField(PlasticField plasticField) 302 { 303 return new BridgeTransformField(plasticField); 304 } 305 306 private static Mapper<PlasticField, TransformField> TO_TRANSFORM_FIELD = new Mapper<PlasticField, TransformField>() 307 { 308 public TransformField map(PlasticField element) 309 { 310 return toTransformField(element); 311 } 312 }; 313 314 private static final class WrapMethodAdviceAsComponentMethodAdvice implements MethodAdvice 315 { 316 private final ComponentMethodAdvice advice; 317 318 private WrapMethodAdviceAsComponentMethodAdvice(ComponentMethodAdvice advice) 319 { 320 this.advice = advice; 321 } 322 323 public void advise(final MethodInvocation invocation) 324 { 325 advice.advise(new ComponentMethodInvocation() 326 { 327 public ComponentResources getComponentResources() 328 { 329 return invocation.getInstanceContext().get(ComponentResources.class); 330 } 331 332 public void rethrow() 333 { 334 invocation.rethrow(); 335 } 336 337 public void proceed() 338 { 339 invocation.proceed(); 340 } 341 342 public void overrideThrown(Exception thrown) 343 { 344 invocation.setCheckedException(thrown); 345 } 346 347 public void overrideResult(Object newResult) 348 { 349 invocation.setReturnValue(newResult); 350 } 351 352 public void override(int index, Object newParameter) 353 { 354 invocation.setParameter(index, newParameter); 355 } 356 357 public boolean isFail() 358 { 359 return invocation.didThrowCheckedException(); 360 } 361 362 public <T extends Throwable> T getThrown(Class<T> throwableClass) 363 { 364 return invocation.getCheckedException(throwableClass); 365 } 366 367 public Class getResultType() 368 { 369 return method().getReturnType(); 370 } 371 372 public Object getResult() 373 { 374 return invocation.getReturnValue(); 375 } 376 377 public Class getParameterType(int index) 378 { 379 return method().getParameterTypes()[index]; 380 } 381 382 public int getParameterCount() 383 { 384 return method().getParameterTypes().length; 385 } 386 387 public Object getParameter(int index) 388 { 389 return invocation.getParameter(index); 390 } 391 392 public String getMethodName() 393 { 394 return method().getName(); 395 } 396 397 private Method method() 398 { 399 return invocation.getMethod(); 400 } 401 402 public <T extends Annotation> T getMethodAnnotation(Class<T> annotationClass) 403 { 404 return invocation.getAnnotation(annotationClass); 405 } 406 407 public Component getInstance() 408 { 409 return (Component) invocation.getInstance(); 410 } 411 }); 412 } 413 } 414 415 private static final class WrapAfterComponentInstanceOperationAsMethodAdvice implements MethodAdvice 416 { 417 private final ComponentInstanceOperation operation; 418 419 private WrapAfterComponentInstanceOperationAsMethodAdvice(ComponentInstanceOperation operation) 420 { 421 this.operation = operation; 422 } 423 424 public void advise(MethodInvocation invocation) 425 { 426 invocation.proceed(); 427 428 operation.invoke((Component) invocation.getInstance()); 429 } 430 } 431 432 private static final class WrapBeforeComponentInstanceOperationAsMethodAdvice implements MethodAdvice 433 { 434 private final ComponentInstanceOperation operation; 435 436 private WrapBeforeComponentInstanceOperationAsMethodAdvice(ComponentInstanceOperation operation) 437 { 438 this.operation = operation; 439 } 440 441 public void advise(MethodInvocation invocation) 442 { 443 operation.invoke((Component) invocation.getInstance()); 444 445 invocation.proceed(); 446 } 447 } 448 449 private class BridgeTransformMethod implements TransformMethod 450 { 451 private final PlasticMethod plasticMethod; 452 453 private TransformMethodSignature signature; 454 455 public BridgeTransformMethod(PlasticMethod plasticMethod) 456 { 457 this.plasticMethod = plasticMethod; 458 } 459 460 public int compareTo(TransformMethod o) 461 { 462 throw new IllegalStateException("compareTo() not yet implemented."); 463 } 464 465 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 466 { 467 return plasticMethod.getAnnotation(annotationClass); 468 } 469 470 public TransformMethodSignature getSignature() 471 { 472 if (signature == null) 473 { 474 signature = toMethodSignature(plasticMethod.getDescription()); 475 } 476 477 return signature; 478 } 479 480 public String getName() 481 { 482 return plasticMethod.getDescription().methodName; 483 } 484 485 public MethodAccess getAccess() 486 { 487 final MethodHandle handle = plasticMethod.getHandle(); 488 489 return new WrapMethodHandleAsMethodAccess(handle); 490 } 491 492 public void addAdvice(final ComponentMethodAdvice advice) 493 { 494 MethodAdvice plasticAdvice = new WrapMethodAdviceAsComponentMethodAdvice(advice); 495 496 plasticMethod.addAdvice(plasticAdvice); 497 } 498 499 public void addOperationBefore(final ComponentInstanceOperation operation) 500 { 501 plasticMethod.addAdvice(new WrapBeforeComponentInstanceOperationAsMethodAdvice(operation)); 502 } 503 504 public void addOperationAfter(final ComponentInstanceOperation operation) 505 { 506 plasticMethod.addAdvice(new WrapAfterComponentInstanceOperationAsMethodAdvice(operation)); 507 } 508 509 public String getMethodIdentifier() 510 { 511 return String.format("%s.%s", plasticClass.getClassName(), getSignature().getMediumDescription()); 512 } 513 514 public boolean isOverride() 515 { 516 return plasticMethod.isOverride(); 517 } 518 519 public <A extends Annotation> A getParameterAnnotation(int index, Class<A> annotationType) 520 { 521 return plasticMethod.getParameters().get(index).getAnnotation(annotationType); 522 } 523 } 524 525 private final Mapper<PlasticMethod, TransformMethod> toTransformMethod = new Mapper<PlasticMethod, TransformMethod>() 526 { 527 public TransformMethod map(PlasticMethod element) 528 { 529 return new BridgeTransformMethod(element); 530 } 531 }; 532 533 public BridgeClassTransformation(PlasticClass plasticClass, TransformationSupport support, 534 MutableComponentModel model) 535 { 536 this.plasticClass = plasticClass; 537 this.support = support; 538 this.model = model; 539 } 540 541 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 542 { 543 return plasticClass.getAnnotation(annotationClass); 544 } 545 546 public String getClassName() 547 { 548 return plasticClass.getClassName(); 549 } 550 551 public String newMemberName(String suggested) 552 { 553 return newMemberName("_", PlasticInternalUtils.toPropertyName(suggested)); 554 } 555 556 public String newMemberName(String prefix, String baseName) 557 { 558 return new StringBuilder(prefix).append(PlasticUtils.nextUID()).append(baseName).toString(); 559 } 560 561 public List<TransformField> matchFieldsWithAnnotation(Class<? extends Annotation> annotationClass) 562 { 563 return F.flow(plasticClass.getFieldsWithAnnotation(annotationClass)).map(TO_TRANSFORM_FIELD).toList(); 564 } 565 566 public List<TransformMethod> matchMethods(Predicate<TransformMethod> predicate) 567 { 568 return F.flow(plasticClass.getMethods()).map(toTransformMethod).filter(predicate).toList(); 569 } 570 571 public List<TransformMethod> matchMethodsWithAnnotation(Class<? extends Annotation> annotationType) 572 { 573 return F.flow(plasticClass.getMethodsWithAnnotation(annotationType)).map(toTransformMethod).toList(); 574 } 575 576 public List<TransformField> matchFields(Predicate<TransformField> predicate) 577 { 578 return F.flow(plasticClass.getAllFields()).map(TO_TRANSFORM_FIELD).filter(predicate).toList(); 579 } 580 581 public TransformField getField(String fieldName) 582 { 583 for (PlasticField f : plasticClass.getAllFields()) 584 { 585 if (f.getName().equals(fieldName)) 586 { 587 return toTransformField(f); 588 } 589 } 590 591 throw new IllegalArgumentException(String.format("Class %s does not contain a field named '%s'.", 592 plasticClass.getClassName(), fieldName)); 593 } 594 595 public List<TransformField> matchUnclaimedFields() 596 { 597 return F.flow(plasticClass.getUnclaimedFields()).map(TO_TRANSFORM_FIELD).toList(); 598 } 599 600 public boolean isField(String fieldName) 601 { 602 throw new IllegalArgumentException("isField() not yet implemented."); 603 } 604 605 public TransformField createField(int modifiers, String type, String suggestedName) 606 { 607 // TODO: modifiers are ignored 608 609 PlasticField newField = plasticClass.introduceField(type, suggestedName); 610 611 return toTransformField(newField); 612 } 613 614 public String addInjectedField(Class type, String suggestedName, Object value) 615 { 616 // TODO: The injected field is not actually protected or shared 617 618 PlasticField field = plasticClass.introduceField(type, suggestedName).inject(value); 619 620 return field.getName(); 621 } 622 623 public <T> TransformField addIndirectInjectedField(Class<T> type, String suggestedName, 624 ComponentValueProvider<T> provider) 625 { 626 627 PlasticField field = plasticClass.introduceField(type, suggestedName).injectComputed(toComputedValue(provider)); 628 629 return toTransformField(field); 630 } 631 632 public void addImplementedInterface(Class interfaceClass) 633 { 634 plasticClass.introduceInterface(interfaceClass); 635 } 636 637 public Class toClass(String type) 638 { 639 return support.toClass(type); 640 } 641 642 public Logger getLogger() 643 { 644 return model.getLogger(); 645 } 646 647 public boolean isRootTransformation() 648 { 649 return support.isRootTransformation(); 650 } 651 652 public TransformMethod getOrCreateMethod(TransformMethodSignature signature) 653 { 654 MethodDescription md = toMethodDescription(signature); 655 656 PlasticMethod plasticMethod = plasticClass.introduceMethod(md); 657 658 return new BridgeTransformMethod(plasticMethod); 659 } 660 661 public boolean isDeclaredMethod(TransformMethodSignature signature) 662 { 663 final MethodDescription md = toMethodDescription(signature); 664 665 return !F.flow(plasticClass.getMethods()).filter(new Predicate<PlasticMethod>() 666 { 667 public boolean accept(PlasticMethod element) 668 { 669 return element.getDescription().equals(md); 670 } 671 }).isEmpty(); 672 } 673 674 public void addComponentEventHandler(String eventType, int minContextValues, String methodDescription, 675 ComponentEventHandler handler) 676 { 677 support.addEventHandler(eventType, minContextValues, methodDescription, handler); 678 } 679 }