PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/picocontainer-2.10.2/org/picocontainer/parameters/CollectionComponentParameter.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 388 lines | 244 code | 28 blank | 116 comment | 55 complexity | 315aac126e083d886eaaa0d07c16e36e MD5 | raw file
  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.parameters;
  11. import org.picocontainer.ComponentAdapter;
  12. import org.picocontainer.Parameter;
  13. import org.picocontainer.NameBinding;
  14. import org.picocontainer.PicoContainer;
  15. import org.picocontainer.PicoCompositionException;
  16. import org.picocontainer.PicoVisitor;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Array;
  19. import java.lang.reflect.Type;
  20. import java.lang.reflect.ParameterizedType;
  21. import java.lang.annotation.Annotation;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.HashMap;
  25. import java.util.HashSet;
  26. import java.util.LinkedHashMap;
  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. * A CollectionComponentParameter should be used to support inject an {@link Array}, a
  36. * {@link Collection}or {@link Map}of components automatically. The collection will contain
  37. * all components of a special type and additionally the type of the key may be specified. In
  38. * case of a map, the map's keys are the one of the component adapter.
  39. *
  40. * @author Aslak Hellesøy
  41. * @author Jörg Schaible
  42. */
  43. @SuppressWarnings("serial")
  44. public class CollectionComponentParameter extends AbstractParameter implements Parameter, Serializable {
  45. /**
  46. * Use <code>ARRAY</code> as {@link Parameter}for an Array that must have elements.
  47. */
  48. public static final CollectionComponentParameter ARRAY = new CollectionComponentParameter();
  49. /**
  50. * Use <code>ARRAY_ALLOW_EMPTY</code> as {@link Parameter}for an Array that may have no
  51. * elements.
  52. */
  53. public static final CollectionComponentParameter ARRAY_ALLOW_EMPTY = new CollectionComponentParameter(true);
  54. private final boolean emptyCollection;
  55. private final Class componentKeyType;
  56. private final Class componentValueType;
  57. /**
  58. * Expect an {@link Array}of an appropriate type as parameter. At least one component of
  59. * the array's component type must exist.
  60. */
  61. public CollectionComponentParameter() {
  62. this(false);
  63. }
  64. /**
  65. * Expect an {@link Array}of an appropriate type as parameter.
  66. *
  67. * @param emptyCollection <code>true</code> if an empty array also is a valid dependency
  68. * resolution.
  69. */
  70. public CollectionComponentParameter(boolean emptyCollection) {
  71. this(Void.TYPE, emptyCollection);
  72. }
  73. /**
  74. * Expect any of the collection types {@link Array},{@link Collection}or {@link Map}as
  75. * parameter.
  76. *
  77. * @param componentValueType the type of the components (ignored in case of an Array)
  78. * @param emptyCollection <code>true</code> if an empty collection resolves the
  79. * dependency.
  80. */
  81. public CollectionComponentParameter(Class componentValueType, boolean emptyCollection) {
  82. this(Object.class, componentValueType, emptyCollection);
  83. }
  84. /**
  85. * Expect any of the collection types {@link Array},{@link Collection}or {@link Map}as
  86. * parameter.
  87. *
  88. * @param componentKeyType the type of the component's key
  89. * @param componentValueType the type of the components (ignored in case of an Array)
  90. * @param emptyCollection <code>true</code> if an empty collection resolves the
  91. * dependency.
  92. */
  93. public CollectionComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
  94. this.emptyCollection = emptyCollection;
  95. this.componentKeyType = componentKeyType;
  96. this.componentValueType = componentValueType;
  97. }
  98. /**
  99. * Check for a successful dependency resolution of the parameter for the expected type. The
  100. * dependency can only be satisfied if the expected type is one of the collection types
  101. * {@link Array},{@link Collection}or {@link Map}. An empty collection is only a valid
  102. * resolution, if the <code>emptyCollection</code> flag was set.
  103. *
  104. * @param container {@inheritDoc}
  105. * @param injecteeAdapter
  106. *@param expectedType {@inheritDoc}
  107. * @param expectedNameBinding {@inheritDoc}
  108. * @param useNames
  109. * @param binding @return <code>true</code> if matching components were found or an empty collective type
  110. * is allowed
  111. */
  112. public Resolver resolve(final PicoContainer container, final ComponentAdapter<?> forAdapter,
  113. ComponentAdapter<?> injecteeAdapter, final Type expectedType, final NameBinding expectedNameBinding,
  114. final boolean useNames, Annotation binding) {
  115. final Class collectionType = getCollectionType(expectedType);
  116. if (collectionType != null) {
  117. final Map<Object, ComponentAdapter<?>> componentAdapters = getMatchingComponentAdapters(container, forAdapter,
  118. componentKeyType, getValueType(expectedType));
  119. return new Resolver() {
  120. public boolean isResolved() {
  121. return emptyCollection || componentAdapters.size() > 0;
  122. }
  123. public Object resolveInstance() {
  124. Object result = null;
  125. if (collectionType.isArray()) {
  126. result = getArrayInstance(container, collectionType, componentAdapters);
  127. } else if (Map.class.isAssignableFrom(collectionType)) {
  128. result = getMapInstance(container, collectionType, componentAdapters);
  129. } else if (Collection.class.isAssignableFrom(collectionType)) {
  130. result = getCollectionInstance(container, (Class<? extends Collection>) collectionType,
  131. componentAdapters, expectedNameBinding, useNames);
  132. } else {
  133. throw new PicoCompositionException(expectedType + " is not a collective type");
  134. }
  135. return result;
  136. }
  137. public ComponentAdapter<?> getComponentAdapter() {
  138. return null;
  139. }
  140. };
  141. }
  142. return new Parameter.NotResolved();
  143. }
  144. private Class getCollectionType(Type expectedType) {
  145. if (expectedType instanceof Class) {
  146. return getCollectionType((Class) expectedType);
  147. } else if (expectedType instanceof ParameterizedType) {
  148. ParameterizedType type = (ParameterizedType) expectedType;
  149. return getCollectionType(type.getRawType());
  150. }
  151. throw new IllegalArgumentException("Unable to get collection type from " + expectedType);
  152. }
  153. /**
  154. * Verify a successful dependency resolution of the parameter for the expected type. The
  155. * method will only return if the expected type is one of the collection types {@link Array},
  156. * {@link Collection}or {@link Map}. An empty collection is only a valid resolution, if
  157. * the <code>emptyCollection</code> flag was set.
  158. *
  159. * @param container {@inheritDoc}
  160. * @param adapter {@inheritDoc}
  161. * @param expectedType {@inheritDoc}
  162. * @param expectedNameBinding {@inheritDoc}
  163. * @param useNames
  164. * @param binding
  165. * @throws PicoCompositionException {@inheritDoc}
  166. */
  167. public void verify(PicoContainer container,
  168. ComponentAdapter<?> adapter,
  169. Type expectedType,
  170. NameBinding expectedNameBinding, boolean useNames, Annotation binding) {
  171. final Class collectionType = getCollectionType(expectedType);
  172. if (collectionType != null) {
  173. final Class valueType = getValueType(expectedType);
  174. final Collection componentAdapters =
  175. getMatchingComponentAdapters(container, adapter, componentKeyType, valueType).values();
  176. if (componentAdapters.isEmpty()) {
  177. if (!emptyCollection) {
  178. throw new PicoCompositionException(expectedType
  179. + " not resolvable, no components of type "
  180. + valueType.getName()
  181. + " available");
  182. }
  183. } else {
  184. for (Object componentAdapter1 : componentAdapters) {
  185. final ComponentAdapter componentAdapter = (ComponentAdapter) componentAdapter1;
  186. componentAdapter.verify(container);
  187. }
  188. }
  189. } else {
  190. throw new PicoCompositionException(expectedType + " is not a collective type");
  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. * Evaluate whether the given component adapter will be part of the collective type.
  203. *
  204. * @param adapter a <code>ComponentAdapter</code> value
  205. * @return <code>true</code> if the adapter takes part
  206. */
  207. protected boolean evaluate(final ComponentAdapter adapter) {
  208. return adapter != null; // use parameter, prevent compiler warning
  209. }
  210. /**
  211. * Collect the matching ComponentAdapter instances.
  212. *
  213. * @param container container to use for dependency resolution
  214. * @param adapter {@link ComponentAdapter} to exclude
  215. * @param keyType the compatible type of the key
  216. * @param valueType the compatible type of the addComponent
  217. * @return a {@link Map} with the ComponentAdapter instances and their component keys as map key.
  218. */
  219. @SuppressWarnings({"unchecked"})
  220. protected Map<Object, ComponentAdapter<?>>
  221. getMatchingComponentAdapters(PicoContainer container, ComponentAdapter adapter,
  222. Class keyType, Class valueType) {
  223. final Map<Object, ComponentAdapter<?>> adapterMap = new LinkedHashMap<Object, ComponentAdapter<?>>();
  224. final PicoContainer parent = container.getParent();
  225. if (parent != null) {
  226. adapterMap.putAll(getMatchingComponentAdapters(parent, adapter, keyType, valueType));
  227. }
  228. final Collection<ComponentAdapter<?>> allAdapters = container.getComponentAdapters();
  229. for (ComponentAdapter componentAdapter : allAdapters) {
  230. adapterMap.remove(componentAdapter.getComponentKey());
  231. }
  232. final List<ComponentAdapter> adapterList = List.class.cast(container.getComponentAdapters(valueType));
  233. for (ComponentAdapter componentAdapter : adapterList) {
  234. final Object 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. private Class getCollectionType(final Class collectionType) {
  245. if (collectionType.isArray() ||
  246. Map.class.isAssignableFrom(collectionType) ||
  247. Collection.class.isAssignableFrom(collectionType)) {
  248. return collectionType;
  249. }
  250. return null;
  251. }
  252. private Class getValueType(Type collectionType) {
  253. if (collectionType instanceof Class) {
  254. return getValueType((Class) collectionType);
  255. } else if (collectionType instanceof ParameterizedType) {
  256. return getValueType((ParameterizedType) collectionType); }
  257. throw new IllegalArgumentException("Unable to determine collection type from " + collectionType);
  258. }
  259. private Class getValueType(final Class collectionType) {
  260. Class valueType = componentValueType;
  261. if (collectionType.isArray()) {
  262. valueType = collectionType.getComponentType();
  263. }
  264. return valueType;
  265. }
  266. private Class getValueType(final ParameterizedType collectionType) {
  267. Class valueType = componentValueType;
  268. if (Collection.class.isAssignableFrom((Class<?>) collectionType.getRawType())) {
  269. Type type = collectionType.getActualTypeArguments()[0];
  270. if (type instanceof Class) {
  271. if (((Class)type).isAssignableFrom(valueType)) {
  272. return valueType;
  273. }
  274. valueType = (Class) type;
  275. }
  276. }
  277. return valueType;
  278. }
  279. private Object[] getArrayInstance(final PicoContainer container,
  280. final Class expectedType,
  281. final Map<Object, ComponentAdapter<?>> adapterList) {
  282. final Object[] result = (Object[]) Array.newInstance(expectedType.getComponentType(), adapterList.size());
  283. int i = 0;
  284. for (ComponentAdapter componentAdapter : adapterList.values()) {
  285. result[i] = container.getComponent(componentAdapter.getComponentKey());
  286. i++;
  287. }
  288. return result;
  289. }
  290. @SuppressWarnings({"unchecked"})
  291. private Collection getCollectionInstance(final PicoContainer container,
  292. final Class<? extends Collection> expectedType,
  293. final Map<Object, ComponentAdapter<?>> adapterList, NameBinding expectedNameBinding, boolean useNames) {
  294. Class<? extends Collection> collectionType = expectedType;
  295. if (collectionType.isInterface()) {
  296. // The order of tests are significant. The least generic types last.
  297. if (List.class.isAssignableFrom(collectionType)) {
  298. collectionType = ArrayList.class;
  299. // } else if (BlockingQueue.class.isAssignableFrom(collectionType)) {
  300. // collectionType = ArrayBlockingQueue.class;
  301. // } else if (Queue.class.isAssignableFrom(collectionType)) {
  302. // collectionType = LinkedList.class;
  303. } else if (SortedSet.class.isAssignableFrom(collectionType)) {
  304. collectionType = TreeSet.class;
  305. } else if (Set.class.isAssignableFrom(collectionType)) {
  306. collectionType = HashSet.class;
  307. } else if (Collection.class.isAssignableFrom(collectionType)) {
  308. collectionType = ArrayList.class;
  309. }
  310. }
  311. try {
  312. Collection result = collectionType.newInstance();
  313. for (ComponentAdapter componentAdapter : adapterList.values()) {
  314. if (!useNames || componentAdapter.getComponentKey() == expectedNameBinding)
  315. result.add(container.getComponent(componentAdapter.getComponentKey()));
  316. }
  317. return result;
  318. } catch (InstantiationException e) {
  319. ///CLOVER:OFF
  320. throw new PicoCompositionException(e);
  321. ///CLOVER:ON
  322. } catch (IllegalAccessException e) {
  323. ///CLOVER:OFF
  324. throw new PicoCompositionException(e);
  325. ///CLOVER:ON
  326. }
  327. }
  328. @SuppressWarnings({"unchecked"})
  329. private Map getMapInstance(final PicoContainer container,
  330. final Class<? extends Map> expectedType,
  331. final Map<Object, ComponentAdapter<?>> adapterList) {
  332. Class<? extends Map> collectionType = expectedType;
  333. if (collectionType.isInterface()) {
  334. // The order of tests are significant. The least generic types last.
  335. if (SortedMap.class.isAssignableFrom(collectionType)) {
  336. collectionType = TreeMap.class;
  337. // } else if (ConcurrentMap.class.isAssignableFrom(collectionType)) {
  338. // collectionType = ConcurrentHashMap.class;
  339. } else if (Map.class.isAssignableFrom(collectionType)) {
  340. collectionType = HashMap.class;
  341. }
  342. }
  343. try {
  344. Map result = collectionType.newInstance();
  345. for (Map.Entry<Object, ComponentAdapter<?>> entry : adapterList.entrySet()) {
  346. final Object key = entry.getKey();
  347. result.put(key, container.getComponent(key));
  348. }
  349. return result;
  350. } catch (InstantiationException e) {
  351. ///CLOVER:OFF
  352. throw new PicoCompositionException(e);
  353. ///CLOVER:ON
  354. } catch (IllegalAccessException e) {
  355. ///CLOVER:OFF
  356. throw new PicoCompositionException(e);
  357. ///CLOVER:ON
  358. }
  359. }
  360. }