KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > enhance > ParameterPropertyWorker


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.enhance;
16
17 import java.lang.reflect.Modifier JavaDoc;
18 import java.util.Iterator JavaDoc;
19
20 import org.apache.hivemind.ApplicationRuntimeException;
21 import org.apache.hivemind.ErrorLog;
22 import org.apache.hivemind.service.BodyBuilder;
23 import org.apache.hivemind.service.ClassFabUtils;
24 import org.apache.hivemind.service.MethodSignature;
25 import org.apache.hivemind.util.Defense;
26 import org.apache.tapestry.IBinding;
27 import org.apache.tapestry.IComponent;
28 import org.apache.tapestry.spec.IComponentSpecification;
29 import org.apache.tapestry.spec.IParameterSpecification;
30
31 /**
32  * Responsible for creating properties for connected parameters.
33  *
34  * @author Howard M. Lewis Ship
35  * @since 4.0
36  */

37 public class ParameterPropertyWorker implements EnhancementWorker
38 {
39     private ErrorLog _errorLog;
40
41     public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
42     {
43         Iterator JavaDoc i = spec.getParameterNames().iterator();
44         while (i.hasNext())
45         {
46             String JavaDoc name = (String JavaDoc) i.next();
47
48             IParameterSpecification ps = spec.getParameter(name);
49
50             try
51             {
52                 performEnhancement(op, name, ps);
53             }
54             catch (RuntimeException JavaDoc ex)
55             {
56                 _errorLog.error(EnhanceMessages.errorAddingProperty(ps.getPropertyName(), op
57                         .getBaseClass(), ex), ps.getLocation(), ex);
58             }
59         }
60     }
61
62     /**
63      * Performs the enhancement for a single parameter; this is about to change radically in release
64      * 4.0 but for the moment we're emulating 3.0 behavior.
65      */

66
67     private void performEnhancement(EnhancementOperation op, String JavaDoc parameterName,
68             IParameterSpecification ps)
69     {
70         String JavaDoc propertyName = ps.getPropertyName();
71         String JavaDoc specifiedType = ps.getType();
72         boolean cache = ps.getCache();
73
74         addParameter(op, parameterName, propertyName, specifiedType, cache);
75     }
76
77     /**
78      * Adds a parameter as a (very smart) property.
79      *
80      * @param op
81      * the enhancement operation
82      * @param parameterName
83      * the name of the parameter (used to access the binding)
84      * @param propertyName
85      * the name of the property to create (usually, but not always, matches the
86      * parameterName)
87      * @param specifiedType
88      * the type declared in the DTD (only 3.0 DTD supports this), may be null (always
89      * null for 4.0 DTD)
90      * @param cache
91      * if true, then the value should be cached while the component renders; false (a
92      * much less common case) means that every access will work through binding object.
93      */

94
95     public void addParameter(EnhancementOperation op, String JavaDoc parameterName, String JavaDoc propertyName,
96             String JavaDoc specifiedType, boolean cache)
97     {
98         Defense.notNull(op, "op");
99         Defense.notNull(parameterName, "parameterName");
100         Defense.notNull(propertyName, "propertyName");
101
102         Class JavaDoc propertyType = EnhanceUtils.extractPropertyType(op, propertyName, specifiedType);
103
104         // 3.0 would allow connected parameter properties to be fully implemented
105
// in the component class. This is not supported in 4.0 and an existing
106
// property will be overwritten in the subclass.
107

108         op.claimProperty(propertyName);
109
110         // 3.0 used to support a property for the binding itself. That's
111
// no longer the case.
112

113         String JavaDoc fieldName = "_$" + propertyName;
114         String JavaDoc defaultFieldName = fieldName + "$Default";
115         String JavaDoc cachedFieldName = fieldName + "$Cached";
116
117         op.addField(fieldName, propertyType);
118         op.addField(defaultFieldName, propertyType);
119         op.addField(cachedFieldName, boolean.class);
120
121         buildAccessor(
122                 op,
123                 parameterName,
124                 propertyName,
125                 propertyType,
126                 fieldName,
127                 defaultFieldName,
128                 cachedFieldName,
129                 cache);
130
131         buildMutator(
132                 op,
133                 parameterName,
134                 propertyName,
135                 propertyType,
136                 fieldName,
137                 defaultFieldName,
138                 cachedFieldName);
139
140         extendCleanupAfterRender(
141                 op,
142                 parameterName,
143                 propertyName,
144                 propertyType,
145                 fieldName,
146                 defaultFieldName,
147                 cachedFieldName);
148     }
149
150     private void extendCleanupAfterRender(EnhancementOperation op, String JavaDoc parameterName,
151             String JavaDoc propertyName, Class JavaDoc propertyType, String JavaDoc fieldName, String JavaDoc defaultFieldName,
152             String JavaDoc cachedFieldName)
153     {
154         BodyBuilder cleanupBody = new BodyBuilder();
155
156         // Cached is only set when the field is updated in the accessor or mutator.
157
// After rendering, we want to clear the cached value and cached flag
158
// unless the binding is invariant, in which case it can stick around
159
// for some future render.
160

161         String JavaDoc bindingName = propertyName + "Binding";
162
163         addBindingReference(cleanupBody, bindingName, parameterName);
164
165         cleanupBody.addln("if ({0} && ! {1}.isInvariant())", cachedFieldName, bindingName);
166         cleanupBody.begin();
167         cleanupBody.addln("{0} = false;", cachedFieldName);
168         cleanupBody.addln("{0} = {1};", fieldName, defaultFieldName);
169         cleanupBody.end();
170
171         op.extendMethodImplementation(
172                 IComponent.class,
173                 EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE,
174                 cleanupBody.toString());
175     }
176
177     private void addBindingReference(BodyBuilder builder, String JavaDoc localVariableName,
178             String JavaDoc parameterName)
179     {
180         builder.addln(
181                 "{0} {1} = getBinding(\"{2}\");",
182                 IBinding.class.getName(),
183                 localVariableName,
184                 parameterName);
185     }
186
187     private void buildMutator(EnhancementOperation op, String JavaDoc parameterName, String JavaDoc propertyName,
188             Class JavaDoc propertyType, String JavaDoc fieldName, String JavaDoc defaultFieldName, String JavaDoc cachedFieldName)
189     {
190         BodyBuilder builder = new BodyBuilder();
191         builder.begin();
192
193         // The mutator method may be invoked from finishLoad(), in which
194
// case it changes the default value for the parameter property, if the parameter
195
// is not bound.
196

197         builder.addln("if (! isInActiveState())");
198         builder.begin();
199         builder.addln("{0} = $1;", defaultFieldName);
200         builder.addln("return;");
201         builder.end();
202
203         // In the normal state, we update the binding firstm, and it's an error
204
// if the parameter is not bound.
205

206         addBindingReference(builder, "binding", parameterName);
207
208         builder.addln("if (binding == null)");
209         builder.addln(
210                 " throw new {0}(\"Parameter ''{1}'' is not bound and can not be updated.\");",
211                 ApplicationRuntimeException.class.getName(),
212                 parameterName);
213
214         // Always updated the binding first (which may fail with an exception).
215

216         builder.addln("binding.setObject(($w) $1);");
217
218         // While rendering, we store the updated value for fast
219
// access again (while the component is still rendering).
220
// The property value will be reset to default by cleanupAfterRender().
221

222         builder.addln("if (isRendering())");
223         builder.begin();
224         builder.addln("{0} = $1;", fieldName);
225         builder.addln("{0} = true;", cachedFieldName);
226         builder.end();
227
228         builder.end();
229
230         String JavaDoc mutatorMethodName = EnhanceUtils.createMutatorMethodName(propertyName);
231
232         op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, mutatorMethodName,
233                 new Class JavaDoc[]
234                 { propertyType }, null), builder.toString());
235     }
236
237     // Package private for testing
238

