    001    // Copyright 2010, 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.annotations.PageReset;
    018    import org.apache.tapestry5.func.*;
    019    import org.apache.tapestry5.internal.InternalComponentResources;
    020    import org.apache.tapestry5.internal.structure.PageResetListener;
    021    import org.apache.tapestry5.model.MutableComponentModel;
    022    import org.apache.tapestry5.plastic.*;
    023    import org.apache.tapestry5.services.TransformConstants;
    024    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
    025    import org.apache.tapestry5.services.transform.TransformationSupport;
    026
    027    /**
    028     * Implementation of the {@link PageReset} annotation. Makes the component implement {@link PageResetListener}.
    029     *
    030     * @since 5.2.0
    031     */
    032    public class PageResetAnnotationWorker implements ComponentClassTransformWorker2
    033    {
    034        private static final String META_KEY = "tapestry.page-reset-listener";
    035
    036        private final ConstructorCallback REGISTER_AS_LISTENER = new ConstructorCallback()
    037        {
    038            public void onConstruct(Object instance, InstanceContext context)
    039            {
    040                InternalComponentResources resources = context.get(InternalComponentResources.class);
    041
    042                resources.addPageResetListener((PageResetListener) instance);
    043            }
    044        };
    045
    046        private final Predicate<PlasticMethod> METHOD_MATCHER = new Predicate<PlasticMethod>()
    047        {
    048            public boolean accept(PlasticMethod method)
    049            {
    050                return method.getDescription().methodName.equalsIgnoreCase("pageReset") ||
    051                        method.hasAnnotation(PageReset.class);
    052            }
    053        };
    054
    055        private final Worker<PlasticMethod> METHOD_VALIDATOR = new Worker<PlasticMethod>()
    056        {
    057            public void work(PlasticMethod method)
    058            {
    059                boolean valid = method.isVoid() && method.getParameters().isEmpty();
    060
    061                if (!valid)
    062                {
    063                    throw new RuntimeException(
    064                            String.format(
    065                                    "Method %s is invalid: methods with the @PageReset annotation must return void, and have no parameters.",
    066                                    method.getMethodIdentifier()));
    067                }
    068            }
    069        };
    070
    071        private final Mapper<PlasticMethod, MethodHandle> TO_HANDLE = new Mapper<PlasticMethod, MethodHandle>()
    072        {
    073            public MethodHandle map(PlasticMethod method)
    074            {
    075                return method.getHandle();
    076            }
    077        };
    078
    079        public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
    080        {
    081            Flow<PlasticMethod> methods = findResetMethods(plasticClass);
    082
    083            if (!methods.isEmpty())
    084            {
    085                if (!plasticClass.isInterfaceImplemented(PageResetListener.class))
    086                {
    087                    plasticClass.introduceInterface(PageResetListener.class);
    088                    plasticClass.onConstruct(REGISTER_AS_LISTENER);
    089                }
    090
    091                invokeMethodsOnPageReset(plasticClass, methods);
    092            }
    093        }
    094
    095        private void invokeMethodsOnPageReset(PlasticClass plasticClass, Flow<PlasticMethod> methods)
    096        {
    097            final MethodHandle[] handles = methods.map(TO_HANDLE).toArray(MethodHandle.class);
    098
    099            plasticClass.introduceMethod(TransformConstants.CONTAINING_PAGE_DID_RESET_DESCRIPTION).addAdvice(new MethodAdvice()
    100            {
    101                public void advise(MethodInvocation invocation)
    102                {
    103                    invocation.proceed();
    104
    105                    Object instance = invocation.getInstance();
    106
    107                    for (MethodHandle handle : handles)
    108                    {
    109                        handle.invoke(instance);
    110                    }
    111                }
    112            });
    113        }
    114
    115        private Flow<PlasticMethod> findResetMethods(PlasticClass plasticClass)
    116        {
    117            return F.flow(plasticClass.getMethods()).filter(METHOD_MATCHER).each(METHOD_VALIDATOR);
    118        }
    119    }