blob: f3bb7a25d550605ba7f651186f32017681b4d980 [file] [log] [blame]
limpbizkita67e39f2008-02-18 20:10:59 +00001/**
2 * Copyright (C) 2008 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 */
16
17package com.google.inject.commands;
18
limpbizkit6663d022008-06-19 07:57:55 +000019import static com.google.common.base.Preconditions.checkArgument;
limpbizkit185d2a22008-06-16 07:22:47 +000020import com.google.common.collect.Lists;
21import com.google.common.collect.Sets;
limpbizkit9532e622008-06-18 08:20:54 +000022import com.google.inject.AbstractModule;
limpbizkit185d2a22008-06-16 07:22:47 +000023import com.google.inject.Binder;
24import com.google.inject.Key;
25import com.google.inject.Module;
26import com.google.inject.Provider;
27import com.google.inject.Scope;
28import com.google.inject.Stage;
29import com.google.inject.TypeLiteral;
limpbizkita67e39f2008-02-18 20:10:59 +000030import com.google.inject.binder.AnnotatedBindingBuilder;
31import com.google.inject.binder.AnnotatedConstantBindingBuilder;
limpbizkit6663d022008-06-19 07:57:55 +000032import com.google.inject.internal.SourceProvider;
limpbizkita67e39f2008-02-18 20:10:59 +000033import com.google.inject.matcher.Matcher;
limpbizkit185d2a22008-06-16 07:22:47 +000034import com.google.inject.spi.Message;
limpbizkita67e39f2008-02-18 20:10:59 +000035import com.google.inject.spi.TypeConverter;
limpbizkita67e39f2008-02-18 20:10:59 +000036import java.lang.annotation.Annotation;
37import java.lang.reflect.Method;
limpbizkit185d2a22008-06-16 07:22:47 +000038import java.util.Arrays;
39import java.util.Collections;
40import java.util.List;
41import java.util.Set;
42import org.aopalliance.intercept.MethodInterceptor;
limpbizkita67e39f2008-02-18 20:10:59 +000043
44/**
45 * Records commands executed by a module so they can be inspected or
46 * {@link CommandReplayer replayed}.
47 *
48 * @author jessewilson@google.com (Jesse Wilson)
49 */
50public final class CommandRecorder {
limpbizkit3d58d6b2008-03-08 16:11:47 +000051 private Stage currentStage = Stage.DEVELOPMENT;
limpbizkita67e39f2008-02-18 20:10:59 +000052 private final EarlyRequestsProvider earlyRequestsProvider;
53
54 /**
55 * @param earlyRequestsProvider satisfies requests to
56 * {@link Binder#getProvider} at module execution time. For modules that
57 * will be used to create an injector, use {@link FutureInjector}.
58 */
59 public CommandRecorder(EarlyRequestsProvider earlyRequestsProvider) {
60 this.earlyRequestsProvider = earlyRequestsProvider;
61 }
62
63 /**
limpbizkit3d58d6b2008-03-08 16:11:47 +000064 * Sets the stage reported by the binder.
65 */
66 public void setCurrentStage(Stage currentStage) {
67 this.currentStage = currentStage;
68 }
69
70 /**
limpbizkita67e39f2008-02-18 20:10:59 +000071 * Records the commands executed by {@code modules}.
72 */
73 public List<Command> recordCommands(Module... modules) {
74 return recordCommands(Arrays.asList(modules));
75 }
76
77 /**
78 * Records the commands executed by {@code modules}.
79 */
80 public List<Command> recordCommands(Iterable<Module> modules) {
81 RecordingBinder binder = new RecordingBinder();
82 for (Module module : modules) {
limpbizkit3d58d6b2008-03-08 16:11:47 +000083 binder.install(module);
limpbizkita67e39f2008-02-18 20:10:59 +000084 }
85 return Collections.unmodifiableList(binder.commands);
86 }
87
88 private class RecordingBinder implements Binder {
limpbizkit7d9991e2008-06-17 02:49:18 +000089 private final Set<Module> modules;
90 private final List<Command> commands;
limpbizkit6663d022008-06-19 07:57:55 +000091 private final Object source;
92 private final SourceProvider sourceProvider;
limpbizkit7d9991e2008-06-17 02:49:18 +000093
94 private RecordingBinder() {
95 modules = Sets.newHashSet();
96 commands = Lists.newArrayList();
limpbizkit6663d022008-06-19 07:57:55 +000097 source = null;
98 sourceProvider
99 = new SourceProvider().plusSkippedClasses(RecordingBinder.class, AbstractModule.class);
limpbizkit7d9991e2008-06-17 02:49:18 +0000100 }
101
102 /**
103 * Creates a recording binder that's backed by the same configuration as
104 * {@code backingBinder}.
105 */
limpbizkit6663d022008-06-19 07:57:55 +0000106 private RecordingBinder(RecordingBinder parent, Object source, SourceProvider sourceProvider) {
107 checkArgument(source == null ^ sourceProvider == null);
108
109 modules = parent.modules;
110 commands = parent.commands;
111 this.source = source;
112 this.sourceProvider = sourceProvider;
limpbizkit7d9991e2008-06-17 02:49:18 +0000113 }
limpbizkita67e39f2008-02-18 20:10:59 +0000114
115 public void bindInterceptor(
116 Matcher<? super Class<?>> classMatcher,
117 Matcher<? super Method> methodMatcher,
118 MethodInterceptor... interceptors) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000119 commands.add(new BindInterceptorCommand(getSource(), classMatcher, methodMatcher, interceptors));
limpbizkita67e39f2008-02-18 20:10:59 +0000120 }
121
122 public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000123 commands.add(new BindScopeCommand(getSource(), annotationType, scope));
limpbizkita67e39f2008-02-18 20:10:59 +0000124 }
125
126 public void requestStaticInjection(Class<?>... types) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000127 commands.add(new RequestStaticInjectionCommand(getSource(), types));
limpbizkita67e39f2008-02-18 20:10:59 +0000128 }
129
130 public void install(Module module) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000131 if (modules.add(module)) {
132 module.configure(this);
133 }
limpbizkita67e39f2008-02-18 20:10:59 +0000134 }
135
136 public Stage currentStage() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000137 return currentStage;
limpbizkita67e39f2008-02-18 20:10:59 +0000138 }
139
140 public void addError(String message, Object... arguments) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000141 commands.add(new AddMessageErrorCommand(getSource(), message, arguments));
limpbizkita67e39f2008-02-18 20:10:59 +0000142 }
143
144 public void addError(Throwable t) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000145 commands.add(new AddThrowableErrorCommand(getSource(), t));
limpbizkita67e39f2008-02-18 20:10:59 +0000146 }
147
limpbizkit185d2a22008-06-16 07:22:47 +0000148 public void addError(Message message) {
149 throw new UnsupportedOperationException("TODO");
150 }
151
limpbizkita67e39f2008-02-18 20:10:59 +0000152 public <T> BindCommand<T>.BindingBuilder bind(Key<T> key) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000153 BindCommand<T> bindCommand = new BindCommand<T>(getSource(), key);
limpbizkita67e39f2008-02-18 20:10:59 +0000154 commands.add(bindCommand);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000155 return bindCommand.bindingBuilder(RecordingBinder.this);
limpbizkita67e39f2008-02-18 20:10:59 +0000156 }
157
158 public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
159 return bind(Key.get(typeLiteral));
160 }
161
162 public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
163 return bind(Key.get(type));
164 }
165
166 public AnnotatedConstantBindingBuilder bindConstant() {
limpbizkit7d9991e2008-06-17 02:49:18 +0000167 BindConstantCommand bindConstantCommand = new BindConstantCommand(getSource());
limpbizkita67e39f2008-02-18 20:10:59 +0000168 commands.add(bindConstantCommand);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000169 return bindConstantCommand.bindingBuilder(RecordingBinder.this);
limpbizkita67e39f2008-02-18 20:10:59 +0000170 }
171
172 public <T> Provider<T> getProvider(final Key<T> key) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000173 commands.add(new GetProviderCommand<T>(getSource(), key, earlyRequestsProvider));
limpbizkita67e39f2008-02-18 20:10:59 +0000174 return new Provider<T>() {
175 public T get() {
176 return earlyRequestsProvider.get(key);
177 }
limpbizkitd6967b92008-05-16 15:28:51 +0000178
179 @Override public String toString() {
180 return "Provider<" + key.getTypeLiteral() + ">";
181 }
limpbizkita67e39f2008-02-18 20:10:59 +0000182 };
183 }
184
185 public <T> Provider<T> getProvider(Class<T> type) {
186 return getProvider(Key.get(type));
187 }
188
189 public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
limpbizkit3d58d6b2008-03-08 16:11:47 +0000190 TypeConverter converter) {
limpbizkit7d9991e2008-06-17 02:49:18 +0000191 commands.add(new ConvertToTypesCommand(getSource(), typeMatcher, converter));
192 }
193
194 public Binder withSource(final Object source) {
limpbizkit6663d022008-06-19 07:57:55 +0000195 return new RecordingBinder(this, source, null);
196 }
limpbizkit7d9991e2008-06-17 02:49:18 +0000197
limpbizkit6663d022008-06-19 07:57:55 +0000198 public Binder skipSources(Class... classesToSkip) {
199 // if a source is specified explicitly, we don't need to skip sources
200 if (source != null) {
201 return this;
202 }
203
204 SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
205 return new RecordingBinder(this, null, newSourceProvider);
limpbizkit7d9991e2008-06-17 02:49:18 +0000206 }
207
208 protected Object getSource() {
limpbizkit6663d022008-06-19 07:57:55 +0000209 return sourceProvider != null
210 ? sourceProvider.get()
211 : source;
limpbizkita67e39f2008-02-18 20:10:59 +0000212 }
limpbizkitd6967b92008-05-16 15:28:51 +0000213
214 @Override public String toString() {
215 return "Binder";
216 }
limpbizkita67e39f2008-02-18 20:10:59 +0000217 }
218}