blob: 390481f7826c59698823d6df0c64bc5016aa1eec [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
limpbizkit5ae41eb2009-06-06 17:51:27 +000017package com.google.inject.internal;
crazybobleee3adfd62007-02-02 21:30:08 +000018
limpbizkita843a952009-04-08 22:24:55 +000019import com.google.inject.spi.InjectionPoint;
crazybobleee3adfd62007-02-02 21:30:08 +000020import java.lang.reflect.InvocationTargetException;
21
22/**
limpbizkit06898062008-11-02 05:14:55 +000023 * Creates instances using an injectable constructor. After construction, all injectable fields and
24 * methods are injected.
crazybobleee3adfd62007-02-02 21:30:08 +000025 *
26 * @author crazybob@google.com (Bob Lee)
kevinb9na99dca72007-02-11 04:48:57 +000027 */
limpbizkit5ae41eb2009-06-06 17:51:27 +000028final class ConstructorInjector<T> {
crazybobleee3adfd62007-02-02 21:30:08 +000029
limpbizkita843a952009-04-08 22:24:55 +000030 private final ImmutableSet<InjectionPoint> injectableMembers;
limpbizkite89c49e2009-05-06 01:02:14 +000031 private final SingleParameterInjector<?>[] parameterInjectors;
limpbizkit03b81a62009-03-18 05:34:39 +000032 private final ConstructionProxy<T> constructionProxy;
limpbizkit7cef5b02009-03-29 21:16:51 +000033 private final MembersInjectorImpl<T> membersInjector;
crazybobleee3adfd62007-02-02 21:30:08 +000034
limpbizkita843a952009-04-08 22:24:55 +000035 ConstructorInjector(ImmutableSet<InjectionPoint> injectableMembers,
36 ConstructionProxy<T> constructionProxy,
limpbizkite89c49e2009-05-06 01:02:14 +000037 SingleParameterInjector<?>[] parameterInjectors,
limpbizkita843a952009-04-08 22:24:55 +000038 MembersInjectorImpl<T> membersInjector)
limpbizkit163c48a2008-06-16 02:58:08 +000039 throws ErrorsException {
limpbizkita843a952009-04-08 22:24:55 +000040 this.injectableMembers = injectableMembers;
limpbizkit03b81a62009-03-18 05:34:39 +000041 this.constructionProxy = constructionProxy;
42 this.parameterInjectors = parameterInjectors;
limpbizkit7cef5b02009-03-29 21:16:51 +000043 this.membersInjector = membersInjector;
limpbizkita843a952009-04-08 22:24:55 +000044 }
45
46 public ImmutableSet<InjectionPoint> getInjectableMembers() {
47 return injectableMembers;
48 }
49
limpbizkita843a952009-04-08 22:24:55 +000050 ConstructionProxy<T> getConstructionProxy() {
51 return constructionProxy;
crazybobleee3adfd62007-02-02 21:30:08 +000052 }
53
crazybobleee3adfd62007-02-02 21:30:08 +000054 /**
kevinb9na99dca72007-02-11 04:48:57 +000055 * Construct an instance. Returns {@code Object} instead of {@code T} because
56 * it may return a proxy.
crazybobleee3adfd62007-02-02 21:30:08 +000057 */
limpbizkit9dc32d42008-06-15 11:29:10 +000058 Object construct(Errors errors, InternalContext context, Class<?> expectedType)
limpbizkit163c48a2008-06-16 02:58:08 +000059 throws ErrorsException {
limpbizkit9dc32d42008-06-15 11:29:10 +000060 ConstructionContext<T> constructionContext = context.getConstructionContext(this);
crazybobleee3adfd62007-02-02 21:30:08 +000061
62 // We have a circular reference between constructors. Return a proxy.
63 if (constructionContext.isConstructing()) {
limpbizkit9dc32d42008-06-15 11:29:10 +000064 // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
65 return constructionContext.createProxy(errors, expectedType);
crazybobleee3adfd62007-02-02 21:30:08 +000066 }
67
68 // If we're re-entering this factory while injecting fields or methods,
69 // return the same instance. This prevents infinite loops.
70 T t = constructionContext.getCurrentReference();
71 if (t != null) {
72 return t;
73 }
74
75 try {
76 // First time through...
77 constructionContext.startConstruction();
78 try {
limpbizkit06898062008-11-02 05:14:55 +000079 Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
kevinb9n48d13072007-02-12 18:21:26 +000080 t = constructionProxy.newInstance(parameters);
crazybobleee3adfd62007-02-02 21:30:08 +000081 constructionContext.setProxyDelegates(t);
limpbizkit06898062008-11-02 05:14:55 +000082 } finally {
crazybobleee3adfd62007-02-02 21:30:08 +000083 constructionContext.finishConstruction();
84 }
85
limpbizkita843a952009-04-08 22:24:55 +000086 // Store reference. If an injector re-enters this factory, they'll get the same reference.
crazybobleee3adfd62007-02-02 21:30:08 +000087 constructionContext.setCurrentReference(t);
88
limpbizkit7cef5b02009-03-29 21:16:51 +000089 membersInjector.injectMembers(t, errors, context);
limpbizkita843a952009-04-08 22:24:55 +000090 membersInjector.notifyListeners(t, errors);
limpbizkit03b81a62009-03-18 05:34:39 +000091
crazybobleee3adfd62007-02-02 21:30:08 +000092 return t;
limpbizkita6e0e782008-09-03 06:19:56 +000093 } catch (InvocationTargetException userException) {
limpbizkit9dc32d42008-06-15 11:29:10 +000094 Throwable cause = userException.getCause() != null
95 ? userException.getCause()
96 : userException;
limpbizkita6e0e782008-09-03 06:19:56 +000097 throw errors.withSource(constructionProxy.getInjectionPoint())
98 .errorInjectingConstructor(cause).toException();
99 } finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000100 constructionContext.removeCurrentReference();
101 }
102 }
crazybobleee3adfd62007-02-02 21:30:08 +0000103}