001 /*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer.parameters;
011
012 import org.picocontainer.ComponentAdapter;
013 import org.picocontainer.Parameter;
014 import org.picocontainer.PicoContainer;
015 import org.picocontainer.PicoVisitor;
016 import org.picocontainer.NameBinding;
017 import org.picocontainer.injectors.AbstractInjector;
018
019 import java.lang.annotation.Annotation;
020 import java.lang.reflect.ParameterizedType;
021 import java.lang.reflect.Type;
022
023
024 /**
025 * A ComponentParameter should be used to pass in a particular component as argument to a
026 * different component's constructor. This is particularly useful in cases where several
027 * components of the same type have been registered, but with a different key. Passing a
028 * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
029 * about what other component to use in the constructor. Collecting parameter types are
030 * supported for {@link java.lang.reflect.Array},{@link java.util.Collection}and
031 * {@link java.util.Map}.
032 *
033 * @author Jon Tirsén
034 * @author Aslak Hellesøy
035 * @author Jörg Schaible
036 * @author Thomas Heller
037 */
038 @SuppressWarnings("serial")
039 public class ComponentParameter
040 extends BasicComponentParameter {
041
042 /**
043 * <code>DEFAULT</code> is an instance of ComponentParameter using the default constructor.
044 */
045 public static final ComponentParameter DEFAULT = new ComponentParameter();
046 /**
047 * Use <code>ARRAY</code> as {@link Parameter}for an Array that must have elements.
048 */
049 public static final ComponentParameter ARRAY = new ComponentParameter(false);
050 /**
051 * Use <code>ARRAY_ALLOW_EMPTY</code> as {@link Parameter}for an Array that may have no
052 * elements.
053 */
054 public static final ComponentParameter ARRAY_ALLOW_EMPTY = new ComponentParameter(true);
055
056 private final Parameter collectionParameter;
057
058 /**
059 * Expect a parameter matching a component of a specific key.
060 *
061 * @param componentKey the key of the desired addComponent
062 */
063 public ComponentParameter(Object componentKey) {
064 this(componentKey, null);
065 }
066
067 /**
068 * Expect any scalar parameter of the appropriate type or an {@link java.lang.reflect.Array}.
069 */
070 public ComponentParameter() {
071 this(false);
072 }
073
074 /**
075 * Expect any scalar parameter of the appropriate type or an {@link java.lang.reflect.Array}.
076 * Resolve the parameter even if no compnoent is of the array's component type.
077 *
078 * @param emptyCollection <code>true</code> allows an Array to be empty
079 */
080 public ComponentParameter(boolean emptyCollection) {
081 this(null, emptyCollection ? CollectionComponentParameter.ARRAY_ALLOW_EMPTY : CollectionComponentParameter.ARRAY);
082 }
083
084 /**
085 * Expect any scalar parameter of the appropriate type or the collecting type
086 * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
087 * The components in the collection will be of the specified type.
088 *
089 * @param componentValueType the component's type (ignored for an Array)
090 * @param emptyCollection <code>true</code> allows the collection to be empty
091 */
092 public ComponentParameter(Class componentValueType, boolean emptyCollection) {
093 this(null, new CollectionComponentParameter(componentValueType, emptyCollection));
094 }
095
096 /**
097 * Expect any scalar parameter of the appropriate type or the collecting type
098 * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
099 * The components in the collection will be of the specified type and their adapter's key
100 * must have a particular type.
101 *
102 * @param componentKeyType the component adapter's key type
103 * @param componentValueType the component's type (ignored for an Array)
104 * @param emptyCollection <code>true</code> allows the collection to be empty
105 */
106 public ComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
107 this(null, new CollectionComponentParameter(componentKeyType, componentValueType, emptyCollection));
108 }
109
110 private ComponentParameter(Object componentKey, Parameter collectionParameter) {
111 super(componentKey);
112 this.collectionParameter = collectionParameter;
113 }
114
115 public Resolver resolve(final PicoContainer container, final ComponentAdapter<?> forAdapter,
116 final ComponentAdapter<?> injecteeAdapter, final Type expectedType, final NameBinding expectedNameBinding,
117 final boolean useNames, final Annotation binding) {
118
119 return new Resolver() {
120 final Resolver resolver = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding, useNames, binding);
121 public boolean isResolved() {
122 boolean superResolved = resolver.isResolved();
123 if (!superResolved) {
124 if (collectionParameter != null) {
125 return collectionParameter.resolve(container, forAdapter, null, expectedType, expectedNameBinding,
126 useNames, binding).isResolved();
127 }
128 return false;
129 }
130 return superResolved;
131 }
132
133 public Object resolveInstance() {
134 Object result = null;
135 if (expectedType instanceof Class) {
136 result = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding, useNames, binding).resolveInstance();
137 } else if (expectedType instanceof ParameterizedType) {
138 result = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, ((ParameterizedType) expectedType).getRawType(), expectedNameBinding, useNames, binding).resolveInstance();
139 }
140 if (result == null && collectionParameter != null) {
141 result = collectionParameter.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding,
142 useNames, binding).resolveInstance();
143 }
144 return result;
145 }
146
147 public ComponentAdapter<?> getComponentAdapter() {
148 return resolver.getComponentAdapter();
149 }
150 };
151 }
152
153 public void verify(PicoContainer container,
154 ComponentAdapter<?> adapter,
155 Type expectedType,
156 NameBinding expectedNameBinding,
157 boolean useNames, Annotation binding) {
158 try {
159 super.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
160 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
161 if (collectionParameter != null) {
162 collectionParameter.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
163 return;
164 }
165 throw e;
166 }
167 }
168
169 /**
170 * Accept the visitor for the current {@link Parameter}. If internally a
171 * {@link CollectionComponentParameter}is used, it is visited also.
172 *
173 * @see BasicComponentParameter#accept(org.picocontainer.PicoVisitor)
174 */
175 public void accept(PicoVisitor visitor) {
176 super.accept(visitor);
177 if (collectionParameter != null) {
178 collectionParameter.accept(visitor);
179 }
180 }
181 }