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 }