    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}