001    // Copyright 2007, 2008 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.ApplicationState;
018    import org.apache.tapestry5.annotations.SessionState;
019    import org.apache.tapestry5.internal.services.ComponentClassCache;
020    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
021    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
022    import org.apache.tapestry5.model.MutableComponentModel;
023    import org.apache.tapestry5.services.ApplicationStateManager;
024    import org.apache.tapestry5.services.ClassTransformation;
025    import org.apache.tapestry5.services.ComponentClassTransformWorker;
026    import org.apache.tapestry5.services.TransformMethodSignature;
027    
028    import static java.lang.String.format;
029    import java.lang.reflect.Modifier;
030    import java.util.List;
031    import java.util.Map;
032    
033    /**
034     * Looks for the {@link ApplicationState} and {@link org.apache.tapestry5.annotations.SessionState} annotations and
035     * converts read and write access on such fields into calls to the {@link ApplicationStateManager}.
036     */
037    public class ApplicationStateWorker implements ComponentClassTransformWorker
038    {
039        private final ApplicationStateManager applicationStateManager;
040    
041        private final ComponentClassCache componentClassCache;
042    
043        public ApplicationStateWorker(ApplicationStateManager applicationStateManager,
044                                      ComponentClassCache componentClassCache)
045        {
046            this.applicationStateManager = applicationStateManager;
047            this.componentClassCache = componentClassCache;
048        }
049    
050        public void transform(ClassTransformation transformation, MutableComponentModel model)
051        {
052            Map<String, Boolean> fields = CollectionFactory.newMap();
053    
054            List<String> asoNames = transformation.findFieldsWithAnnotation(ApplicationState.class);
055    
056            for (String name : asoNames)
057            {
058                ApplicationState applicationState = transformation.getFieldAnnotation(name, ApplicationState.class);
059    
060                fields.put(name, applicationState.create());
061            }
062    
063    
064            List<String> ssoNames = transformation.findFieldsWithAnnotation(SessionState.class);
065    
066            for (String name : ssoNames)
067            {
068                SessionState sessionState = transformation.getFieldAnnotation(name, SessionState.class);
069    
070                fields.put(name, sessionState.create());
071            }
072    
073    
074            if (fields.isEmpty()) return;
075    
076            String managerFieldName = transformation.addInjectedField(ApplicationStateManager.class,
077                                                                      "applicationStateManager", applicationStateManager);
078    
079            for (String fieldName : InternalUtils.sortedKeys(fields))
080            {
081                processField(fieldName, managerFieldName, transformation, fields.get(fieldName));
082            }
083        }
084    
085        private void processField(String fieldName, String managerFieldName, ClassTransformation transformation,
086                                  boolean create)
087        {
088            String fieldType = transformation.getFieldType(fieldName);
089    
090            Class fieldClass = componentClassCache.forName(fieldType);
091    
092            String typeFieldName = transformation.addInjectedField(Class.class, fieldName + "_type", fieldClass);
093    
094            replaceRead(transformation, fieldName, fieldType, managerFieldName, typeFieldName, create);
095    
096            replaceWrite(transformation, fieldName, fieldType, managerFieldName, typeFieldName);
097    
098            transformation.removeField(fieldName);
099    
100            String booleanFieldName = fieldName + "Exists";
101    
102            if (transformation.isField(booleanFieldName) && transformation.getFieldType(booleanFieldName).equals("boolean"))
103            {
104                replaceFlagRead(transformation, booleanFieldName, typeFieldName, managerFieldName);
105            }
106        }
107    
108        private void replaceFlagRead(ClassTransformation transformation, String booleanFieldName, String typeFieldName,
109                                     String managerFieldName)
110        {
111            String readMethodName = transformation.newMemberName("read", booleanFieldName);
112    
113            TransformMethodSignature sig = new TransformMethodSignature(Modifier.PRIVATE, "boolean", readMethodName, null,
114                                                                        null);
115    
116            String body = format("return %s.exists(%s);", managerFieldName, typeFieldName);
117    
118            transformation.addMethod(sig, body);
119    
120            transformation.replaceReadAccess(booleanFieldName, readMethodName);
121            transformation.makeReadOnly(booleanFieldName);
122            transformation.removeField(booleanFieldName);
123        }
124    
125        private void replaceWrite(ClassTransformation transformation, String fieldName, String fieldType,
126                                  String managerFieldName, String typeFieldName)
127        {
128            String writeMethodName = transformation.newMemberName("write", fieldName);
129    
130            TransformMethodSignature writeSignature = new TransformMethodSignature(Modifier.PRIVATE, "void",
131                                                                                   writeMethodName,
132                                                                                   new String[] { fieldType },
133                                                                                   null);
134    
135            String body = format("%s.set(%s, $1);", managerFieldName, typeFieldName);
136    
137            transformation.addMethod(writeSignature, body);
138    
139            transformation.replaceWriteAccess(fieldName, writeMethodName);
140        }
141    
142        private void replaceRead(ClassTransformation transformation, String fieldName, String fieldType,
143                                 String managerFieldName, String typeFieldName, boolean create)
144        {
145            String readMethodName = transformation.newMemberName("read", fieldName);
146    
147            TransformMethodSignature readMethodSignature = new TransformMethodSignature(Modifier.PRIVATE, fieldType,
148                                                                                        readMethodName, null, null);
149    
150            String methodName = create ? "get" : "getIfExists";
151    
152            String body = format("return (%s) %s.%s(%s);", fieldType, managerFieldName, methodName, typeFieldName);
153    
154            transformation.addMethod(readMethodSignature, body);
155    
156            transformation.replaceReadAccess(fieldName, readMethodName);
157        }
158    }