001: // Copyright 2006 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.tapestry.internal.services;
016:
017: import static java.lang.String.format;
018:
019: import java.lang.reflect.Modifier;
020:
021: import org.apache.tapestry.annotations.Persist;
022: import org.apache.tapestry.ioc.util.BodyBuilder;
023: import org.apache.tapestry.model.MutableComponentModel;
024: import org.apache.tapestry.services.ClassTransformation;
025: import org.apache.tapestry.services.ComponentClassTransformWorker;
026: import org.apache.tapestry.services.MethodSignature;
027: import org.apache.tapestry.services.PersistentFieldBundle;
028: import org.apache.tapestry.services.TransformConstants;
029: import org.apache.tapestry.services.TransformUtils;
030:
031: /**
032: * Converts fields with the {@link Persist} annotation into persistent fields.
033: */
034: public class PersistWorker implements ComponentClassTransformWorker {
035:
036: public void transform(ClassTransformation transformation,
037: MutableComponentModel model) {
038: for (String name : transformation
039: .findFieldsWithAnnotation(Persist.class)) {
040: makeFieldPersistent(name, transformation, model);
041: }
042: }
043:
044: /**
045: * Making a field persistent:
046: * <ul>
047: * <li>Need a secondary default field that stores the initial value</li>
048: * <li>Store the active value into the default field when the page finishes loading</li>
049: * <li>Roll the active value back to the default when the page detaches</li>
050: * <ii>On changes to the active field, post the change via the InternalComponentResources</li>
051: * <li>When the page attaches, pull the persisted value for the field out of the
052: * {@link PersistentFieldBundle}</li>
053: * </ul>
054: *
055: * @param fieldName
056: * @param transformation
057: * @param model
058: */
059: private void makeFieldPersistent(String fieldName,
060: ClassTransformation transformation,
061: MutableComponentModel model) {
062: String fieldType = transformation.getFieldType(fieldName);
063: Persist annotation = transformation.getFieldAnnotation(
064: fieldName, Persist.class);
065:
066: // Record the type of persistence, until needed later.
067:
068: String logicalFieldName = model.setFieldPersistenceStrategy(
069: fieldName, annotation.value());
070:
071: String defaultFieldName = transformation.addField(
072: Modifier.PRIVATE, fieldType, fieldName + "_default");
073:
074: transformation.extendMethod(
075: TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
076: format("%s = %s;", defaultFieldName, fieldName));
077:
078: transformation
079: .extendMethod(
080: TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
081: format("%s = %s;", fieldName, defaultFieldName));
082:
083: String resourcesFieldName = transformation
084: .getResourcesFieldName();
085:
086: String writeMethodName = transformation.newMemberName("write",
087: fieldName);
088:
089: BodyBuilder builder = new BodyBuilder();
090:
091: builder.begin();
092: builder.addln("%s.persistFieldChange(\"%s\", ($w) $1);",
093: resourcesFieldName, logicalFieldName);
094: builder.addln("%s = $1;", fieldName);
095: builder.end();
096:
097: transformation.addMethod(new MethodSignature(Modifier.PRIVATE,
098: "void", writeMethodName, new String[] { fieldType },
099: null), builder.toString());
100:
101: transformation.replaceWriteAccess(fieldName, writeMethodName);
102:
103: builder.clear();
104: builder.begin();
105:
106: // Check to see if there's a recorded change for this component, this field.
107:
108: builder.addln("if (%s.hasFieldChange(\"%s\"))",
109: resourcesFieldName, logicalFieldName);
110:
111: String wrapperType = TransformUtils
112: .getWrapperTypeName(fieldType);
113:
114: // Get the value, cast it to the correct type (or wrapper type)
115: builder.add(" %s = ((%s) %s.getFieldChange(\"%s\"))",
116: fieldName, wrapperType, resourcesFieldName,
117: logicalFieldName);
118:
119: // For primtive types, add in the method call to unwrap the wrapper type to a primitive type
120:
121: String unwrapMethodName = TransformUtils
122: .getUnwrapperMethodName(fieldType);
123:
124: if (unwrapMethodName == null)
125: builder.addln(";");
126: else
127: builder.addln(".%s();", unwrapMethodName);
128:
129: builder.end();
130:
131: transformation
132: .extendMethod(
133: TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE,
134: builder.toString());
135:
136: transformation.claimField(fieldName, annotation);
137: }
138: }
|