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 }