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 }