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