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 }