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