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    }