    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
    015package org.apache.tapestry5.internal.transform;
    016
    017import org.apache.tapestry5.annotations.PageReset;
    018import org.apache.tapestry5.func.*;
    019import org.apache.tapestry5.internal.InternalComponentResources;
    020import org.apache.tapestry5.internal.structure.PageResetListener;
    021import org.apache.tapestry5.model.MutableComponentModel;
    022import org.apache.tapestry5.plastic.*;
    023import org.apache.tapestry5.services.TransformConstants;
    024import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
    025import 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 */
    032public 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}