// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.internal.transform;

import java.util.List;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.internal.InternalComponentResources;
import org.apache.tapestry5.internal.services.ComponentClassCache;
import org.apache.tapestry5.ioc.services.FieldValueConduit;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.apache.tapestry5.model.MutableComponentModel;
import org.apache.tapestry5.runtime.PageLifecycleAdapter;
import org.apache.tapestry5.services.ClassTransformation;
import org.apache.tapestry5.services.ComponentClassTransformWorker;
import org.apache.tapestry5.services.ComponentValueProvider;
import org.apache.tapestry5.services.TransformField;

/**
 * Converts fields with the {@link org.apache.tapestry5.annotations.Persist} annotation into persistent fields.
 */
public class PersistWorker implements ComponentClassTransformWorker
{
    class PersistentFieldConduit implements FieldValueConduit
    {
        private final InternalComponentResources resources;

        private final String name;

        private final Object defaultValue;

        private final String key;

        public PersistentFieldConduit(InternalComponentResources resources, String name, Object defaultValue)
        {
            this.resources = resources;
            this.name = name;
            this.defaultValue = defaultValue;

            this.key = String.format("PersistWorker:%s/%s", resources.getCompleteId(), name);

            resources.addPageLifecycleListener(new PageLifecycleAdapter()
            {
                @Override
                public void restoreStateBeforePageAttach()
                {
                    restoreStateAtPageAttach();
                }
            });
        }

        private PerThreadState getState()
        {
            PerThreadState state = (PerThreadState) perThreadManager.get(key);

            if (state == null)
            {
                state = new PerThreadState(defaultValue);
                perThreadManager.put(key, state);
            }

            return state;
        }

        public Object get()
        {
            return getState().value;
        }

        public void set(Object newValue)
        {
            resources.persistFieldChange(name, newValue);

            getState().value = newValue;
        }

        private void restoreStateAtPageAttach()
        {
            if (resources.hasFieldChange(name))
                getState().value = resources.getFieldChange(name);
        }
    }

    private final ComponentClassCache classCache;

    private final PerthreadManager perThreadManager;

    public PersistWorker(ComponentClassCache classCache, PerthreadManager perThreadManager)
    {
        this.classCache = classCache;
        this.perThreadManager = perThreadManager;
    }

    public void transform(ClassTransformation transformation, MutableComponentModel model)
    {
        List<TransformField> fieldsWithAnnotation = transformation.matchFieldsWithAnnotation(Persist.class);

        for (TransformField field : fieldsWithAnnotation)
        {
            makeFieldPersistent(field, model);
        }
    }

    private void makeFieldPersistent(TransformField field, MutableComponentModel model)
    {
        Persist annotation = field.getAnnotation(Persist.class);

        field.claim(annotation);

        final String logicalFieldName = model.setFieldPersistenceStrategy(field.getName(), annotation.value());

        final Object defaultValue = determineDefaultValueFromFieldType(field);

        ComponentValueProvider<FieldValueConduit> provider = new ComponentValueProvider<FieldValueConduit>()
        {
            public FieldValueConduit get(ComponentResources resources)
            {
                return new PersistentFieldConduit((InternalComponentResources) resources, logicalFieldName,
                        defaultValue);
            }
        };

        field.replaceAccess(provider);
    }

    private Object determineDefaultValueFromFieldType(TransformField field)
    {
        return classCache.defaultValueForType(field.getType());
    }
}

@@ -984,16 +984,73 @@
 onduit;%0A
+import org.apache.tapestry5.ioc.services.PerThreadValue;%0A
 import o
@@ -1829,38 +1829,52 @@
 ivate final 
-Object default
+PerThreadValue%3CObject%3E field
 Value;%0A%0A    
@@ -1895,18 +1895,27 @@
 nal 
-String key
+Object defaultValue
 ;%0A%0A 
@@ -2001,16 +2001,67 @@
 ng name,
+%0A                PerThreadValue%3CObject%3E fieldValue,
  Object 
@@ -2154,16 +2154,58 @@
 = name;%0A
+            this.fieldValue = fieldValue;%0A
         
@@ -2247,103 +2247,8 @@
 e;%0A%0A
-            this.key = String.format(%22PersistWorker:%25s/%25s%22, resources.getCompleteId(), name);%0A%0A
     
@@ -2540,160 +2540,73 @@
    p
-rivate PerThreadState getState()%0A        %7B%0A            PerThreadState state = (PerThreadState) perThreadManager.get(key);%0A%0A            if (state == null
+ublic Object get()%0A        %7B%0A            if (!fieldValue.exists()
 )%0A  
@@ -2618,54 +2618,20 @@
     
- %7B%0A
      
-           state = new PerThreadState(
+return 
 defa
@@ -2642,186 +2642,46 @@
 alue
-)
 ;%0A
-                perThreadManager.put(key, state);%0A            %7D%0A%0A            return state;%0A        %7D%0A%0A        public Object get()%0A        %7B%0A            return getState().value
+%0A            return fieldValue.get()
 ;%0A  
@@ -2815,35 +2815,31 @@
     
-getState().value = 
+fieldValue.set(
 newValue
 ;%0A  
@@ -2830,24 +2830,25 @@
 set(newValue
+)
 ;%0A        %7D%0A
@@ -2974,27 +2974,23 @@
     
-getState().value = 
+fieldValue.set(
 reso
@@ -3015,16 +3015,17 @@
 ge(name)
+)
 ;%0A      
@@ -4255,32 +4255,145 @@
 )%0A            %7B%0A
+                String key = String.format(%22PersistWorker:%25s/%25s%22, resources.getCompleteId(), logicalFieldName);%0A%0A
                 
@@ -4507,16 +4507,51 @@
         
+ perThreadManager.createValue(key),
  default

Make use of PerThreadValue inside PersistWorker