    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    }