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 }