Clover coverage report - PicoContainer - 1.1
Coverage timestamp: Thu Nov 4 2004 11:55:45 CST
file stats: LOC: 345   Methods: 15
NCLOC: 198   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 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  72
     public CollectionComponentParameter() {
 68  72
         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  140
     public CollectionComponentParameter(boolean emptyCollection) {
 78  140
         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  188
     public CollectionComponentParameter(Class componentValueType, boolean emptyCollection) {
 90  188
         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  192
     public CollectionComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
 103  192
         this.emptyCollection = emptyCollection;
 104  192
         this.componentKeyType = componentKeyType;
 105  192
         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  102
     public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType)
 121   
             throws PicoInstantiationException {
 122   
         // type check is done in isResolvable
 123  102
         Object result = null;
 124  102
         final Class collectionType = getCollectionType(expectedType);
 125  102
         if (collectionType != null) {
 126  96
             final Map adapterMap = getMatchingComponentAdapters(container, adapter, componentKeyType, getValueType(expectedType));
 127  96
             if (Array.class.isAssignableFrom(collectionType)) {
 128  40
                 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  102
         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  1138
     public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
 153  1138
         final Class collectionType = getCollectionType(expectedType);
 154  1138
         final Class valueType = getValueType(expectedType);
 155  1138
         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  8
     public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType) throws PicoIntrospectionException {
 170  8
         final Class collectionType = getCollectionType(expectedType);
 171  8
         if (collectionType != null) {
 172  8
             final Class valueType = getValueType(expectedType);
 173  8
             final Collection componentAdapters = getMatchingComponentAdapters(container, adapter, componentKeyType, valueType).values();
 174  8
             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  4
                 for (final Iterator iter = componentAdapters.iterator(); iter.hasNext();) {
 183  4
                     final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 184  4
                     componentAdapter.verify(container);
 185   
                 }
 186   
             }
 187   
         } else {
 188  0
             throw new PicoIntrospectionException(expectedType.getName() + " is not a collective type");
 189   
         }
 190  6
         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  268
     protected boolean evaluate(final ComponentAdapter adapter) {
 209  268
         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  866
     protected Map getMatchingComponentAdapters(PicoContainer container, ComponentAdapter adapter, Class keyType, Class valueType) {
 221  866
         final Map adapterMap = new HashMap();
 222  866
         final PicoContainer parent = container.getParent();
 223  866
         if (parent != null) {
 224  26
             adapterMap.putAll(getMatchingComponentAdapters(parent, adapter, keyType, valueType));
 225   
         }
 226  866
         final Collection allAdapters = container.getComponentAdapters();
 227  866
         for (final Iterator iter = allAdapters.iterator(); iter.hasNext();) {
 228  1880
             final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 229  1880
             adapterMap.remove(componentAdapter.getComponentKey());
 230   
         }
 231  866
         final List adapterList = container.getComponentAdaptersOfType(valueType);
 232  866
         for (final Iterator iter = adapterList.iterator(); iter.hasNext();) {
 233  304
             final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
 234  304
             final Object key = componentAdapter.getComponentKey();
 235  304
             if (adapter != null && key.equals(adapter.getComponentKey())) {
 236  4
                 continue;
 237   
             }
 238  300
             if (keyType.isAssignableFrom(key.getClass()) && evaluate(componentAdapter)) {
 239  284
                 adapterMap.put(key, componentAdapter);
 240   
             }
 241   
         }
 242  866
         return adapterMap;
 243   
     }
 244   
 
 245  1248
     private Class getCollectionType(final Class collectionType) {
 246  1248
         Class collectionClass = null;
 247  1248
         if (collectionType.isArray()) {
 248  712
             collectionClass = Array.class;
 249  536
         } else if (Map.class.isAssignableFrom(collectionType)) {
 250  76
             collectionClass = Map.class;
 251  460
         } else if (Collection.class.isAssignableFrom(collectionType)) {
 252  74
             collectionClass = Collection.class;
 253   
         }
 254  1248
         return collectionClass;
 255   
     }
 256   
 
 257  1244
     private Class getValueType(final Class collectionType) {
 258  1244
         Class valueType = componentValueType;
 259  1244
         if (collectionType.isArray()) {
 260  714
             valueType = collectionType.getComponentType();
 261   
         }
 262  1244
         return valueType;
 263   
     }
 264   
 
 265  40
     private Object[] getArrayInstance(final PicoContainer container, final Class expectedType, final Map adapterList) {
 266  40
         final Object[] result = (Object[]) Array.newInstance(expectedType.getComponentType(), adapterList.size());
 267  40
         int i = 0;
 268  40
         for (final Iterator iterator = adapterList.values().iterator(); iterator.hasNext();) {
 269  52
             final ComponentAdapter componentAdapter = (ComponentAdapter) iterator.next();
 270  52
             result[i] = container.getComponentInstance(componentAdapter.getComponentKey());
 271  52
             i++;
 272   
         }
 273  40
         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   
 }
 345