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
015package org.apache.tapestry5.internal.transform;
016
017import java.lang.reflect.Modifier;
018
019import org.apache.tapestry5.ComponentResources;
020import org.apache.tapestry5.internal.InternalComponentResources;
021import org.apache.tapestry5.internal.services.ComponentClassCache;
022import org.apache.tapestry5.ioc.services.PerThreadValue;
023import org.apache.tapestry5.ioc.services.PerthreadManager;
024import org.apache.tapestry5.model.MutableComponentModel;
025import org.apache.tapestry5.plastic.ComputedValue;
026import org.apache.tapestry5.plastic.FieldConduit;
027import org.apache.tapestry5.plastic.InstanceContext;
028import org.apache.tapestry5.plastic.PlasticClass;
029import org.apache.tapestry5.plastic.PlasticField;
030import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
031import 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 */
037public 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}