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    
015    package org.apache.tapestry5.internal.transform;
016    
017    import org.apache.tapestry5.annotations.Persist;
018    import org.apache.tapestry5.internal.InternalComponentResources;
019    import org.apache.tapestry5.internal.services.ComponentClassCache;
020    import org.apache.tapestry5.ioc.services.PerThreadValue;
021    import org.apache.tapestry5.ioc.services.PerthreadManager;
022    import org.apache.tapestry5.model.MutableComponentModel;
023    import org.apache.tapestry5.plastic.*;
024    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
025    import org.apache.tapestry5.services.transform.TransformationSupport;
026    
027    /**
028     * Converts fields with the {@link org.apache.tapestry5.annotations.Persist} annotation into persistent fields.
029     */
030    public 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    }