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 java.lang.annotation.Annotation; 016 import java.lang.reflect.AccessibleObject; 017 import java.lang.reflect.Type; 018 import org.picocontainer.ComponentAdapter; 019 import org.picocontainer.ComponentMonitor; 020 import org.picocontainer.LifecycleStrategy; 021 import org.picocontainer.Parameter; 022 import org.picocontainer.PicoCompositionException; 023 import org.picocontainer.PicoContainer; 024 import org.picocontainer.annotations.Bind; 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); 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 = currentParameter.resolve(container, this, injecteeAdapter, parameterType, expectedNameBinding, useNames(), binding).resolveInstance(); 077 nullCheck(member, i, expectedNameBinding, result); 078 return result; 079 } 080 081 @SuppressWarnings("synthetic-access") 082 protected void nullCheck(AccessibleObject member, int i, ParameterNameBinding expectedNameBinding, Object result) { 083 if (result == null && !isNullParamAllowed(member, i)) { 084 throw new ParameterCannotBeNullException(i, member, expectedNameBinding.getName()); 085 } 086 } 087 088 /** 089 * Checks to see if a null parameter is allowed in the given 090 * constructor/field/method. The default version allows null 091 * if the target object is not a primitive type. 092 * @param member constructor method or field 093 * @param i parameter #. 094 * @return true if the null parameter might be allowed. 095 */ 096 protected boolean isNullParamAllowed(AccessibleObject member, int i) { 097 return !(isPrimitiveArgument(member, i)); 098 } 099 100 protected Annotation[] getBindings(Annotation[][] annotationss) { 101 Annotation[] retVal = new Annotation[annotationss.length]; 102 for (int i = 0; i < annotationss.length; i++) { 103 Annotation[] annotations = annotationss[i]; 104 for (Annotation annotation : annotations) { 105 if (annotation.annotationType().getAnnotation(Bind.class) != null) { 106 retVal[i] = annotation; 107 break; 108 } 109 } 110 } 111 return retVal; 112 } 113 114 public static class ParameterCannotBeNullException extends PicoCompositionException { 115 private final String name; 116 private ParameterCannotBeNullException(int ix, AccessibleObject member, String name) { 117 super("Parameter " + ix + " of '" + member + "' named '" + name + "' cannot be null"); 118 this.name = name; 119 } 120 public String getParameterName() { 121 return name; 122 } 123 } 124 125 }