Clover coverage report - Code Coverage for tapestry release 4.0-beta-3
Coverage timestamp: Sun Jul 24 2005 08:26:33 EDT
file stats: LOC: 293   Methods: 8
NCLOC: 169   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  422 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
 42    {
 43  422 Iterator i = spec.getParameterNames().iterator();
 44  422 while (i.hasNext())
 45    {
 46  1096 String name = (String) i.next();
 47   
 48  1096 IParameterSpecification ps = spec.getParameter(name);
 49   
 50  1096 try
 51    {
 52  1096 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  1096 private void performEnhancement(EnhancementOperation op, String parameterName,
 68    IParameterSpecification ps)
 69    {
 70    // If the parameter name doesn't match, its because this is an alias
 71    // for a true parameter; we ignore aliases.
 72   
 73  1096 if (!parameterName.equals(ps.getParameterName()))
 74  1 return;
 75   
 76  1095 String propertyName = ps.getPropertyName();
 77  1095 String specifiedType = ps.getType();
 78  1095 boolean cache = ps.getCache();
 79   
 80  1095 addParameter(op, parameterName, propertyName, specifiedType, cache);
 81    }
 82   
 83    /**
 84    * Adds a parameter as a (very smart) property.
 85    *
 86    * @param op
 87    * the enhancement operation
 88    * @param parameterName
 89    * the name of the parameter (used to access the binding)
 90    * @param propertyName
 91    * the name of the property to create (usually, but not always, matches the
 92    * parameterName)
 93    * @param specifiedType
 94    * the type declared in the DTD (only 3.0 DTD supports this), may be null (always
 95    * null for 4.0 DTD)
 96    * @param cache
 97    * if true, then the value should be cached while the component renders; false (a
 98    * much less common case) means that every access will work through binding object.
 99    */
 100   
 101  1095 public void addParameter(EnhancementOperation op, String parameterName, String propertyName,
 102    String specifiedType, boolean cache)
 103    {
 104  1095 Defense.notNull(op, "op");
 105  1095 Defense.notNull(parameterName, "parameterName");
 106  1095 Defense.notNull(propertyName, "propertyName");
 107   
 108  1095 Class propertyType = EnhanceUtils.extractPropertyType(op, propertyName, specifiedType);
 109   
 110    // 3.0 would allow connected parameter properties to be fully implemented
 111    // in the component class. This is not supported in 4.0 and an existing
 112    // property will be overwritten in the subclass.
 113   
 114  1094 op.claimProperty(propertyName);
 115   
 116    // 3.0 used to support a property for the binding itself. That's
 117    // no longer the case.
 118   
 119  1094 String fieldName = "_$" + propertyName;
 120  1094 String defaultFieldName = fieldName + "$Default";
 121  1094 String cachedFieldName = fieldName + "$Cached";
 122   
 123  1094 op.addField(fieldName, propertyType);
 124  1094 op.addField(defaultFieldName, propertyType);
 125  1094 op.addField(cachedFieldName, boolean.class);
 126   
 127  1094 buildAccessor(
 128    op,
 129    parameterName,
 130    propertyName,
 131    propertyType,
 132    fieldName,
 133    defaultFieldName,
 134    cachedFieldName,
 135    cache);
 136   
 137  1094 buildMutator(
 138    op,
 139    parameterName,
 140    propertyName,
 141    propertyType,
 142    fieldName,
 143    defaultFieldName,
 144    cachedFieldName);
 145   
 146  1094 extendCleanupAfterRender(
 147    op,
 148    parameterName,
 149    propertyName,
 150    propertyType,
 151    fieldName,
 152    defaultFieldName,
 153    cachedFieldName);
 154    }
 155   
 156  1094 private void extendCleanupAfterRender(EnhancementOperation op, String parameterName,
 157    String propertyName, Class propertyType, String fieldName, String defaultFieldName,
 158    String cachedFieldName)
 159    {
 160  1094 BodyBuilder cleanupBody = new BodyBuilder();
 161   
 162    // Cached is only set when the field is updated in the accessor or mutator.
 163    // After rendering, we want to clear the cached value and cached flag
 164    // unless the binding is invariant, in which case it can stick around
 165    // for some future render.
 166   
 167  1094 String bindingName = propertyName + "Binding";
 168   
 169  1094 addBindingReference(cleanupBody, bindingName, parameterName);
 170   
 171  1094 cleanupBody.addln("if ({0} && ! {1}.isInvariant())", cachedFieldName, bindingName);
 172  1094 cleanupBody.begin();
 173  1094 cleanupBody.addln("{0} = false;", cachedFieldName);
 174  1094 cleanupBody.addln("{0} = {1};", fieldName, defaultFieldName);
 175  1094 cleanupBody.end();
 176   
 177  1094 op.extendMethodImplementation(
 178    IComponent.class,
 179    EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE,
 180    cleanupBody.toString());
 181    }
 182   
 183  3284 private void addBindingReference(BodyBuilder builder, String localVariableName,
 184    String parameterName)
 185    {
 186  3284 builder.addln(
 187    "{0} {1} = getBinding(\"{2}\");",
 188    IBinding.class.getName(),
 189    localVariableName,
 190    parameterName);
 191    }
 192   
 193  1094 private void buildMutator(EnhancementOperation op, String parameterName, String propertyName,
 194    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName)
 195    {
 196  1094 BodyBuilder builder = new BodyBuilder();
 197  1094 builder.begin();
 198   
 199    // The mutator method may be invoked from finishLoad(), in which
 200    // case it changes the default value for the parameter property, if the parameter
 201    // is not bound.
 202   
 203  1094 builder.addln("if (! isInActiveState())");
 204  1094 builder.begin();
 205  1094 builder.addln("{0} = $1;", defaultFieldName);
 206  1094 builder.addln("return;");
 207  1094 builder.end();
 208   
 209    // In the normal state, we update the binding firstm, and it's an error
 210    // if the parameter is not bound.
 211   
 212  1094 addBindingReference(builder, "binding", parameterName);
 213   
 214  1094 builder.addln("if (binding == null)");
 215  1094 builder.addln(
 216    " throw new {0}(\"Parameter ''{1}'' is not bound and can not be updated.\");",
 217    ApplicationRuntimeException.class.getName(),
 218    parameterName);
 219   
 220    // Always updated the binding first (which may fail with an exception).
 221   
 222  1094 builder.addln("binding.setObject(($w) $1);");
 223   
 224    // While rendering, we store the updated value for fast
 225    // access again (while the component is still rendering).
 226    // The property value will be reset to default by cleanupAfterRender().
 227   
 228  1094 builder.addln("if (isRendering())");
 229  1094 builder.begin();
 230  1094 builder.addln("{0} = $1;", fieldName);
 231  1094 builder.addln("{0} = true;", cachedFieldName);
 232  1094 builder.end();
 233   
 234  1094 builder.end();
 235   
 236  1094 String mutatorMethodName = EnhanceUtils.createMutatorMethodName(propertyName);
 237   
 238  1094 op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, mutatorMethodName,
 239    new Class[]
 240    { propertyType }, null), builder.toString());
 241    }
 242   
 243    // Package private for testing
 244   
 245  1096 void buildAccessor(EnhancementOperation op, String parameterName, String propertyName,
 246    Class propertyType, String fieldName, String defaultFieldName, String cachedFieldName,
 247    boolean cache)
 248    {
 249  1096 BodyBuilder builder = new BodyBuilder();
 250  1096 builder.begin();
 251   
 252  1096 builder.addln("if ({0}) return {1};", cachedFieldName, fieldName);
 253   
 254  1096 addBindingReference(builder, "binding", parameterName);
 255   
 256  1096 builder.addln("if (binding == null) return {0};", defaultFieldName);
 257   
 258  1096 String javaTypeName = ClassFabUtils.getJavaClassName(propertyType);
 259   
 260  1096 builder.addln("{0} result = {1};", javaTypeName, EnhanceUtils.createUnwrapExpression(
 261    op,
 262    "binding",
 263    propertyType));
 264   
 265    // Values read via the binding are cached during the render of
 266    // the component (if the parameter defines cache to be true, which
 267    // is the default), or any time the binding is invariant
 268    // (such as most bindings besides ExpressionBinding.
 269   
 270  1096 String expression = cache ? "isRendering() || binding.isInvariant()"
 271    : "binding.isInvariant()";
 272   
 273  1096 builder.addln("if ({0})", expression);
 274  1096 builder.begin();
 275  1096 builder.addln("{0} = result;", fieldName);
 276  1096 builder.addln("{0} = true;", cachedFieldName);
 277  1096 builder.end();
 278   
 279  1096 builder.addln("return result;");
 280   
 281  1096 builder.end();
 282   
 283  1096 String accessorMethodName = op.getAccessorMethodName(propertyName);
 284   
 285  1096 op.addMethod(Modifier.PUBLIC, new MethodSignature(propertyType, accessorMethodName, null,
 286    null), builder.toString());
 287    }
 288   
 289  42 public void setErrorLog(ErrorLog errorLog)
 290    {
 291  42 _errorLog = errorLog;
 292    }
 293    }