Clover coverage report - Code Coverage for tapestry release 4.0-beta-12
Coverage timestamp: Sun Oct 30 2005 16:22:01 EST
file stats: LOC: 183   Methods: 5
NCLOC: 98   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Creator.java 100% 96.9% 100% 97.9%
coverage 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.test;
 16   
 17    import java.util.ArrayList;
 18    import java.util.HashMap;
 19    import java.util.Iterator;
 20    import java.util.List;
 21    import java.util.Map;
 22   
 23    import org.apache.hivemind.ApplicationRuntimeException;
 24    import org.apache.hivemind.ClassResolver;
 25    import org.apache.hivemind.Location;
 26    import org.apache.hivemind.Resource;
 27    import org.apache.hivemind.impl.DefaultClassResolver;
 28    import org.apache.hivemind.service.ClassFactory;
 29    import org.apache.hivemind.service.impl.ClassFactoryImpl;
 30    import org.apache.hivemind.util.ClasspathResource;
 31    import org.apache.hivemind.util.PropertyUtils;
 32    import org.apache.tapestry.Tapestry;
 33    import org.apache.tapestry.enhance.AbstractPropertyWorker;
 34    import org.apache.tapestry.enhance.EnhancementOperationImpl;
 35    import org.apache.tapestry.enhance.EnhancementWorker;
 36    import org.apache.tapestry.services.ComponentConstructor;
 37    import org.apache.tapestry.spec.ComponentSpecification;
 38    import org.apache.tapestry.spec.IComponentSpecification;
 39    import org.apache.tapestry.util.DescribedLocation;
 40   
 41    /**
 42    * A utility class that is used to instantiate abstract Tapestry pages and components. It creates,
 43    * at runtime, a subclass where all abstract properties are filled in (each property complete with
 44    * an instance variable, an accessor method and a mutator method). This isn't quite the same as how
 45    * the class is enhanced at runtime (though it does use a subset of the same
 46    * {@link org.apache.tapestry.enhance.EnhancementWorker code}), but is sufficient to unit test the
 47    * class, especially listener methods.
 48    * <p>
 49    * One part of the enhancement is that the
 50    * {@link org.apache.tapestry.IComponent#getSpecification() specification}&nbsp;and
 51    * {@link org.apache.tapestry.IComponent#getMessages() messages}&nbsp;properties of the page or
 52    * component class are converted into read/write properties that can be set via reflection
 53    * (including {@link #newInstance(Class, Map)}.
 54    *
 55    * @author Howard Lewis Ship
 56    * @since 4.0
 57    */
 58    public class Creator
 59    {
 60    /**
 61    * Keyed on Class, value is an {@link ComponentConstructor}.
 62    */
 63    private final Map _constructors = new HashMap();
 64   
 65    private final ClassFactory _classFactory = new ClassFactoryImpl();
 66   
 67    private final ClassResolver _classResolver = new DefaultClassResolver();
 68   
 69    private final List _workers = new ArrayList();
 70   
 71    private final Resource _creatorResource = new ClasspathResource(_classResolver,
 72    "/CreatorLocation");
 73   
 74    private final Location _creatorLocation = new DescribedLocation(_creatorResource,
 75    "Creator Location");
 76   
 77    {
 78    // Overrride AbstractComponent's implementations of
 79    // these two properties (making them read/write).
 80   
 81  158 _workers.add(new CreatePropertyWorker("messages", _creatorLocation));
 82  158 _workers.add(new CreatePropertyWorker("specification", _creatorLocation));
 83   
 84    // Implement any abstract properties.
 85    // Note that we don't bother setting the errorLog property
 86    // so failures may turn into NPEs.
 87   
 88  158 _workers.add(new AbstractPropertyWorker());
 89    }
 90   
 91  167 private ComponentConstructor createComponentConstructor(Class inputClass)
 92    {
 93  167 if (inputClass.isInterface() || inputClass.isPrimitive() || inputClass.isArray())
 94  1 throw new IllegalArgumentException(ScriptMessages.wrongTypeForEnhancement(inputClass));
 95   
 96  166 EnhancementOperationImpl op = new EnhancementOperationImpl(_classResolver,
 97    new ComponentSpecification(), inputClass, _classFactory, null);
 98   
 99  166 IComponentSpecification spec = new ComponentSpecification();
 100  166 spec.setLocation(_creatorLocation);
 101   
 102  166 Iterator i = _workers.iterator();
 103  166 while (i.hasNext())
 104    {
 105  498 EnhancementWorker worker = (EnhancementWorker) i.next();
 106   
 107  498 worker.performEnhancement(op, spec);
 108    }
 109   
 110  166 return op.getConstructor();
 111    }
 112   
 113  168 private ComponentConstructor getComponentConstructor(Class inputClass)
 114    {
 115  168 ComponentConstructor result = (ComponentConstructor) _constructors.get(inputClass);
 116   
 117  168 if (result == null)
 118    {
 119  167 result = createComponentConstructor(inputClass);
 120   
 121  166 _constructors.put(inputClass, result);
 122    }
 123   
 124  167 return result;
 125    }
 126   
 127    /**
 128    * Given a particular abstract class; will create an instance of that class. A subclass is
 129    * created with all abstract properties filled in with ordinary implementations.
 130    */
 131  168 public Object newInstance(Class abstractClass)
 132    {
 133  168 ComponentConstructor constructor = getComponentConstructor(abstractClass);
 134   
 135  167 try
 136    {
 137  167 return constructor.newInstance();
 138    }
 139    catch (Exception ex)
 140    {
 141  0 throw new ApplicationRuntimeException(ScriptMessages.unableToInstantiate(
 142    abstractClass,
 143    ex));
 144    }
 145    }
 146   
 147    /**
 148    * Creates a new instance of a given class, and then initializes properties of the instance. The
 149    * map contains string keys that are property names, and object values.
 150    */
 151  142 public Object newInstance(Class abstractClass, Map properties)
 152    {
 153  142 Object result = newInstance(abstractClass);
 154   
 155  142 if (properties != null)
 156    {
 157  112 Iterator i = properties.entrySet().iterator();
 158   
 159  112 while (i.hasNext())
 160    {
 161  280 Map.Entry e = (Map.Entry) i.next();
 162   
 163  280 String propertyName = (String) e.getKey();
 164   
 165  280 PropertyUtils.write(result, propertyName, e.getValue());
 166    }
 167    }
 168   
 169  142 return result;
 170    }
 171   
 172    /**
 173    * A convienience (useful in test code) for invoking {@link #newInstance(Class, Map)}. The Map
 174    * is constructed from the properties array, which consists of alternating keys and values.
 175    */
 176   
 177  142 public Object newInstance(Class abstractClass, Object[] properties)
 178    {
 179  142 Map propertyMap = Tapestry.convertArrayToMap(properties);
 180   
 181  142 return newInstance(abstractClass, propertyMap);
 182    }
 183    }