    001// Copyright 2006, 2007, 2008, 2009, 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.Persist;
    018import org.apache.tapestry5.internal.InternalComponentResources;
    019import org.apache.tapestry5.internal.services.ComponentClassCache;
    020import org.apache.tapestry5.ioc.services.PerThreadValue;
    021import org.apache.tapestry5.ioc.services.PerthreadManager;
    022import org.apache.tapestry5.model.MutableComponentModel;
    023import org.apache.tapestry5.plastic.*;
    024import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
    025import org.apache.tapestry5.services.transform.TransformationSupport;
    026
    027/**
    028 * Converts fields with the {@link org.apache.tapestry5.annotations.Persist} annotation into persistent fields.
    029 */
    030public class PersistWorker implements ComponentClassTransformWorker2
    031{
    032    class PersistentFieldConduit implements FieldConduit<Object>
    033    {
    034        private final InternalComponentResources resources;
    035
    036        private final String name;
    037
    038        private final PerThreadValue<Object> fieldValue = perThreadManager.createValue();
    039
    040        private final Object defaultValue;
    041
    042        public PersistentFieldConduit(InternalComponentResources resources, String name,
    043                                      Object defaultValue)
    044        {
    045            this.resources = resources;
    046            this.name = name;
    047            this.defaultValue = defaultValue;
    048        }
    049
    050        public Object get(Object instance, InstanceContext context)
    051        {
    052            if (!fieldValue.exists())
    053            {
    054                Object persistedValue = resources.hasFieldChange(name) ? resources.getFieldChange(name) : defaultValue;
    055
    056                fieldValue.set(persistedValue);
    057            }
    058
    059            return fieldValue.get();
    060        }
    061
    062        public void set(Object instance, InstanceContext context, Object newValue)
    063        {
    064            resources.persistFieldChange(name, newValue);
    065
    066            fieldValue.set(newValue);
    067        }
    068    }
    069
    070    private final ComponentClassCache classCache;
    071
    072    private final PerthreadManager perThreadManager;
    073
    074    public PersistWorker(ComponentClassCache classCache, PerthreadManager perThreadManager)
    075    {
    076        this.classCache = classCache;
    077        this.perThreadManager = perThreadManager;
    078    }
    079
    080    public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
    081    {
    082        for (PlasticField field : plasticClass.getFieldsWithAnnotation(Persist.class))
    083        {
    084            makeFieldPersistent(field, model);
    085        }
    086    }
    087
    088    private void makeFieldPersistent(PlasticField field, MutableComponentModel model)
    089    {
    090        Persist annotation = field.getAnnotation(Persist.class);
    091
    092        field.claim(annotation);
    093
    094        final String logicalFieldName = model.setFieldPersistenceStrategy(field.getName(), annotation.value());
    095
    096        final Object defaultValue = determineDefaultValueFromFieldType(field);
    097
    098        ComputedValue<FieldConduit<Object>> computed = new ComputedValue<FieldConduit<Object>>()
    099        {
    100            public FieldConduit<Object> get(InstanceContext context)
    101            {
    102                InternalComponentResources resources = context.get(InternalComponentResources.class);
    103                return new PersistentFieldConduit(resources, logicalFieldName, defaultValue);
    104            }
    105        };
    106
    107        field.setComputedConduit(computed);
    108    }
    109
    110    private Object determineDefaultValueFromFieldType(PlasticField field)
    111    {
    112        return classCache.defaultValueForType(field.getTypeName());
    113    }
    114}