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