Clover coverage report - picocontainer - 1.2-beta-1
Coverage timestamp: Sun May 29 2005 14:29:04 BST
file stats: LOC: 344   Methods: 15
NCLOC: 198   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CollectionComponentParameter.java 92.9% 98% 100% 96.5%
coverage coverage
 1    /*****************************************************************************
 2    * Copyright (C) PicoContainer Organization. All rights reserved. *
 3    * ------------------------------------------------------------------------- *
 4    * The software in this package is published under the terms of the BSD *
 5    * style license a copy of which has been included with this distribution in *
 6    * the LICENSE.txt file. *
 7    * *
 8    * Original code by *
 9    *****************************************************************************/
 10    package org.picocontainer.defaults;
 11   
 12    import org.picocontainer.ComponentAdapter;
 13    import org.picocontainer.Parameter;
 14    import org.picocontainer.PicoContainer;
 15    import org.picocontainer.PicoInitializationException;
 16    import org.picocontainer.PicoInstantiationException;
 17    import org.picocontainer.PicoIntrospectionException;
 18    import org.picocontainer.PicoVisitor;
 19   
 20    import java.io.Serializable;
 21    import java.lang.reflect.Array;
 22    import java.util.ArrayList;
 23    import java.util.Collection;
 24    import java.util.HashMap;
 25    import java.util.HashSet;
 26    import java.util.Iterator;
 27    import java.util.List;
 28    import java.util.Map;
 29    import java.util.Set;
 30    import java.util.SortedMap;
 31    import java.util.SortedSet;
 32    import java.util.TreeMap;
 33    import java.util.TreeSet;
 34   
 35   
 36    /**
 37    * A CollectionComponentParameter should be used to support inject an {@link Array}, a
 38    * {@link Collection}or {@link Map}of components automatically. The collection will contain
 39    * all components of a special type and additionally the type of the key may be specified. In
 40    * case of a map, the map's keys are the one of the component adapter.
 41    *
 42    * @author Aslak Hellesøy
 43    * @author Jörg Schaible
 44    * @since 1.1
 45    */
 46    public class CollectionComponentParameter
 47    implements Parameter, Serializable {
 48   
 49    /**
 50    * Use <code>ARRAY</code> as {@link Parameter}for an Array that must have elements.
 51    */
 52    public static final CollectionComponentParameter ARRAY = new CollectionComponentParameter();
 53    /**
 54    * Use <code>ARRAY_ALLOW_EMPTY</code> as {@link Parameter}for an Array that may have no
 55    * elements.
 56    */
 57    public static final CollectionComponentParameter ARRAY_ALLOW_EMPTY = new CollectionComponentParameter(true);
 58   
 59    private final boolean emptyCollection;
 60    private final Class componentKeyType;
 61    private final Class componentValueType;
 62   
 63    /**
 64    * Expect an {@link Array}of an appropriate type as parameter. At least one component of
 65    * the array's component type must exist.
 66    */
 67  74 public CollectionComponentParameter() {
 68  74 this(false);
 69    }
 70   
 71    /**
 72    * Expect an {@link Array}of an appropriate type as parameter.
 73    *
 74    * @param emptyCollection <code>true</code> if an empty array also is a valid dependency
 75    * resolution.
 76    */
 77  144 public CollectionComponentParameter(boolean emptyCollection) {
 78  144 this(Void.TYPE, emptyCollection);
 79    }
 80   
 81    /**
 82    * Expect any of the collection types {@link Array},{@link Collection}or {@link Map}as
 83    * parameter.
 84    *
 85    * @param componentValueType the type of the components (ignored in case of an Array)
 86    * @param emptyCollection <code>true</code> if an empty collection resolves the
 87    * dependency.
 88    */
 89  192 public CollectionComponentParameter(Class componentValueType, boolean emptyCollection) {
 90  192 this(Object.class, componentValueType, emptyCollection);
 91    }
 92   
 93    /**
 94    * Expect any of the collection types {@link Array},{@link Collection}or {@link Map}as
 95    * parameter.
 96    *
 97    * @param componentKeyType the type of the component's key
 98    * @param componentValueType the type of the components (ignored in case of an Array)
 99    * @param emptyCollection <code>true</code> if an empty collection resolves the
 100    * dependency.
 101    */
 102  196 public CollectionComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
 103  196 this.emptyCollection = emptyCollection;
 104  196 this.componentKeyType = componentKeyType;
 105  196 this.componentValueType = componentValueType;
 106    }
 107   
 108    /**
 109    * Resolve the parameter for the expected type. The method will return <code>null</code>
 110    * If the expected type is not one of the collection types {@link Array},
 111    * {@link Collection}or {@link Map}. An empty collection is only a valid resolution, if
 112    * the <code>emptyCollection</code> flag was set.
 113    *
 114    * @param container {@inheritDoc}
 115    * @param adapter {@inheritDoc}
 116    * @param expectedType {@inheritDoc}
 117    * @return the instance of the collection type or <code>null</code>
 118    * @throws PicoInstantiationException {@inheritDoc}
 119    */
 120  104 public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType)
 121    throws PicoInstantiationException {
 122    // type check is done in isResolvable
 123  104 Object result = null;
 124  104 final Class collectionType = getCollectionType(expectedType);
 125  104 if (collectionType != null) {
 126  98 final Map adapterMap = getMatchingComponentAdapters(container, adapter, componentKeyType, getValueType(expectedType));
 127  98 if (Array.class.isAssignableFrom(collectionType)) {
 128  42 result = getArrayInstance(container, expectedType, adapterMap);
 129  56 } else if (Map.class.isAssignableFrom(collectionType)) {
 130  26 result = getMapInstance(container, expectedType, adapterMap);
 131  30 } else if (Collection.class.isAssignableFrom(collectionType)) {
 132  30 result = getCollectionInstance(container, expectedType, adapterMap);
 133    } else {
 134  0 throw new PicoIntrospectionException(expectedType.getName() + " is not a collective type");
 135    }
 136    }
 137  104 return result;
 138    }
 139   
 140    /**
 141    * Check for a successful dependency resolution of the parameter for the expected type. The
 142    * dependency can only be satisfied if the expected type is one of the collection types
 143    * {@link Array},{@link Collection}or {@link Map}. An empty collection is only a valid
 144    * resolution, if the <code>emptyCollection</code> flag was set.
 145    *
 146    * @param container {@inheritDoc}
 147    * @param adapter {@inheritDoc}
 148    * @param expectedType {@inheritDoc}
 149    * @return <code>true</code> if matching components were found or an empty collective type
 150    * is allowed
 151    */
 152  1146 public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
 153  1146 final Class collectionType = getCollectionType(expectedType);
 154  1146 final Class valueType = getValueType(expectedType);
 155  1146 return collectionType != null && (emptyCollection || getMatchingComponentAdapters(container, adapter, componentKeyType, valueType).size() > 0);
 156    }
 157   
 158    /**
 159    * Verify a successful dependency resolution of the parameter for the expected type. The
 160    * method will only return if the expected type is one of the collection types {@link Array},
 161    * {@link Collection}or {@link Map}. An empty collection is only a valid resolution, if
 162    * the <code>emptyCollection</code> flag was set.
 163    *
 164    * @param container {@inheritDoc}
 165    * @param adapter {@inheritDoc}
 166    * @param expectedType {@inheritDoc}
 167    * @throws PicoIntrospectionException {@inheritDoc}
 168    */
 169  10 public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType) throws PicoIntrospectionException {
 170  10 final Class collectionType = getCollectionType(expectedType);
 171  10 if (collectionType != null) {
 172  10 final Class valueType = getValueType(expectedType);
 173  10 final Collection componentAdapters = getMatchingComponentAdapters(container, adapter, componentKeyType, valueType).values();
 174  10 if (componentAdapters.isEmpty()) {
 175  4 if (!emptyCollection) {
 176  2 throw new PicoIntrospectionException(expectedType.getName()
 177    + " not resolvable, no components of type "
 178    + getValueType(expectedType).getName()
 179    + " available");
 180    }
 181    } else {
 182  6 for (final Iterator iter = componentAdapters.iterator(); iter.hasNext();) {
 183  8 final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 184  8 componentAdapter.verify(container);
 185    }
 186    }
 187    } else {
 188  0 throw new PicoIntrospectionException(expectedType.getName() + " is not a collective type");
 189    }
 190  8 return;
 191    }
 192   
 193    /**
 194    * Visit the current {@link Parameter}.
 195    *
 196    * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
 197    */
 198  12 public void accept(final PicoVisitor visitor) {
 199  12 visitor.visitParameter(this);
 200    }
 201   
 202    /**
 203    * Evaluate whether the given component adapter will be part of the collective type.
 204    *
 205    * @param adapter a <code>ComponentAdapter</code> value
 206    * @return <code>true</code> if the adapter takes part
 207    */
 208  284 protected boolean evaluate(final ComponentAdapter adapter) {
 209  284 return adapter != null; // use parameter, prevent compiler warning
 210    }
 211   
 212    /**
 213    * Collect the matching ComponentAdapter instances.
 214    * @param container container to use for dependency resolution
 215    * @param adapter {@link ComponentAdapter} to exclude
 216    * @param keyType the compatible type of the key
 217    * @param valueType the compatible type of the component
 218    * @return a {@link Map} with the ComponentAdapter instances and their component keys as map key.
 219    */
 220  874 protected Map getMatchingComponentAdapters(PicoContainer container, ComponentAdapter adapter, Class keyType, Class valueType) {
 221  874 final Map adapterMap = new HashMap();
 222  874 final PicoContainer parent = container.getParent();
 223  874 if (parent != null) {
 224  26 adapterMap.putAll(getMatchingComponentAdapters(parent, adapter, keyType, valueType));
 225    }
 226  874 final Collection allAdapters = container.getComponentAdapters();
 227  874 for (final Iterator iter = allAdapters.iterator(); iter.hasNext();) {
 228  1904 final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 229  1904 adapterMap.remove(componentAdapter.getComponentKey());
 230    }
 231  874 final List adapterList = container.getComponentAdaptersOfType(valueType);
 232  874 for (final Iterator iter = adapterList.iterator(); iter.hasNext();) {
 233  320 final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 234  320 final Object key = componentAdapter.getComponentKey();
 235  320 if (adapter != null && key.equals(adapter.getComponentKey())) {
 236  4 continue;
 237    }
 238  316 if (keyType.isAssignableFrom(key.getClass()) && evaluate(componentAdapter)) {
 239  300 adapterMap.put(key, componentAdapter);
 240    }
 241    }
 242  874 return adapterMap;
 243    }
 244   
 245  1260 private Class getCollectionType(final Class collectionType) {
 246  1260 Class collectionClass = null;
 247  1260 if (collectionType.isArray()) {
 248  720 collectionClass = Array.class;
 249  540 } else if (Map.class.isAssignableFrom(collectionType)) {
 250  76 collectionClass = Map.class;
 251  464 } else if (Collection.class.isAssignableFrom(collectionType)) {
 252  74 collectionClass = Collection.class;
 253    }
 254  1260 return collectionClass;
 255    }
 256   
 257  1256 private Class getValueType(final Class collectionType) {
 258  1256 Class valueType = componentValueType;
 259  1256 if (collectionType.isArray()) {
 260  722 valueType = collectionType.getComponentType();
 261    }
 262  1256 return valueType;
 263    }
 264   
 265  42 private Object[] getArrayInstance(final PicoContainer container, final Class expectedType, final Map adapterList) {
 266  42 final Object[] result = (Object[]) Array.newInstance(expectedType.getComponentType(), adapterList.size());
 267  42 int i = 0;
 268  42 for (final Iterator iterator = adapterList.values().iterator(); iterator.hasNext();) {
 269  56 final ComponentAdapter componentAdapter = (ComponentAdapter) iterator.next();
 270  56 result[i] = container.getComponentInstance(componentAdapter.getComponentKey());
 271  56 i++;
 272    }
 273  42 return result;
 274    }
 275   
 276  30 private Collection getCollectionInstance(final PicoContainer container, final Class expectedType, final Map adapterList) {
 277  30 Class collectionType = expectedType;
 278  30 if (collectionType.isInterface()) {
 279    // The order of tests are significant. The least generic types last.
 280  22 if (List.class.isAssignableFrom(collectionType)) {
 281  2 collectionType = ArrayList.class;
 282    // } else if (BlockingQueue.class.isAssignableFrom(collectionType)) {
 283    // collectionType = ArrayBlockingQueue.class;
 284    // } else if (Queue.class.isAssignableFrom(collectionType)) {
 285    // collectionType = LinkedList.class;
 286  20 } else if (SortedSet.class.isAssignableFrom(collectionType)) {
 287  2 collectionType = TreeSet.class;
 288  18 } else if (Set.class.isAssignableFrom(collectionType)) {
 289  2 collectionType = HashSet.class;
 290  16 } else if (Collection.class.isAssignableFrom(collectionType)) {
 291  16 collectionType = ArrayList.class;
 292    }
 293    }
 294  30 try {
 295  30 Collection result = (Collection) collectionType.newInstance();
 296  30 for (final Iterator iterator = adapterList.values().iterator(); iterator.hasNext();) {
 297  34 final ComponentAdapter componentAdapter = (ComponentAdapter) iterator.next();
 298  34 result.add(container.getComponentInstance(componentAdapter.getComponentKey()));
 299    }
 300  30 return result;
 301    } catch (InstantiationException e) {
 302    ///CLOVER:OFF
 303    throw new PicoInitializationException(e);
 304    ///CLOVER:ON
 305    } catch (IllegalAccessException e) {
 306    ///CLOVER:OFF
 307    throw new PicoInitializationException(e);
 308    ///CLOVER:ON
 309    }
 310    }
 311   
 312  26 private Map getMapInstance(final PicoContainer container, final Class expectedType, final Map adapterList) {
 313  26 Class collectionType = expectedType;
 314  26 if (collectionType.isInterface()) {
 315    // The order of tests are significant. The least generic types last.
 316  16 if (SortedMap.class.isAssignableFrom(collectionType)) {
 317  2 collectionType = TreeMap.class;
 318    // } else if (ConcurrentMap.class.isAssignableFrom(collectionType)) {
 319    // collectionType = ConcurrentHashMap.class;
 320  14 } else if (Map.class.isAssignableFrom(collectionType)) {
 321  14 collectionType = HashMap.class;
 322    }
 323    }
 324  26 try {
 325  26 Map result = (Map) collectionType.newInstance();
 326  26 for (final Iterator iterator = adapterList.entrySet().iterator(); iterator.hasNext();) {
 327  48 final Map.Entry entry = (Map.Entry) iterator.next();
 328  48 final Object key = entry.getKey();
 329  48 final ComponentAdapter componentAdapter = (ComponentAdapter) entry.getValue();
 330  48 result.put(key, container.getComponentInstance(key));
 331    }
 332  26 return result;
 333    } catch (InstantiationException e) {
 334    ///CLOVER:OFF
 335    throw new PicoInitializationException(e);
 336    ///CLOVER:ON
 337    } catch (IllegalAccessException e) {
 338    ///CLOVER:OFF
 339    throw new PicoInitializationException(e);
 340    ///CLOVER:ON
 341    }
 342    }
 343   
 344    }