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}