001 //
002 // Licensed under the Apache License, Version 2.0 (the "License");
003 // you may not use this file except in compliance with the License.
004 // You may obtain a copy of the License at
005 //
006 // http://www.apache.org/licenses/LICENSE-2.0
007 //
008 // Unless required by applicable law or agreed to in writing, software
009 // distributed under the License is distributed on an "AS IS" BASIS,
010 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 // See the License for the specific language governing permissions and
012 // limitations under the License.
013
014 package net.sf.tacos.annotations;
015
016 import java.lang.reflect.Method;
017 import java.lang.reflect.Modifier;
018
019 import org.apache.hivemind.ApplicationRuntimeException;
020 import org.apache.hivemind.Location;
021 import org.apache.hivemind.service.BodyBuilder;
022 import org.apache.hivemind.service.MethodSignature;
023 import org.apache.tapestry.annotations.MethodAnnotationEnhancementWorker;
024 import org.apache.tapestry.enhance.EnhancementOperation;
025 import org.apache.tapestry.enhance.EnhanceUtils;
026 import org.apache.tapestry.spec.IComponentSpecification;
027 import org.apache.tapestry.IComponent;
028
029 /**
030 * @author Andreas Andreou
031 */
032
033 public class CachedAnnotationWorker implements MethodAnnotationEnhancementWorker {
034
035 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec,
036 Method method, Location location) {
037 Class<?> type = method.getReturnType();
038 if (type.equals(void.class))
039 throw new ApplicationRuntimeException(
040 "Cached annotation cannot be used with a method that returns void");
041
042 boolean reset = method.getAnnotation(Cached.class).resetAfterRewind();
043
044 String fieldName = "_$cached$" + method.getName();
045 String loadedFlagField = fieldName + "$loadedFlag";
046 String rewindFlagField = null;
047 op.addField(fieldName, type);
048 op.addField(loadedFlagField, boolean.class);
049 if (reset) {
050 rewindFlagField = fieldName + "$rewindFlag";
051 op.addField(rewindFlagField, Boolean.class);
052 }
053
054 MethodSignature signature = new MethodSignature(method);
055 BodyBuilder builder = createCacheMethod(signature, fieldName, loadedFlagField, rewindFlagField);
056 op.addMethod(Modifier.PUBLIC, signature, builder.toString(), location);
057
058 extendCleanupAfterRender(op, type.isPrimitive() ? null : fieldName, loadedFlagField, rewindFlagField);
059 }
060
061 BodyBuilder createCacheMethod(MethodSignature sig, String cacheField, String loadedFlagField, String rewindFlagField)
062 {
063 BodyBuilder result = new BodyBuilder();
064
065 result.begin();
066
067 if (rewindFlagField!=null) {
068 result.addln("boolean rewinding=getPage().getRequestCycle().isRewinding();");
069 result.addln("if (!this.{0} || (this.{1}!=null && this.{1}.booleanValue()!=rewinding))",
070 loadedFlagField, rewindFlagField);
071 } else {
072 result.addln("if (!this.{0})", loadedFlagField);
073 }
074 result.addln("{");
075 result.addln("this.{0} = super.{1}($$);", cacheField, sig.getName());
076 result.addln("this.{0} = true;", loadedFlagField);
077 if (rewindFlagField!=null) {
078 result.addln("this.{0} = Boolean.valueOf(rewinding);", rewindFlagField);
079 }
080 result.addln("}");
081
082 result.addln("return this.{0};", cacheField);
083
084 result.end();
085
086 return result;
087 }
088
089 void extendCleanupAfterRender(EnhancementOperation op, String fieldName, String loadedFlagField, String rewindFlagField)
090 {
091 BodyBuilder cleanupBody = new BodyBuilder();
092 if (fieldName!=null) {
093 cleanupBody.addln("this.{0} = null;", fieldName);
094 }
095 cleanupBody.addln("this.{0} = false;", loadedFlagField);
096 if (rewindFlagField!=null) {
097 cleanupBody.addln("this.{0} = null;", rewindFlagField);
098 }
099 op.extendMethodImplementation(IComponent.class,
100 EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE, cleanupBody.toString());
101 }
102 }