|
|||||||||||||||||||
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% |
|
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 |
|
|