blob: d242b9146dff5c01f56fb38face8b372c7c295b8 [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;
21
22/**
23 * Injects constructors.
24 *
25 * @author crazybob@google.com (Bob Lee)
kevinb9na99dca72007-02-11 04:48:57 +000026 */
crazybobleee3adfd62007-02-02 21:30:08 +000027class ConstructorInjector<T> implements Factory<T> {
28
29 final Class<T> implementation;
30 final ContainerImpl.Injector[] injectors;
31 final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
32 final ConstructionProxy<T> constructionProxy;
33
kevinb9n6a565c72007-02-11 01:58:33 +000034 private final ContainerImpl container;
crazybobleee3adfd62007-02-02 21:30:08 +000035
36 ConstructorInjector(ContainerImpl container, Class<T> implementation) {
37 this.container = container;
38 this.implementation = implementation;
39 Constructor<T> constructor = findConstructorIn(implementation);
40 parameterInjectors = createParameterInjector(constructor);
41 injectors = container.injectors.get(implementation)
42 .toArray(new ContainerImpl.Injector[0]);
43 constructionProxy = container.constructionProxyFactory.get(constructor);
44 }
45
46 ContainerImpl.ParameterInjector<?>[] createParameterInjector(
47 Constructor<T> constructor) {
48 try {
49 Inject inject = constructor.getAnnotation(Inject.class);
50 return inject == null
51 ? null // default constructor.
52 : container.getParametersInjectors(
53 constructor,
54 constructor.getParameterAnnotations(),
55 constructor.getGenericParameterTypes(),
56 inject.value()
57 );
kevinb9na99dca72007-02-11 04:48:57 +000058 }
59 catch (ContainerImpl.MissingDependencyException e) {
crazybobleee3adfd62007-02-02 21:30:08 +000060 e.handle(container.errorHandler);
61 return null;
62 }
63 }
64
kevinb9na99dca72007-02-11 04:48:57 +000065 @SuppressWarnings("unchecked")
crazybobleee3adfd62007-02-02 21:30:08 +000066 private Constructor<T> findConstructorIn(Class<T> implementation) {
67 Constructor<T> found = null;
kevinb9na99dca72007-02-11 04:48:57 +000068 for (Constructor<T> constructor : implementation.getDeclaredConstructors())
69 {
crazybobleee3adfd62007-02-02 21:30:08 +000070 if (constructor.getAnnotation(Inject.class) != null) {
71 if (found != null) {
72 container.errorHandler.handle(
crazybobleee039bac2007-02-02 21:42:09 +000073 ErrorMessages.TOO_MANY_CONSTRUCTORS, implementation);
crazybobleee3adfd62007-02-02 21:30:08 +000074 return ContainerImpl.invalidConstructor();
75 }
76 found = constructor;
77 }
78 }
79 if (found != null) {
80 return found;
81 }
82
83 // If no annotated constructor is found, look for a no-arg constructor
84 // instead.
85 try {
86 return implementation.getDeclaredConstructor();
kevinb9na99dca72007-02-11 04:48:57 +000087 }
88 catch (NoSuchMethodException e) {
89 container.errorHandler.handle(
90 ErrorMessages.MISSING_CONSTRUCTOR, implementation);
crazybobleee3adfd62007-02-02 21:30:08 +000091 return ContainerImpl.invalidConstructor();
92 }
93 }
94
95 /**
kevinb9na99dca72007-02-11 04:48:57 +000096 * Construct an instance. Returns {@code Object} instead of {@code T} because
97 * it may return a proxy.
crazybobleee3adfd62007-02-02 21:30:08 +000098 */
99 Object construct(InternalContext context, Class<? super T> expectedType) {
kevinb9na99dca72007-02-11 04:48:57 +0000100 ConstructionContext<T> constructionContext
101 = context.getConstructionContext(this);
crazybobleee3adfd62007-02-02 21:30:08 +0000102
103 // We have a circular reference between constructors. Return a proxy.
104 if (constructionContext.isConstructing()) {
105 // TODO (crazybob): if we can't proxy this object, can we proxy the
106 // other object?
107 return constructionContext.createProxy(expectedType);
108 }
109
110 // If we're re-entering this factory while injecting fields or methods,
111 // return the same instance. This prevents infinite loops.
112 T t = constructionContext.getCurrentReference();
113 if (t != null) {
114 return t;
115 }
116
117 try {
118 // First time through...
119 constructionContext.startConstruction();
120 try {
kevinb9na99dca72007-02-11 04:48:57 +0000121 Object[] parameters
122 = ContainerImpl.getParameters(context, parameterInjectors);
crazybobleee3adfd62007-02-02 21:30:08 +0000123 t = newInstance(parameters);
124 constructionContext.setProxyDelegates(t);
kevinb9na99dca72007-02-11 04:48:57 +0000125 }
126 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000127 constructionContext.finishConstruction();
128 }
129
130 // Store reference. If an injector re-enters this factory, they'll
131 // get the same reference.
132 constructionContext.setCurrentReference(t);
133
134 // Inject fields and methods.
kevinb9na99dca72007-02-11 04:48:57 +0000135 for (ContainerImpl.Injector injector : injectors) {
136 injector.inject(context, t);
crazybobleee3adfd62007-02-02 21:30:08 +0000137 }
138
139 return t;
kevinb9na99dca72007-02-11 04:48:57 +0000140 }
141 catch (InvocationTargetException e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000142 throw new RuntimeException(e);
kevinb9na99dca72007-02-11 04:48:57 +0000143 }
144 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000145 constructionContext.removeCurrentReference();
146 }
147 }
148
kevinb9na99dca72007-02-11 04:48:57 +0000149 @SuppressWarnings("unchecked")
150 private T newInstance(Object[] parameters) throws InvocationTargetException {
151 return constructionProxy.newInstance(parameters);
crazybobleee3adfd62007-02-02 21:30:08 +0000152 }
153
154 public T get() {
155 try {
156 return container.callInContext(new ContextualCallable<T>() {
kevinb9na99dca72007-02-11 04:48:57 +0000157 @SuppressWarnings("unchecked")
crazybobleee3adfd62007-02-02 21:30:08 +0000158 public T call(InternalContext context) {
159 return (T) construct(context, implementation);
160 }
161 });
kevinb9na99dca72007-02-11 04:48:57 +0000162 }
163 catch (RuntimeException e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000164 throw e;
kevinb9na99dca72007-02-11 04:48:57 +0000165 }
166 catch (Exception e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000167 throw new RuntimeException(e);
168 }
169 }
170}