001 // Copyright 2004, 2005 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.enhance;
016
017 import org.apache.hivemind.ErrorLog;
018 import org.apache.hivemind.Location;
019 import org.apache.hivemind.service.BodyBuilder;
020 import org.apache.hivemind.service.MethodSignature;
021 import org.apache.hivemind.util.Defense;
022 import org.apache.tapestry.IBinding;
023 import org.apache.tapestry.IComponent;
024 import org.apache.tapestry.binding.BindingSource;
025 import org.apache.tapestry.event.PageDetachListener;
026 import org.apache.tapestry.spec.IComponentSpecification;
027 import org.apache.tapestry.spec.IPropertySpecification;
028
029 import java.lang.reflect.Modifier;
030 import java.util.Iterator;
031
032 /**
033 * Responsible for adding properties to a class corresponding to specified
034 * properties in the component's specification - which may come from .jwc / .page specifications
035 * or annotated abstract methods.
036 *
037 * @author Howard M. Lewis Ship
038 * @since 4.0
039 */
040 public class SpecifiedPropertyWorker implements EnhancementWorker
041 {
042 private ErrorLog _errorLog;
043
044 private BindingSource _bindingSource;
045
046 /**
047 * Iterates over the specified properties, creating an enhanced property for
048 * each (a field, an accessor, a mutator). Persistent properties will invoke
049 * {@link org.apache.tapestry.Tapestry#fireObservedChange(IComponent, String, Object)}in
050 * thier mutator.
051 */
052
053 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
054 {
055 Iterator i = spec.getPropertySpecificationNames().iterator();
056
057 while(i.hasNext())
058 {
059 String name = (String) i.next();
060 IPropertySpecification ps = spec.getPropertySpecification(name);
061
062 try
063 {
064 performEnhancement(op, ps);
065 }
066 catch (RuntimeException ex)
067 {
068 _errorLog.error(EnhanceMessages.errorAddingProperty(name, op
069 .getBaseClass(), ex), ps.getLocation(), ex);
070 }
071 }
072 }
073
074 private void performEnhancement(EnhancementOperation op,
075 IPropertySpecification ps)
076 {
077 Defense.notNull(ps, "ps");
078
079 String propertyName = ps.getName();
080 String specifiedType = ps.getType();
081 boolean persistent = ps.isPersistent();
082 String initialValue = ps.getInitialValue();
083 Location location = ps.getLocation();
084
085 addProperty(op, propertyName, specifiedType, persistent, initialValue, location, ps);
086 }
087
088 public void addProperty(EnhancementOperation op, String propertyName, String specifiedType,
089 boolean persistent, String initialValue, Location location, IPropertySpecification ps)
090 {
091 Class propertyType = EnhanceUtils.extractPropertyType(op, propertyName, specifiedType, ps.isGeneric());
092
093 op.claimProperty(propertyName);
094
095 String field = "_$" + propertyName;
096
097 op.addField(field, propertyType);
098
099 // Release 3.0 would squack a bit about overriding non-abstract methods
100 // if they exist. 4.0 is less picky ... it blindly adds new methods,
101 // possibly
102 // overwriting methods in the base component class.
103
104 EnhanceUtils.createSimpleAccessor(op, field, propertyName, propertyType, location);
105
106 addMutator(op, propertyName, propertyType, field, persistent, location);
107
108 if (initialValue == null)
109 addReinitializer(op, propertyType, field);
110 else
111 addInitialValue(op, propertyName, propertyType, field, initialValue, persistent, location);
112 }
113
114 private void addReinitializer(EnhancementOperation op, Class propertyType, String fieldName)
115 {
116 String defaultFieldName = fieldName + "$default";
117
118 op.addField(defaultFieldName, propertyType);
119
120 // On finishLoad(), store the current value into the default field.
121
122 op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE,
123 defaultFieldName + " = " + fieldName + ";");
124
125 // On pageDetach(), restore the attribute to its default value.
126
127 op.extendMethodImplementation(PageDetachListener.class,
128 EnhanceUtils.PAGE_DETACHED_SIGNATURE, fieldName + " = " + defaultFieldName + ";");
129 }
130
131 private void addInitialValue(EnhancementOperation op, String propertyName, Class propertyType,
132 String fieldName, String initialValue, boolean persistent, Location location)
133 {
134 String description = EnhanceMessages.initialValueForProperty(propertyName);
135
136 InitialValueBindingCreator creator =
137 new InitialValueBindingCreator(_bindingSource, description, initialValue, location);
138
139 String creatorField = op.addInjectedField(fieldName + "$initialValueBindingCreator", InitialValueBindingCreator.class, creator);
140
141 String bindingField = fieldName + "$initialValueBinding";
142 op.addField(bindingField, IBinding.class);
143
144 BodyBuilder builder = new BodyBuilder();
145
146 builder.addln("{0} = {1}.createBinding(this);", bindingField, creatorField);
147
148 op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, builder.toString());
149
150 builder.clear();
151
152 builder.addln("{0} = {1};", fieldName, EnhanceUtils.createUnwrapExpression(op, bindingField, propertyType));
153
154 String code = builder.toString();
155
156 // In finishLoad() and pageDetach(), de-reference the binding to get the
157 // value
158 // for the property.
159
160 op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, code);
161
162 op.extendMethodImplementation(PageDetachListener.class, EnhanceUtils.PAGE_DETACHED_SIGNATURE, code);
163 }
164
165 private void addMutator(EnhancementOperation op, String propertyName,
166 Class propertyType, String fieldName, boolean persistent, Location location)
167 {
168 String methodName = EnhanceUtils.createMutatorMethodName(propertyName);
169
170 BodyBuilder body = new BodyBuilder();
171
172 body.begin();
173
174 if (persistent) {
175
176 body.add("org.apache.tapestry.Tapestry#fireObservedChange(this, ");
177 body.addQuoted(propertyName);
178 body.addln(", ($w) $1);");
179 }
180
181 body.addln(fieldName + " = $1;");
182 body.end();
183
184 MethodSignature sig = new MethodSignature(void.class, methodName, new Class[] { propertyType }, null);
185
186 op.addMethod(Modifier.PUBLIC, sig, body.toString(), location);
187 }
188
189 public void setErrorLog(ErrorLog errorLog)
190 {
191 _errorLog = errorLog;
192 }
193
194 public void setBindingSource(BindingSource bindingSource)
195 {
196 _bindingSource = bindingSource;
197 }
198 }