Clover coverage report - Code Coverage for tapestry release 4.0-beta-2
Coverage timestamp: Sat Jul 9 2005 22:02:17 EDT
file stats: LOC: 287   Methods: 8
NCLOC: 167   Classes: 1
30 day Evaluation License registered to hlship@comcast.net Your 30 day evaluation period has expired. Please visit http://www.cenqua.com to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
ParameterPropertyWorker.java 100% 100% 100% 100%
coverage
 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;
 18    import java.util.Iterator;
 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  421 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
 42    {
 43  421 Iterator i = spec.getParameterNames().iterator();
 44  421 while (i.hasNext())
 45    {
 46  1089 String name = (String) i.next();
 47   
 48  1089 IParameterSpecification ps = spec.getParameter(name);
 49   
 50  1089 try
 51    {
 52  1089 performEnhancement(op, name, ps);
 53    }
 54    catch (RuntimeException ex)
 55    {
 56  1 _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  1089 private void performEnhancement(EnhancementOperation op, String parameterName,
 68    IParameterSpecification ps)
 69    {
 70  1089 String propertyName = ps.getPropertyName();
 71  1089 String specifiedType = ps.getType();
 72  1089 boolean cache = ps.getCache();
 73   
 74  1089 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  1089 public void addParameter(EnhancementOperation op, String parameterName, String propertyName,
 96    String specifiedType, boolean cache)
 97    {
 98  1089 Defense.notNull(op, "op");
 99  1089 Defense.notNull(parameterName, "parameterName");
 100  1089 Defense.notNull(propertyName, "propertyName");
 101   
 102  1089 Class 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  1088 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  1088 String fieldName = "_$" + propertyName;
 114  1088 String defaultFieldName = fieldName + "$Default";
 115  1088 String cachedFieldName = fieldName + "$Cached";
 116   
 117  1088 op.addField(fieldName, propertyType);
 118  1088 op.addField(defaultFieldName, propertyType);
 119  1088 op.addField(cachedFieldName, boolean.class);
 120   
 121  1088 buildAccessor(
 122    op,
 123    parameterName,
 124    propertyName,
 125    propertyType,
 126    fieldName,
 127    defaultFieldName,
 128    cachedFieldName,
 129    cache);
 130   
 131  1088 buildMutator(
 132    op,
 133    parameterName,
 134    propertyName,
 135    propertyType,
 136    fieldName,
 137    defaultFieldName,
 138    cachedFieldName);
 139   
 140  1088 extendCleanupAfterRender(
 141    op,
 142    parameterName,
 143    propertyName,
 144    propertyType,
 145    fieldName,
 146    defaultFieldName,
 147    cachedFieldName);
 148    }
 149   
 150  1088 private void extendCleanupAfterRender(EnhancementOperation op, String parameterName,
 151    String propertyName, Class propertyType, String fieldName, String defaultFieldName,
 152    String cachedFieldName)
 153    {
 154  1088 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  1088 String bindingName = propertyName + "Binding";
 162   
 163  1088 addBindingReference(cleanupBody, bindingName, parameterName);
 164   
 165  1088 cleanupBody.addln("if ({0} && ! {1}.isInvariant())", cachedFieldName, bindingName);
 166  1088 cleanupBody.begin();
 167  1088 cleanupBody.addln("{0} = false;", cachedFieldName);
 168  1088 cleanupBody.addln("{0} = {1};", fieldName, defaultFieldName);
 169  1088 cleanupBody.end();
 170   
 171  1088 op.extendMethodImplementation(
 172    IComponent.class,
 173    EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE,
 174    cleanupBody.toString());
 175    }
 176   
 177  3266 private void addBindingReference(BodyBuilder builder, String localVariableName,
 178    String parameterName)
 179    {
 180  3266 builder.addln(
 181    "{0} {1} = getBinding(\"{2}\");",
 182    IBinding.class.getName(),
 183    localVariableName,
 184    parameterName);
 185    }
 186   
 187  1088 private void buildMutator(EnhancementOperation op, String parameterName, String propertyName,
 188    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName)
 189    {
 190  1088 BodyBuilder builder = new BodyBuilder();
 191  1088 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  1088 builder.addln("if (! isInActiveState())");
 198  1088 builder.begin();
 199  1088 builder.addln("{0} = $1;", defaultFieldName);
 200  1088 builder.addln("return;");
 201  1088 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  1088 addBindingReference(builder, "binding", parameterName);
 207   
 208  1088 builder.addln("if (binding == null)");
 209  1088 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  1088 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  1088 builder.addln("if (isRendering())");
 223  1088 builder.begin();
 224  1088 builder.addln("{0} = $1;", fieldName);
 225  1088 builder.addln("{0} = true;", cachedFieldName);
 226  1088 builder.end();
 227   
 228  1088 builder.end();
 229   
 230  1088 String mutatorMethodName = EnhanceUtils.createMutatorMethodName(propertyName);
 231   
 232  1088 op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, mutatorMethodName,
 233    new Class[]
 234    { propertyType }, null), builder.toString());
 235    }
 236   
 237    // Package private for testing
 238   
 239  1090 void buildAccessor(EnhancementOperation op, String parameterName, String propertyName,
 240    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName,
 241    boolean cache)
 242    {
 243  1090 BodyBuilder builder = new BodyBuilder();
 244  1090 builder.begin();
 245   
 246  1090 builder.addln("if ({0}) return {1};", cachedFieldName, fieldName);
 247   
 248  1090 addBindingReference(builder, "binding", parameterName);
 249   
 250  1090 builder.addln("if (binding == null) return {0};", defaultFieldName);
 251   
 252  1090 String javaTypeName = ClassFabUtils.getJavaClassName(propertyType);
 253   
 254  1090 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  1090 String expression = cache ? "isRendering() || binding.isInvariant()"
 265    : "binding.isInvariant()";
 266   
 267  1090 builder.addln("if ({0})", expression);
 268  1090 builder.begin();
 269  1090 builder.addln("{0} = result;", fieldName);
 270  1090 builder.addln("{0} = true;", cachedFieldName);
 271  1090 builder.end();
 272   
 273  1090 builder.addln("return result;");
 274   
 275  1090 builder.end();
 276   
 277  1090 String accessorMethodName = op.getAccessorMethodName(propertyName);
 278   
 279  1090 op.addMethod(Modifier.PUBLIC, new MethodSignature(propertyType, accessorMethodName, null,
 280    null), builder.toString());
 281    }
 282   
 283  42 public void setErrorLog(ErrorLog errorLog)
 284    {
 285  42 _errorLog = errorLog;
 286    }
 287    }