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 *****************************************************************************/
009 package org.picocontainer.injectors;
010
011 import com.thoughtworks.paranamer.AdaptiveParanamer;
012 import com.thoughtworks.paranamer.AnnotationParanamer;
013 import com.thoughtworks.paranamer.CachingParanamer;
014 import com.thoughtworks.paranamer.Paranamer;
015 import org.picocontainer.ComponentAdapter;
016 import org.picocontainer.ComponentMonitor;
017 import org.picocontainer.Parameter;
018 import org.picocontainer.PicoCompositionException;
019 import org.picocontainer.PicoContainer;
020 import org.picocontainer.annotations.Bind;
021
022 import java.lang.annotation.Annotation;
023 import java.lang.reflect.AccessibleObject;
024 import java.lang.reflect.Type;
025
026 import static org.picocontainer.injectors.PrimitiveMemberChecker.isPrimitiveArgument;
027
028 /**
029 * Injection will happen in a single member function on the component.
030 *
031 * @author Paul Hammant
032 *
033 */
034 @SuppressWarnings("serial")
035 public abstract class SingleMemberInjector<T> extends AbstractInjector<T> {
036
037 private transient Paranamer paranamer;
038
039 public SingleMemberInjector(Object componentKey,
040 Class componentImplementation,
041 Parameter[] parameters,
042 ComponentMonitor monitor,
043 boolean useNames) {
044 super(componentKey, componentImplementation, parameters, monitor, useNames);
045 }
046
047 protected Paranamer getParanamer() {
048 if (paranamer == null) {
049 paranamer = new CachingParanamer(new AnnotationParanamer(new AdaptiveParanamer()));
050 }
051 return paranamer;
052 }
053
054 @SuppressWarnings("unchecked")
055 protected Object[] getMemberArguments(PicoContainer container, final AccessibleObject member, final Type[] parameterTypes, final Annotation[] bindings) {
056 boxParameters(parameterTypes);
057 Object[] result = new Object[parameterTypes.length];
058 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes.length);
059
060 for (int i = 0; i < currentParameters.length; i++) {
061 result[i] = getParameter(container, member, i, parameterTypes[i], bindings[i], currentParameters[i], null);
062 }
063
064 return result;
065 }
066
067 protected void boxParameters(Type[] parameterTypes) {
068 for (int i = 0; i < parameterTypes.length; i++) {
069 parameterTypes[i] = box(parameterTypes[i]);
070 }
071 }
072
073 protected Object getParameter(PicoContainer container, AccessibleObject member, int i, Type parameterType, Annotation binding,
074 Parameter currentParameter, ComponentAdapter<?> injecteeAdapter) {
075 ParameterNameBinding expectedNameBinding = new ParameterNameBinding(getParanamer(), member, i);
076 Object result = null;
077 try {
078 result = currentParameter.resolve(container, this, injecteeAdapter, parameterType, expectedNameBinding, useNames(), binding).resolveInstance();
079 } catch (AmbiguousComponentResolutionException e) {
080 e.setMember(member);
081 throw e;
082 }
083 nullCheck(member, i, expectedNameBinding, result);
084 return result;
085 }
086
087 @SuppressWarnings("synthetic-access")
088 protected void nullCheck(AccessibleObject member, int i, ParameterNameBinding expectedNameBinding, Object result) {
089 if (result == null && !isNullParamAllowed(member, i)) {
090 throw new ParameterCannotBeNullException(i, member, expectedNameBinding.getName());
091 }
092 }
093
094 /**
095 * Checks to see if a null parameter is allowed in the given
096 * constructor/field/method. The default version allows null
097 * if the target object is not a primitive type.
098 * @param member constructor method or field
099 * @param i parameter #.
100 * @return true if the null parameter might be allowed.
101 */
102 protected boolean isNullParamAllowed(AccessibleObject member, int i) {
103 return !(isPrimitiveArgument(member, i));
104 }
105
106 protected Annotation[] getBindings(Annotation[][] annotationss) {
107 Annotation[] retVal = new Annotation[annotationss.length];
108 for (int i = 0; i < annotationss.length; i++) {
109 Annotation[] annotations = annotationss[i];
110 for (Annotation annotation : annotations) {
111 if (annotation.annotationType().getAnnotation(Bind.class) != null) {
112 retVal[i] = annotation;
113 break;
114 }
115 }
116 }
117 return retVal;
118 }
119
120 public static class ParameterCannotBeNullException extends PicoCompositionException {
121 private final String name;
122 private ParameterCannotBeNullException(int ix, AccessibleObject member, String name) {
123 super("Parameter " + ix + " of '" + member + "' named '" + name + "' cannot be null");
124 this.name = name;
125 }
126 public String getParameterName() {
127 return name;
128 }
129 }
130
131 }