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 }