Clover coverage report - Code Coverage for tapestry release 4.0-beta-1
Coverage timestamp: Fri Jun 24 2005 14:32:46 EDT
file stats: LOC: 287   Methods: 8
NCLOC: 167   Classes: 1
 
 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  414 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
 42    {
 43  414 Iterator i = spec.getParameterNames().iterator();
 44  414 while (i.hasNext())
 45    {
 46  1030 String name = (String) i.next();
 47   
 48  1030 IParameterSpecification ps = spec.getParameter(name);
 49   
 50  1030 try
 51    {
 52  1030 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  1030 private void performEnhancement(EnhancementOperation op, String parameterName,
 68    IParameterSpecification ps)
 69    {
 70  1030 String propertyName = ps.getPropertyName();
 71  1030 String specifiedType = ps.getType();
 72  1030 boolean cache = ps.getCache();
 73   
 74  1030 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  1030 public void addParameter(EnhancementOperation op, String parameterName, String propertyName,
 96    String specifiedType, boolean cache)
 97    {
 98  1030 Defense.notNull(op, "op");
 99  1030 Defense.notNull(parameterName, "parameterName");
 100  1030 Defense.notNull(propertyName, "propertyName");
 101   
 102  1030 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  1029 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  1029 String fieldName = "_$" + propertyName;
 114  1029 String defaultFieldName = fieldName + "$Default";
 115  1029 String cachedFieldName = fieldName + "$Cached";
 116   
 117  1029 op.addField(fieldName, propertyType);
 118  1029 op.addField(defaultFieldName, propertyType);
 119  1029 op.addField(cachedFieldName, boolean.class);
 120   
 121  1029 buildAccessor(
 122    op,
 123    parameterName,
 124    propertyName,
 125    propertyType,
 126    fieldName,
 127    defaultFieldName,
 128    cachedFieldName,
 129    cache);
 130   
 131  1029 buildMutator(
 132    op,
 133    parameterName,
 134    propertyName,
 135    propertyType,
 136    fieldName,
 137    defaultFieldName,
 138    cachedFieldName);
 139   
 140  1029 extendCleanupAfterRender(
 141    op,
 142    parameterName,
 143    propertyName,
 144    propertyType,
 145    fieldName,
 146    defaultFieldName,
 147    cachedFieldName);
 148    }
 149   
 150  1029 private void extendCleanupAfterRender(EnhancementOperation op, String parameterName,
 151    String propertyName, Class propertyType, String fieldName, String defaultFieldName,
 152    String cachedFieldName)
 153    {
 154  1029 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  1029 String bindingName = propertyName + "Binding";
 162   
 163  1029 addBindingReference(cleanupBody, bindingName, parameterName);
 164   
 165  1029 cleanupBody.addln("if ({0} && ! {1}.isInvariant())", cachedFieldName, bindingName);
 166  1029 cleanupBody.begin();
 167  1029 cleanupBody.addln("{0} = false;", cachedFieldName);
 168  1029 cleanupBody.addln("{0} = {1};", fieldName, defaultFieldName);
 169  1029 cleanupBody.end();
 170   
 171  1029 op.extendMethodImplementation(
 172    IComponent.class,
 173    EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE,
 174    cleanupBody.toString());
 175    }
 176   
 177  3089 private void addBindingReference(BodyBuilder builder, String localVariableName,
 178    String parameterName)
 179    {
 180  3089 builder.addln(
 181    "{0} {1} = getBinding(\"{2}\");",
 182    IBinding.class.getName(),
 183    localVariableName,
 184    parameterName);
 185    }
 186   
 187  1029 private void buildMutator(EnhancementOperation op, String parameterName, String propertyName,
 188    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName)
 189    {
 190  1029 BodyBuilder builder = new BodyBuilder();
 191  1029 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  1029 builder.addln("if (! isInActiveState())");
 198  1029 builder.begin();
 199  1029 builder.addln("{0} = $1;", defaultFieldName);
 200  1029 builder.addln("return;");
 201  1029 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  1029 addBindingReference(builder, "binding", parameterName);
 207   
 208  1029 builder.addln("if (binding == null)");
 209  1029 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  1029 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  1029 builder.addln("if (isRendering())");
 223  1029 builder.begin();
 224  1029 builder.addln("{0} = $1;", fieldName);
 225  1029 builder.addln("{0} = true;", cachedFieldName);
 226  1029 builder.end();
 227   
 228  1029 builder.end();
 229   
 230  1029 String mutatorMethodName = EnhanceUtils.createMutatorMethodName(propertyName);
 231   
 232  1029 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  1031 void buildAccessor(EnhancementOperation op, String parameterName, String propertyName,
 240    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName,
 241    boolean cache)
 242    {
 243  1031 BodyBuilder builder = new BodyBuilder();
 244  1031 builder.begin();
 245   
 246  1031 builder.addln("if ({0}) return {1};", cachedFieldName, fieldName);
 247   
 248  1031 addBindingReference(builder, "binding", parameterName);
 249   
 250  1031 builder.addln("if (binding == null) return {0};", defaultFieldName);
 251   
 252  1031 String javaTypeName = ClassFabUtils.getJavaClassName(propertyType);
 253   
 254  1031 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  1031 String expression = cache ? "isRendering() || binding.isInvariant()"
 265    : "binding.isInvariant()";
 266   
 267  1031 builder.addln("if ({0})", expression);
 268  1031 builder.begin();
 269  1031 builder.addln("{0} = result;", fieldName);
 270  1031 builder.addln("{0} = true;", cachedFieldName);
 271  1031 builder.end();
 272   
 273  1031 builder.addln("return result;");
 274   
 275  1031 builder.end();
 276   
 277  1031 String accessorMethodName = op.getAccessorMethodName(propertyName);
 278   
 279  1031 op.addMethod(Modifier.PUBLIC, new MethodSignature(propertyType, accessorMethodName, null,
 280    null), builder.toString());
 281    }
 282   
 283  41 public void setErrorLog(ErrorLog errorLog)
 284    {
 285  41 _errorLog = errorLog;
 286    }
 287    }