KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > picocontainer > defaults > CollectionComponentParameter


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 JavaDoc;
21 import java.lang.reflect.Array JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.SortedMap JavaDoc;
31 import java.util.SortedSet JavaDoc;
32 import java.util.TreeMap JavaDoc;
33 import java.util.TreeSet JavaDoc;
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 JavaDoc {
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 JavaDoc componentKeyType;
61     private final Class JavaDoc 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     public CollectionComponentParameter() {
68         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     public CollectionComponentParameter(boolean emptyCollection) {
78         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     public CollectionComponentParameter(Class JavaDoc componentValueType, boolean emptyCollection) {
90         this(Object JavaDoc.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     public CollectionComponentParameter(Class JavaDoc componentKeyType, Class JavaDoc componentValueType, boolean emptyCollection) {
103         this.emptyCollection = emptyCollection;
104         this.componentKeyType = componentKeyType;
105         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     public Object JavaDoc resolveInstance(PicoContainer container, ComponentAdapter adapter, Class JavaDoc expectedType)
121             throws PicoInstantiationException {
122         // type check is done in isResolvable
123
Object JavaDoc result = null;
124         final Class JavaDoc collectionType = getCollectionType(expectedType);
125         if (collectionType != null) {
126             final Map JavaDoc adapterMap = getMatchingComponentAdapters(container, adapter, componentKeyType, getValueType(expectedType));
127             if (Array JavaDoc.class.isAssignableFrom(collectionType)) {
128                 result = getArrayInstance(container, expectedType, adapterMap);
129             } else if (Map JavaDoc.class.isAssignableFrom(collectionType)) {
130                 result = getMapInstance(container, expectedType, adapterMap);
131             } else if (Collection JavaDoc.class.isAssignableFrom(collectionType)) {
132                 result = getCollectionInstance(container, expectedType, adapterMap);
133             } else {
134                 throw new PicoIntrospectionException(expectedType.getName() + " is not a collective type");
135             }
136         }
137         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     public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class JavaDoc expectedType) {
153         final Class JavaDoc collectionType = getCollectionType(expectedType);
154         final Class JavaDoc valueType = getValueType(expectedType);
155         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     public void verify(PicoContainer container, ComponentAdapter adapter, Class JavaDoc expectedType) throws PicoIntrospectionException {
170         final Class JavaDoc collectionType = getCollectionType(expectedType);
171         if (collectionType != null) {
172             final Class JavaDoc valueType = getValueType(expectedType);
173             final Collection JavaDoc componentAdapters = getMatchingComponentAdapters(container, adapter, componentKeyType, valueType).values();
174             if (componentAdapters.isEmpty()) {
175                 if (!emptyCollection) {
176                     throw new PicoIntrospectionException(expectedType.getName()
177                             + " not resolvable, no components of type "
178                             + getValueType(expectedType).getName()
179                             + " available");
180                 }
181             } else {
182                 for (final Iterator JavaDoc iter = componentAdapters.iterator(); iter.hasNext();) {
183                     final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
184                     componentAdapter.verify(container);
185                 }
186             }
187         } else {
188             throw new PicoIntrospectionException(expectedType.getName() + " is not a collective type");
189         }
190         return;
191     }
192
193     /**
194      * Visit the current {@link Parameter}.
195      *
196      * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
197      */

198     public void accept(final PicoVisitor visitor) {
199         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     protected boolean evaluate(final ComponentAdapter adapter) {
209         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     protected Map JavaDoc getMatchingComponentAdapters(PicoContainer container, ComponentAdapter adapter, Class JavaDoc keyType, Class JavaDoc valueType) {
221         final Map JavaDoc adapterMap = new HashMap JavaDoc();
222         final PicoContainer parent = container.getParent();
223         if (parent != null) {
224             adapterMap.putAll(getMatchingComponentAdapters(parent, adapter, keyType, valueType));
225         }
226         final Collection JavaDoc allAdapters = container.getComponentAdapters();
227         for (final Iterator JavaDoc iter = allAdapters.iterator(); iter.hasNext();) {
228             final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
229             adapterMap.remove(componentAdapter.getComponentKey());
230         }
231         final List JavaDoc adapterList = container.getComponentAdaptersOfType(valueType);
232         for (final Iterator JavaDoc iter = adapterList.iterator(); iter.hasNext();) {
233             final ComponentAdapter componentAdapter = (ComponentAdapter) iter.next();
234             final Object JavaDoc key = componentAdapter.getComponentKey();
235             if (adapter != null && key.equals(adapter.getComponentKey())) {
236                 continue;
237             }
238             if (keyType.isAssignableFrom(key.getClass()) && evaluate(componentAdapter)) {
239                 adapterMap.put(key, componentAdapter);
240             }
241         }
242         return adapterMap;
243     }
244
245     private Class JavaDoc getCollectionType(final Class JavaDoc collectionType) {
246         Class JavaDoc collectionClass = null;
247         if (collectionType.isArray()) {
248             collectionClass = Array JavaDoc.class;
249         } else if (Map JavaDoc.class.isAssignableFrom(collectionType)) {
250             collectionClass = Map JavaDoc.class;
251         } else if (Collection JavaDoc.class.isAssignableFrom(collectionType)) {
252             collectionClass = Collection JavaDoc.class;
253         }
254         return collectionClass;
255     }
256
257     private Class JavaDoc getValueType(final Class JavaDoc collectionType) {
258         Class JavaDoc valueType = componentValueType;
259         if (collectionType.isArray()) {
260             valueType = collectionType.getComponentType();
261         }
262         return valueType;
263     }
264
265     private Object JavaDoc[] getArrayInstance(final PicoContainer container, final Class JavaDoc expectedType, final Map JavaDoc adapterList) {
266         final Object JavaDoc[] result = (Object JavaDoc[]) Array.newInstance(expectedType.getComponentType(), adapterList.size());
267         int i = 0;
268         for (final Iterator JavaDoc iterator = adapterList.values().iterator(); iterator.hasNext();) {
269             final ComponentAdapter componentAdapter = (ComponentAdapter) iterator.next();
270             result[i] = container.getComponentInstance(componentAdapter.getComponentKey());
271             i++;
272         }
273         return result;
274     }
275
276     private Collection JavaDoc getCollectionInstance(final PicoContainer container, final Class JavaDoc expectedType, final Map JavaDoc adapterList) {
277         Class JavaDoc collectionType = expectedType;
278         if (collectionType.isInterface()) {
279             // The order of tests are significant. The least generic types last.
280
if (List JavaDoc.class.isAssignableFrom(collectionType)) {
281                 collectionType = ArrayList JavaDoc.class;
282 // } else if (BlockingQueue.class.isAssignableFrom(collectionType)) {
283
// collectionType = ArrayBlockingQueue.class;
284
// } else if (Queue.class.isAssignableFrom(collectionType)) {
285
// collectionType = LinkedList.class;
286
} else if (SortedSet JavaDoc.class.isAssignableFrom(collectionType)) {
287                 collectionType = TreeSet JavaDoc.class;
288             } else if (Set JavaDoc.class.isAssignableFrom(collectionType)) {
289                 collectionType = HashSet JavaDoc.class;
290             } else if (Collection JavaDoc.class.isAssignableFrom(collectionType)) {
291                 collectionType = ArrayList JavaDoc.class;
292             }
293         }
294         try {
295             Collection JavaDoc result = (Collection JavaDoc) collectionType.newInstance();
296             for (final Iterator JavaDoc iterator = adapterList.values().iterator(); iterator.hasNext();) {
297                 final ComponentAdapter componentAdapter = (ComponentAdapter) iterator.next();
298                 result.add(container.getComponentInstance(componentAdapter.getComponentKey()));
299             }
300             return result;
301         } catch (InstantiationException JavaDoc e) {
302             ///CLOVER:OFF
303
throw new PicoInitializationException(e);
304             ///CLOVER:ON
305
} catch (IllegalAccessException JavaDoc e) {
306             ///CLOVER:OFF
307
throw new PicoInitializationException(e);
308             ///CLOVER:ON
309
}
310     }
311
312     private Map JavaDoc getMapInstance(final PicoContainer container, final Class JavaDoc expectedType, final Map JavaDoc adapterList) {
313         Class JavaDoc collectionType = expectedType;
314         if (collectionType.isInterface()) {
315             // The order of tests are significant. The least generic types last.
316
if (SortedMap JavaDoc.class.isAssignableFrom(collectionType)) {
317                 collectionType = TreeMap JavaDoc.class;
318 // } else if (ConcurrentMap.class.isAssignableFrom(collectionType)) {
319
// collectionType = ConcurrentHashMap.class;
320
} else if (Map JavaDoc.class.isAssignableFrom(collectionType)) {
321                 collectionType = HashMap JavaDoc.class;
322             }
323         }
324         try {
325             Map JavaDoc result = (Map JavaDoc) collectionType.newInstance();
326             for (final Iterator JavaDoc iterator = adapterList.entrySet().iterator(); iterator.hasNext();) {
327                 final Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
328                 final Object JavaDoc key = entry.getKey();
329                 final ComponentAdapter componentAdapter = (ComponentAdapter) entry.getValue();
330                 result.put(key, container.getComponentInstance(key));
331             }
332             return result;
333         } catch (InstantiationException JavaDoc e) {
334             ///CLOVER:OFF
335
throw new PicoInitializationException(e);
336             ///CLOVER:ON
337
} catch (IllegalAccessException JavaDoc e) {
338             ///CLOVER:OFF
339
throw new PicoInitializationException(e);
340             ///CLOVER:ON
341
}
342     }
343
344 }
345
Popular Tags