001    // Copyright 2006, 2007, 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 java.lang.reflect.Modifier;
018    
019    import org.apache.tapestry5.ComponentResources;
020    import org.apache.tapestry5.internal.InternalComponentResources;
021    import org.apache.tapestry5.internal.services.ComponentClassCache;
022    import org.apache.tapestry5.ioc.services.PerThreadValue;
023    import org.apache.tapestry5.ioc.services.PerthreadManager;
024    import org.apache.tapestry5.model.MutableComponentModel;
025    import org.apache.tapestry5.plastic.ComputedValue;
026    import org.apache.tapestry5.plastic.FieldConduit;
027    import org.apache.tapestry5.plastic.InstanceContext;
028    import org.apache.tapestry5.plastic.PlasticClass;
029    import org.apache.tapestry5.plastic.PlasticField;
030    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
031    import org.apache.tapestry5.services.transform.TransformationSupport;
032    
033    /**
034     * Designed to be just about the last worker in the pipeline. Its job is to convert each otherwise unclaimed
035     * field into a value stored in the {@link PerthreadManager}.
036     */
037    public final class UnclaimedFieldWorker implements ComponentClassTransformWorker2
038    {
039        private final PerthreadManager perThreadManager;
040    
041        private final ComponentClassCache classCache;
042    
043        static class UnclaimedFieldConduit implements FieldConduit<Object>
044        {
045            private final InternalComponentResources resources;
046    
047            private final PerThreadValue<Object> fieldValue;
048    
049            // Set prior to the containingPageDidLoad lifecycle event
050            private Object fieldDefaultValue;
051    
052            private UnclaimedFieldConduit(InternalComponentResources resources, PerThreadValue<Object> fieldValue,
053                    Object fieldDefaultValue)
054            {
055                this.resources = resources;
056    
057                this.fieldValue = fieldValue;
058                this.fieldDefaultValue = fieldDefaultValue;
059            }
060    
061            public Object get(Object instance, InstanceContext context)
062            {
063                return fieldValue.get(fieldDefaultValue);
064            }
065    
066            public void set(Object instance, InstanceContext context, Object newValue)
067            {
068                fieldValue.set(newValue);
069    
070                // This catches the case where the instance initializer method sets a value for the field.
071                // That value is captured and used when no specific value has been stored.
072    
073                if (!resources.isLoaded())
074                    fieldDefaultValue = newValue;
075            }
076        }
077    
078        public UnclaimedFieldWorker(ComponentClassCache classCache, PerthreadManager perThreadManager)
079        {
080            this.classCache = classCache;
081            this.perThreadManager = perThreadManager;
082        }
083    
084        public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
085        {
086            for (PlasticField field : plasticClass.getUnclaimedFields())
087            {
088                transformField(field);
089            }
090        }
091    
092        private void transformField(PlasticField field)
093        {
094            if (Modifier.isFinal(field.getModifiers()))
095                return;
096    
097            ComputedValue<FieldConduit<Object>> computed = createComputedFieldConduit(field);
098    
099            field.setComputedConduit(computed);
100        }
101    
102        private ComputedValue<FieldConduit<Object>> createComputedFieldConduit(PlasticField field)
103        {
104            final String fieldType = field.getTypeName();
105    
106            return new ComputedValue<FieldConduit<Object>>()
107            {
108                public FieldConduit<Object> get(InstanceContext context)
109                {
110                    Object fieldDefaultValue = classCache.defaultValueForType(fieldType);
111                    InternalComponentResources resources = context.get(InternalComponentResources.class);
112    
113                    return new UnclaimedFieldConduit(resources, perThreadManager.createValue(), fieldDefaultValue);
114                }
115            };
116        }
117    }