| // Copyright 2004, 2005 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry.enhance; |
| |
| import org.apache.hivemind.ErrorLog; |
| import org.apache.hivemind.Location; |
| import org.apache.hivemind.service.BodyBuilder; |
| import org.apache.hivemind.service.MethodSignature; |
| import org.apache.hivemind.util.Defense; |
| import org.apache.tapestry.IBinding; |
| import org.apache.tapestry.IComponent; |
| import org.apache.tapestry.binding.BindingSource; |
| import org.apache.tapestry.event.PageDetachListener; |
| import org.apache.tapestry.spec.IComponentSpecification; |
| import org.apache.tapestry.spec.IPropertySpecification; |
| |
| import java.lang.reflect.Modifier; |
| import java.util.Iterator; |
| |
| /** |
| * Responsible for adding properties to a class corresponding to specified |
| * properties in the component's specification - which may come from .jwc / .page specifications |
| * or annotated abstract methods. |
| * |
| * @author Howard M. Lewis Ship |
| * @since 4.0 |
| */ |
| public class SpecifiedPropertyWorker implements EnhancementWorker |
| { |
| private ErrorLog _errorLog; |
| |
| private BindingSource _bindingSource; |
| |
| /** |
| * Iterates over the specified properties, creating an enhanced property for |
| * each (a field, an accessor, a mutator). Persistent properties will invoke |
| * {@link org.apache.tapestry.Tapestry#fireObservedChange(IComponent, String, Object)}in |
| * thier mutator. |
| */ |
| |
| public void performEnhancement(EnhancementOperation op, IComponentSpecification spec) |
| { |
| Iterator i = spec.getPropertySpecificationNames().iterator(); |
| |
| while(i.hasNext()) |
| { |
| String name = (String) i.next(); |
| IPropertySpecification ps = spec.getPropertySpecification(name); |
| |
| try |
| { |
| performEnhancement(op, ps); |
| } |
| catch (RuntimeException ex) |
| { |
| _errorLog.error(EnhanceMessages.errorAddingProperty(name, op |
| .getBaseClass(), ex), ps.getLocation(), ex); |
| } |
| } |
| } |
| |
| private void performEnhancement(EnhancementOperation op, |
| IPropertySpecification ps) |
| { |
| Defense.notNull(ps, "ps"); |
| |
| String propertyName = ps.getName(); |
| String specifiedType = ps.getType(); |
| boolean persistent = ps.isPersistent(); |
| String initialValue = ps.getInitialValue(); |
| Location location = ps.getLocation(); |
| |
| addProperty(op, propertyName, specifiedType, persistent, initialValue, location, ps); |
| } |
| |
| public void addProperty(EnhancementOperation op, String propertyName, String specifiedType, |
| boolean persistent, String initialValue, Location location, IPropertySpecification ps) |
| { |
| Class propertyType = EnhanceUtils.extractPropertyType(op, propertyName, specifiedType, ps.isGeneric()); |
| |
| op.claimProperty(propertyName); |
| |
| String field = "_$" + propertyName; |
| |
| op.addField(field, propertyType); |
| |
| // Release 3.0 would squack a bit about overriding non-abstract methods |
| // if they exist. 4.0 is less picky ... it blindly adds new methods, |
| // possibly |
| // overwriting methods in the base component class. |
| |
| EnhanceUtils.createSimpleAccessor(op, field, propertyName, propertyType, location); |
| |
| addMutator(op, propertyName, propertyType, field, persistent, location); |
| |
| if (initialValue == null) |
| addReinitializer(op, propertyType, field); |
| else |
| addInitialValue(op, propertyName, propertyType, field, initialValue, persistent, location); |
| } |
| |
| private void addReinitializer(EnhancementOperation op, Class propertyType, String fieldName) |
| { |
| String defaultFieldName = fieldName + "$default"; |
| |
| op.addField(defaultFieldName, propertyType); |
| |
| // On finishLoad(), store the current value into the default field. |
| |
| op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, |
| defaultFieldName + " = " + fieldName + ";"); |
| |
| // On pageDetach(), restore the attribute to its default value. |
| |
| op.extendMethodImplementation(PageDetachListener.class, |
| EnhanceUtils.PAGE_DETACHED_SIGNATURE, fieldName + " = " + defaultFieldName + ";"); |
| } |
| |
| private void addInitialValue(EnhancementOperation op, String propertyName, Class propertyType, |
| String fieldName, String initialValue, boolean persistent, Location location) |
| { |
| String description = EnhanceMessages.initialValueForProperty(propertyName); |
| |
| InitialValueBindingCreator creator = |
| new InitialValueBindingCreator(_bindingSource, description, initialValue, location); |
| |
| String creatorField = op.addInjectedField(fieldName + "$initialValueBindingCreator", InitialValueBindingCreator.class, creator); |
| |
| String bindingField = fieldName + "$initialValueBinding"; |
| op.addField(bindingField, IBinding.class); |
| |
| BodyBuilder builder = new BodyBuilder(); |
| |
| builder.addln("{0} = {1}.createBinding(this);", bindingField, creatorField); |
| |
| op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, builder.toString()); |
| |
| builder.clear(); |
| |
| builder.addln("{0} = {1};", fieldName, EnhanceUtils.createUnwrapExpression(op, bindingField, propertyType)); |
| |
| String code = builder.toString(); |
| |
| // In finishLoad() and pageDetach(), de-reference the binding to get the |
| // value |
| // for the property. |
| |
| op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, code); |
| |
| op.extendMethodImplementation(PageDetachListener.class, EnhanceUtils.PAGE_DETACHED_SIGNATURE, code); |
| } |
| |
| private void addMutator(EnhancementOperation op, String propertyName, |
| Class propertyType, String fieldName, boolean persistent, Location location) |
| { |
| String methodName = EnhanceUtils.createMutatorMethodName(propertyName); |
| |
| BodyBuilder body = new BodyBuilder(); |
| |
| body.begin(); |
| |
| if (persistent) { |
| |
| body.add("org.apache.tapestry.Tapestry#fireObservedChange(this, "); |
| body.addQuoted(propertyName); |
| body.addln(", ($w) $1);"); |
| } |
| |
| body.addln(fieldName + " = $1;"); |
| body.end(); |
| |
| MethodSignature sig = new MethodSignature(void.class, methodName, new Class[] { propertyType }, null); |
| |
| op.addMethod(Modifier.PUBLIC, sig, body.toString(), location); |
| } |
| |
| public void setErrorLog(ErrorLog errorLog) |
| { |
| _errorLog = errorLog; |
| } |
| |
| public void setBindingSource(BindingSource bindingSource) |
| { |
| _bindingSource = bindingSource; |
| } |
| } |