blob: 77e0f043fb313aa7c7e044cd3623b3d3790b3b1d [file] [log] [blame]
crazybobleeb8cf1e52007-02-02 21:48:16 +00001/**
2 * Copyright (C) 2006 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
crazybobleee3adfd62007-02-02 21:30:08 +000016
17package com.google.inject;
18
crazybobleee3adfd62007-02-02 21:30:08 +000019import java.lang.reflect.Constructor;
20import java.lang.reflect.InvocationTargetException;
crazyboblee9a3861b2007-02-20 03:37:31 +000021import com.google.inject.util.StackTraceElements;
crazybobleee3adfd62007-02-02 21:30:08 +000022
23/**
24 * Injects constructors.
25 *
26 * @author crazybob@google.com (Bob Lee)
kevinb9na99dca72007-02-11 04:48:57 +000027 */
crazyboblee0789b192007-02-13 02:43:28 +000028class ConstructorInjector<T> {
crazybobleee3adfd62007-02-02 21:30:08 +000029
30 final Class<T> implementation;
31 final ContainerImpl.Injector[] injectors;
32 final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
33 final ConstructionProxy<T> constructionProxy;
34
crazybobleee3adfd62007-02-02 21:30:08 +000035 ConstructorInjector(ContainerImpl container, Class<T> implementation) {
crazybobleee3adfd62007-02-02 21:30:08 +000036 this.implementation = implementation;
crazyboblee0789b192007-02-13 02:43:28 +000037 Constructor<T> constructor = findConstructorIn(container, implementation);
38 parameterInjectors = createParameterInjector(container, constructor);
crazybobleee3adfd62007-02-02 21:30:08 +000039 injectors = container.injectors.get(implementation)
40 .toArray(new ContainerImpl.Injector[0]);
41 constructionProxy = container.constructionProxyFactory.get(constructor);
42 }
43
crazyboblee0789b192007-02-13 02:43:28 +000044 /**
45 * Used to create an invalid injector.
46 */
47 private ConstructorInjector() {
48 implementation = null;
49 injectors = null;
50 parameterInjectors = null;
51 constructionProxy = null;
52 }
53
crazybobleee3adfd62007-02-02 21:30:08 +000054 ContainerImpl.ParameterInjector<?>[] createParameterInjector(
crazyboblee0789b192007-02-13 02:43:28 +000055 ContainerImpl container, Constructor<T> constructor) {
crazybobleee3adfd62007-02-02 21:30:08 +000056 try {
crazyboblee4602a6f2007-02-15 02:45:18 +000057 return constructor.getParameterTypes().length == 0
crazybobleee3adfd62007-02-02 21:30:08 +000058 ? null // default constructor.
59 : container.getParametersInjectors(
60 constructor,
61 constructor.getParameterAnnotations(),
crazyboblee4602a6f2007-02-15 02:45:18 +000062 constructor.getGenericParameterTypes()
crazybobleee3adfd62007-02-02 21:30:08 +000063 );
kevinb9na99dca72007-02-11 04:48:57 +000064 }
65 catch (ContainerImpl.MissingDependencyException e) {
crazybobleee3adfd62007-02-02 21:30:08 +000066 e.handle(container.errorHandler);
67 return null;
68 }
69 }
70
crazyboblee0789b192007-02-13 02:43:28 +000071 private Constructor<T> findConstructorIn(ContainerImpl container,
72 Class<T> implementation) {
crazybobleee3adfd62007-02-02 21:30:08 +000073 Constructor<T> found = null;
crazyboblee4602a6f2007-02-15 02:45:18 +000074 @SuppressWarnings("unchecked")
kevinb9n48d13072007-02-12 18:21:26 +000075 Constructor<T>[] constructors
76 = (Constructor<T>[]) implementation.getDeclaredConstructors();
77 for (Constructor<T> constructor : constructors) {
crazyboblee0bfdbc62007-02-20 21:47:19 +000078 Inject inject = constructor.getAnnotation(Inject.class);
79 if (inject != null) {
80 if (inject.optional()) {
81 container.errorHandler.handle(
82 StackTraceElements.forMember(constructor),
83 ErrorMessages.OPTIONAL_CONSTRUCTOR);
84 }
85
crazybobleee3adfd62007-02-02 21:30:08 +000086 if (found != null) {
87 container.errorHandler.handle(
crazyboblee9a3861b2007-02-20 03:37:31 +000088 StackTraceElements.forMember(found),
89 ErrorMessages.TOO_MANY_CONSTRUCTORS);
crazybobleee3adfd62007-02-02 21:30:08 +000090 return ContainerImpl.invalidConstructor();
91 }
92 found = constructor;
93 }
94 }
95 if (found != null) {
96 return found;
97 }
98
99 // If no annotated constructor is found, look for a no-arg constructor
100 // instead.
101 try {
102 return implementation.getDeclaredConstructor();
kevinb9na99dca72007-02-11 04:48:57 +0000103 }
104 catch (NoSuchMethodException e) {
105 container.errorHandler.handle(
crazyboblee9a3861b2007-02-20 03:37:31 +0000106 StackTraceElements.forMember(
107 implementation.getDeclaredConstructors()[0]),
108 ErrorMessages.MISSING_CONSTRUCTOR,
109 implementation);
crazybobleee3adfd62007-02-02 21:30:08 +0000110 return ContainerImpl.invalidConstructor();
111 }
112 }
113
114 /**
kevinb9na99dca72007-02-11 04:48:57 +0000115 * Construct an instance. Returns {@code Object} instead of {@code T} because
116 * it may return a proxy.
crazybobleee3adfd62007-02-02 21:30:08 +0000117 */
kevinb9n48d13072007-02-12 18:21:26 +0000118 T construct(InternalContext context, Class<T> expectedType) {
kevinb9na99dca72007-02-11 04:48:57 +0000119 ConstructionContext<T> constructionContext
120 = context.getConstructionContext(this);
crazybobleee3adfd62007-02-02 21:30:08 +0000121
122 // We have a circular reference between constructors. Return a proxy.
123 if (constructionContext.isConstructing()) {
124 // TODO (crazybob): if we can't proxy this object, can we proxy the
125 // other object?
126 return constructionContext.createProxy(expectedType);
127 }
128
129 // If we're re-entering this factory while injecting fields or methods,
130 // return the same instance. This prevents infinite loops.
131 T t = constructionContext.getCurrentReference();
132 if (t != null) {
133 return t;
134 }
135
136 try {
137 // First time through...
138 constructionContext.startConstruction();
139 try {
kevinb9na99dca72007-02-11 04:48:57 +0000140 Object[] parameters
141 = ContainerImpl.getParameters(context, parameterInjectors);
kevinb9n48d13072007-02-12 18:21:26 +0000142 t = constructionProxy.newInstance(parameters);
crazybobleee3adfd62007-02-02 21:30:08 +0000143 constructionContext.setProxyDelegates(t);
kevinb9na99dca72007-02-11 04:48:57 +0000144 }
145 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000146 constructionContext.finishConstruction();
147 }
148
149 // Store reference. If an injector re-enters this factory, they'll
150 // get the same reference.
151 constructionContext.setCurrentReference(t);
152
153 // Inject fields and methods.
kevinb9na99dca72007-02-11 04:48:57 +0000154 for (ContainerImpl.Injector injector : injectors) {
155 injector.inject(context, t);
crazybobleee3adfd62007-02-02 21:30:08 +0000156 }
157
158 return t;
kevinb9na99dca72007-02-11 04:48:57 +0000159 }
160 catch (InvocationTargetException e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000161 throw new RuntimeException(e);
kevinb9na99dca72007-02-11 04:48:57 +0000162 }
163 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000164 constructionContext.removeCurrentReference();
165 }
166 }
167
crazyboblee0789b192007-02-13 02:43:28 +0000168 /**
169 * Returns an invalid constructor. This enables us to keep running and
170 * reporting legitimate errors.
171 */
172 static <T> ConstructorInjector<T> invalidConstructor() {
173 return new ConstructorInjector<T>() {
174 T construct(InternalContext context, Class<T> expectedType) {
175 throw new UnsupportedOperationException();
176 }
177 public T get() {
178 throw new UnsupportedOperationException();
179 }
180 };
crazybobleee3adfd62007-02-02 21:30:08 +0000181 }
182}