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: *****************************************************************************/package org.picocontainer.parameters;
010:
011: import java.io.File;
012: import java.io.Serializable;
013: import java.lang.reflect.Constructor;
014: import java.lang.reflect.InvocationTargetException;
015: import java.lang.reflect.Method;
016: import java.lang.annotation.Annotation;
017: import java.util.HashMap;
018: import java.util.HashSet;
019: import java.util.List;
020: import java.util.Map;
021: import java.util.Set;
022:
023: import org.picocontainer.ComponentAdapter;
024: import org.picocontainer.Parameter;
025: import org.picocontainer.NameBinding;
026: import org.picocontainer.PicoContainer;
027: import org.picocontainer.PicoVisitor;
028: import org.picocontainer.injectors.AbstractInjector;
029:
030: /**
031: * A BasicComponentParameter should be used to pass in a particular component as argument to a
032: * different component's constructor. This is particularly useful in cases where several
033: * components of the same type have been registered, but with a different key. Passing a
034: * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
035: * about what other component to use in the constructor. This Parameter will never resolve
036: * against a collecting type, that is not directly registered in the PicoContainer itself.
037: *
038: * @author Jon Tirsén
039: * @author Aslak Hellesøy
040: * @author Jörg Schaible
041: * @author Thomas Heller
042: */
043: public class BasicComponentParameter implements Parameter, Serializable {
044:
045: private static interface Converter {
046: Object convert(String paramValue);
047: }
048:
049: private static class ValueOfConverter implements Converter {
050: private Method m;
051:
052: private ValueOfConverter(Class clazz) {
053: try {
054: m = clazz.getMethod("valueOf", String.class);
055: } catch (NoSuchMethodException e) {
056: }
057: }
058:
059: public Object convert(String paramValue) {
060: try {
061: return m.invoke(null, paramValue);
062: } catch (IllegalAccessException e) {
063: } catch (InvocationTargetException e) {
064: }
065: return null;
066:
067: }
068: }
069:
070: private static class NewInstanceConverter implements Converter {
071: private Constructor c;
072:
073: private NewInstanceConverter(Class clazz) {
074: try {
075: c = clazz.getConstructor(String.class);
076: } catch (NoSuchMethodException e) {
077: }
078: }
079:
080: public Object convert(String paramValue) {
081: try {
082: return c.newInstance(paramValue);
083: } catch (IllegalAccessException e) {
084: } catch (InvocationTargetException e) {
085: } catch (InstantiationException e) {
086: }
087: return null;
088: }
089: }
090:
091: /** <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor. */
092: public static final BasicComponentParameter BASIC_DEFAULT = new BasicComponentParameter();
093:
094: private Object componentKey;
095:
096: private static final Map<Class, Converter> stringConverters = new HashMap<Class, Converter>();
097: static {
098: stringConverters.put(Integer.class, new ValueOfConverter(
099: Integer.class));
100: stringConverters.put(Double.class, new ValueOfConverter(
101: Double.class));
102: stringConverters.put(Boolean.class, new ValueOfConverter(
103: Boolean.class));
104: stringConverters.put(Long.class, new ValueOfConverter(
105: Long.class));
106: stringConverters.put(Float.class, new ValueOfConverter(
107: Float.class));
108: stringConverters.put(Character.class, new ValueOfConverter(
109: Character.class));
110: stringConverters.put(Byte.class, new ValueOfConverter(
111: Byte.class));
112: stringConverters.put(Byte.class, new ValueOfConverter(
113: Short.class));
114: stringConverters.put(File.class, new NewInstanceConverter(
115: File.class));
116:
117: }
118:
119: /**
120: * Expect a parameter matching a component of a specific key.
121: *
122: * @param componentKey the key of the desired addComponent
123: */
124: public BasicComponentParameter(Object componentKey) {
125: this .componentKey = componentKey;
126: }
127:
128: /** Expect any paramter of the appropriate type. */
129: public BasicComponentParameter() {
130: }
131:
132: /**
133: * Check wether the given Parameter can be statisfied by the container.
134: *
135: * @return <code>true</code> if the Parameter can be verified.
136: *
137: * @throws org.picocontainer.PicoCompositionException
138: * {@inheritDoc}
139: * @see Parameter#isResolvable(PicoContainer, ComponentAdapter, Class, NameBinding ,boolean, Annotation)
140: */
141: public boolean isResolvable(PicoContainer container,
142: ComponentAdapter adapter, Class expectedType,
143: NameBinding expectedNameBinding, boolean useNames,
144: Annotation binding) {
145: return resolveAdapter(container, adapter,
146: (Class<?>) expectedType, expectedNameBinding, useNames,
147: binding) != null;
148: }
149:
150: public <T> T resolveInstance(PicoContainer container,
151: ComponentAdapter adapter, Class<T> expectedType,
152: NameBinding expectedNameBinding, boolean useNames,
153: Annotation binding) {
154: final ComponentAdapter componentAdapter = resolveAdapter(
155: container, adapter, (Class<?>) expectedType,
156: expectedNameBinding, useNames, binding);
157: if (componentAdapter != null) {
158: Object o = container.getComponent(componentAdapter
159: .getComponentKey());
160: if (o instanceof String && expectedType != String.class) {
161: Converter converter = stringConverters
162: .get(expectedType);
163: return (T) converter.convert((String) o);
164: }
165: return (T) o;
166: }
167: return null;
168: }
169:
170: public void verify(PicoContainer container,
171: ComponentAdapter adapter, Class expectedType,
172: NameBinding expectedNameBinding, boolean useNames,
173: Annotation binding) {
174: final ComponentAdapter componentAdapter = resolveAdapter(
175: container, adapter, (Class<?>) expectedType,
176: expectedNameBinding, useNames, binding);
177: if (componentAdapter == null) {
178: final Set<Class> set = new HashSet<Class>();
179: set.add(expectedType);
180: throw new AbstractInjector.UnsatisfiableDependenciesException(
181: adapter, null, set, container);
182: }
183: componentAdapter.verify(container);
184: }
185:
186: /**
187: * Visit the current {@link Parameter}.
188: *
189: * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
190: */
191: public void accept(final PicoVisitor visitor) {
192: visitor.visitParameter(this );
193: }
194:
195: private <T> ComponentAdapter<T> resolveAdapter(
196: PicoContainer container, ComponentAdapter adapter,
197: Class<T> expectedType, NameBinding expectedNameBinding,
198: boolean useNames, Annotation binding) {
199: Class type = expectedType;
200: if (type.isPrimitive()) {
201: String expectedTypeName = expectedType.getName();
202: if (expectedTypeName == "int") {
203: type = Integer.class;
204: } else if (expectedTypeName == "long") {
205: type = Long.class;
206: } else if (expectedTypeName == "float") {
207: type = Float.class;
208: } else if (expectedTypeName == "double") {
209: type = Double.class;
210: } else if (expectedTypeName == "boolean") {
211: type = Boolean.class;
212: } else if (expectedTypeName == "char") {
213: type = Character.class;
214: } else if (expectedTypeName == "short") {
215: type = Short.class;
216: } else if (expectedTypeName == "byte") {
217: type = Byte.class;
218: }
219: }
220:
221: final ComponentAdapter<T> result = getTargetAdapter(container,
222: type, expectedNameBinding, adapter, useNames, binding);
223: if (result == null) {
224: return null;
225: }
226:
227: if (!type.isAssignableFrom(result.getComponentImplementation())) {
228: if (!(result.getComponentImplementation() == String.class && stringConverters
229: .containsKey(type))) {
230: return null;
231: }
232: }
233: return result;
234: }
235:
236: @SuppressWarnings({"unchecked"})
237: private static <T> ComponentAdapter<T> typeComponentAdapter(
238: ComponentAdapter<?> componentAdapter) {
239: return (ComponentAdapter<T>) componentAdapter;
240: }
241:
242: private <T> ComponentAdapter<T> getTargetAdapter(
243: PicoContainer container, Class<T> expectedType,
244: NameBinding expectedNameBinding,
245: ComponentAdapter excludeAdapter, boolean useNames,
246: Annotation binding) {
247: if (componentKey != null) {
248: // key tells us where to look so we follow
249: return typeComponentAdapter(container
250: .getComponentAdapter(componentKey));
251: } else if (excludeAdapter == null) {
252: return container.getComponentAdapter(expectedType,
253: (NameBinding) null);
254: } else {
255:
256: Object excludeKey = excludeAdapter.getComponentKey();
257: ComponentAdapter byKey = container
258: .getComponentAdapter((Object) expectedType);
259: if (byKey != null
260: && !excludeKey.equals(byKey.getComponentKey())) {
261: return typeComponentAdapter(byKey);
262: }
263: if (useNames) {
264: ComponentAdapter found = container
265: .getComponentAdapter(expectedNameBinding
266: .getName());
267: if ((found != null)
268: && areCompatible(expectedType, found)
269: && found != excludeAdapter) {
270: return (ComponentAdapter<T>) found;
271: }
272: }
273: List<ComponentAdapter<T>> found = binding == null ? container
274: .getComponentAdapters(expectedType)
275: : container.getComponentAdapters(expectedType,
276: binding.annotationType());
277: ComponentAdapter exclude = null;
278: for (ComponentAdapter work : found) {
279: if (work.getComponentKey().equals(excludeKey)) {
280: exclude = work;
281: }
282: }
283: found.remove(exclude);
284: if (found.size() == 0) {
285: if (container.getParent() != null) {
286: return container.getParent().getComponentAdapter(
287: expectedType, expectedNameBinding);
288: } else {
289: return null;
290: }
291: } else if (found.size() == 1) {
292: return found.get(0);
293: } else {
294: Class[] foundClasses = new Class[found.size()];
295: for (int i = 0; i < foundClasses.length; i++) {
296: foundClasses[i] = found.get(i)
297: .getComponentImplementation();
298: }
299: throw new AbstractInjector.AmbiguousComponentResolutionException(
300: expectedType, foundClasses);
301: }
302: }
303: }
304:
305: private <T> boolean areCompatible(Class<T> expectedType,
306: ComponentAdapter found) {
307: Class foundImpl = found.getComponentImplementation();
308: return expectedType.isAssignableFrom(foundImpl)
309: || (foundImpl == String.class && stringConverters
310: .containsKey(expectedType));
311: }
312: }
|