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.ParameterName;
017 import org.picocontainer.injectors.AbstractInjector;
018
019
020 /**
021 * A ComponentParameter should be used to pass in a particular component as argument to a
022 * different component's constructor. This is particularly useful in cases where several
023 * components of the same type have been registered, but with a different key. Passing a
024 * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
025 * about what other component to use in the constructor. Collecting parameter types are
026 * supported for {@link java.lang.reflect.Array},{@link java.util.Collection}and
027 * {@link java.util.Map}.
028 *
029 * @author Jon Tirsén
030 * @author Aslak Hellesøy
031 * @author Jörg Schaible
032 * @author Thomas Heller
033 */
034 public class ComponentParameter
035 extends BasicComponentParameter {
036
037 /**
038 * <code>DEFAULT</code> is an instance of ComponentParameter using the default constructor.
039 */
040 public static final ComponentParameter DEFAULT = new ComponentParameter();
041 /**
042 * Use <code>ARRAY</code> as {@link Parameter}for an Array that must have elements.
043 */
044 public static final ComponentParameter ARRAY = new ComponentParameter(false);
045 /**
046 * Use <code>ARRAY_ALLOW_EMPTY</code> as {@link Parameter}for an Array that may have no
047 * elements.
048 */
049 public static final ComponentParameter ARRAY_ALLOW_EMPTY = new ComponentParameter(true);
050
051 private final Parameter collectionParameter;
052
053 /**
054 * Expect a parameter matching a component of a specific key.
055 *
056 * @param componentKey the key of the desired addComponent
057 */
058 public ComponentParameter(Object componentKey) {
059 this(componentKey, null);
060 }
061
062 /**
063 * Expect any scalar paramter of the appropriate type or an {@link java.lang.reflect.Array}.
064 */
065 public ComponentParameter() {
066 this(false);
067 }
068
069 /**
070 * Expect any scalar paramter of the appropriate type or an {@link java.lang.reflect.Array}.
071 * Resolve the parameter even if no compoennt is of the array's component type.
072 *
073 * @param emptyCollection <code>true</code> allows an Array to be empty
074 */
075 public ComponentParameter(boolean emptyCollection) {
076 this(null, emptyCollection ? CollectionComponentParameter.ARRAY_ALLOW_EMPTY : CollectionComponentParameter.ARRAY);
077 }
078
079 /**
080 * Expect any scalar paramter of the appropriate type or the collecting type
081 * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
082 * The components in the collection will be of the specified type.
083 *
084 * @param componentValueType the component's type (ignored for an Array)
085 * @param emptyCollection <code>true</code> allows the collection to be empty
086 */
087 public ComponentParameter(Class componentValueType, boolean emptyCollection) {
088 this(null, new CollectionComponentParameter(componentValueType, emptyCollection));
089 }
090
091 /**
092 * Expect any scalar paramter of the appropriate type or the collecting type
093 * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
094 * The components in the collection will be of the specified type and their adapter's key
095 * must have a particular type.
096 *
097 * @param componentKeyType the component adapter's key type
098 * @param componentValueType the component's type (ignored for an Array)
099 * @param emptyCollection <code>true</code> allows the collection to be empty
100 */
101 public ComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
102 this(null, new CollectionComponentParameter(componentKeyType, componentValueType, emptyCollection));
103 }
104
105 private ComponentParameter(Object componentKey, Parameter collectionParameter) {
106 super(componentKey);
107 this.collectionParameter = collectionParameter;
108 }
109
110 public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType, ParameterName expectedParameterName) {
111 // type check is done in isResolvable
112 Object result = super.resolveInstance(container, adapter, expectedType, expectedParameterName);
113 if (result == null && collectionParameter != null) {
114 result = collectionParameter.resolveInstance(container, adapter, expectedType, expectedParameterName);
115 }
116 return result;
117 }
118
119 public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType, ParameterName expectedParameterName) {
120 if (!super.isResolvable(container, adapter, expectedType, expectedParameterName)) {
121 if (collectionParameter != null) {
122 return collectionParameter.isResolvable(container, adapter, expectedType, expectedParameterName);
123 }
124 return false;
125 }
126 return true;
127 }
128
129 public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType, ParameterName expectedParameterName) {
130 try {
131 super.verify(container, adapter, expectedType, expectedParameterName);
132 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
133 if (collectionParameter != null) {
134 collectionParameter.verify(container, adapter, expectedType, expectedParameterName);
135 return;
136 }
137 throw e;
138 }
139 }
140
141 /**
142 * Accept the visitor for the current {@link Parameter}. If internally a
143 * {@link CollectionComponentParameter}is used, it is visited also.
144 *
145 * @see BasicComponentParameter#accept(org.picocontainer.PicoVisitor)
146 */
147 public void accept(PicoVisitor visitor) {
148 super.accept(visitor);
149 if (collectionParameter != null) {
150 collectionParameter.accept(visitor);
151 }
152 }
153 }