239     void buildAccessor(EnhancementOperation op, String JavaDoc parameterName, String JavaDoc propertyName,
240             Class JavaDoc propertyType, String JavaDoc fieldName, String JavaDoc defaultFieldName, String JavaDoc cachedFieldName,
241             boolean cache)
242     {
243         BodyBuilder builder = new BodyBuilder();
244         builder.begin();
245
246         builder.addln("if ({0}) return {1};", cachedFieldName, fieldName);
247
248         addBindingReference(builder, "binding", parameterName);
249
250         builder.addln("if (binding == null) return {0};", defaultFieldName);
251
252         String JavaDoc javaTypeName = ClassFabUtils.getJavaClassName(propertyType);
253
254         builder.addln("{0} result = {1};", javaTypeName, EnhanceUtils.createUnwrapExpression(
255                 op,
256                 "binding",
257                 propertyType));
258
259         // Values read via the binding are cached during the render of
260
// the component (if the parameter defines cache to be true, which
261
// is the default), or any time the binding is invariant
262
// (such as most bindings besides ExpressionBinding.
263

264         String JavaDoc expression = cache ? "isRendering() || binding.isInvariant()"
265                 : "binding.isInvariant()";
266
267         builder.addln("if ({0})", expression);
268         builder.begin();
269         builder.addln("{0} = result;", fieldName);
270         builder.addln("{0} = true;", cachedFieldName);
271         builder.end();
272
273         builder.addln("return result;");
274
275         builder.end();
276
277         String JavaDoc accessorMethodName = op.getAccessorMethodName(propertyName);
278
279         op.addMethod(Modifier.PUBLIC, new MethodSignature(propertyType, accessorMethodName, null,
280                 null), builder.toString());
281     }
282
283     public void setErrorLog(ErrorLog errorLog)
284     {
285         _errorLog = errorLog;
286     }
287 }
Popular Tags