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    }