Elements.java revision bf0d876bb767f45c5cfbed3929e2cf6acd7d061a
1/** 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.spi; 18 19import static com.google.common.base.Preconditions.checkArgument; 20import static com.google.common.base.Preconditions.checkState; 21import com.google.common.collect.ImmutableList; 22import com.google.common.collect.Lists; 23import com.google.common.collect.Sets; 24import com.google.inject.AbstractModule; 25import com.google.inject.Binder; 26import com.google.inject.Binding; 27import com.google.inject.Key; 28import com.google.inject.Module; 29import com.google.inject.PrivateBinder; 30import com.google.inject.PrivateModule; 31import com.google.inject.Provider; 32import com.google.inject.Scope; 33import com.google.inject.Stage; 34import com.google.inject.TypeLiteral; 35import com.google.inject.binder.AnnotatedBindingBuilder; 36import com.google.inject.binder.AnnotatedConstantBindingBuilder; 37import com.google.inject.binder.AnnotatedElementBuilder; 38import com.google.inject.internal.AbstractBindingBuilder; 39import com.google.inject.internal.BindingBuilder; 40import com.google.inject.internal.ConstantBindingBuilderImpl; 41import com.google.inject.internal.Errors; 42import com.google.inject.internal.PrivateElementsImpl; 43import com.google.inject.internal.ProviderMethodsModule; 44import com.google.inject.internal.SourceProvider; 45import com.google.inject.matcher.Matcher; 46import java.lang.annotation.Annotation; 47import java.lang.reflect.Method; 48import java.util.Arrays; 49import java.util.Collection; 50import java.util.Collections; 51import java.util.List; 52import java.util.Set; 53import org.aopalliance.intercept.MethodInterceptor; 54 55/** 56 * Exposes elements of a module so they can be inspected, validated or {@link ModuleWriter 57 * rewritten}. 58 * 59 * @author jessewilson@google.com (Jesse Wilson) 60 * @since 2.0 61 */ 62public final class Elements { 63 private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR 64 = new DefaultBindingTargetVisitor<Object, Object>() { 65 @Override public Object visitInstance(InstanceBinding<?> binding) { 66 return binding.getInstance(); 67 } 68 69 @Override protected Object visitOther(Binding<?> binding) { 70 throw new IllegalArgumentException(); 71 } 72 }; 73 74 /** 75 * Records the elements executed by {@code modules}. 76 */ 77 public static List<Element> getElements(Module... modules) { 78 return getElements(Stage.DEVELOPMENT, Arrays.asList(modules)); 79 } 80 81 /** 82 * Records the elements executed by {@code modules}. 83 */ 84 public static List<Element> getElements(Stage stage, Module... modules) { 85 return getElements(stage, Arrays.asList(modules)); 86 } 87 88 /** 89 * Records the elements executed by {@code modules}. 90 */ 91 public static List<Element> getElements(Iterable<? extends Module> modules) { 92 return getElements(Stage.DEVELOPMENT, modules); 93 } 94 95 /** 96 * Records the elements executed by {@code modules}. 97 */ 98 public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) { 99 RecordingBinder binder = new RecordingBinder(stage); 100 for (Module module : modules) { 101 binder.install(module); 102 } 103 return Collections.unmodifiableList(binder.elements); 104 } 105 106 @SuppressWarnings("unchecked") 107 static <T> BindingTargetVisitor<T, T> getInstanceVisitor() { 108 return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR; 109 } 110 111 private static class RecordingBinder implements Binder, PrivateBinder { 112 private final Stage stage; 113 private final Set<Module> modules; 114 private final List<Element> elements; 115 private final Object source; 116 private final SourceProvider sourceProvider; 117 118 /** The binder where exposed bindings will be created */ 119 private final RecordingBinder parent; 120 private final PrivateElementsImpl privateElements; 121 122 private RecordingBinder(Stage stage) { 123 this.stage = stage; 124 this.modules = Sets.newHashSet(); 125 this.elements = Lists.newArrayList(); 126 this.source = null; 127 this.sourceProvider = new SourceProvider().plusSkippedClasses( 128 Elements.class, RecordingBinder.class, AbstractModule.class, 129 ConstantBindingBuilderImpl.class, AbstractBindingBuilder.class, BindingBuilder.class); 130 this.parent = null; 131 this.privateElements = null; 132 } 133 134 /** Creates a recording binder that's backed by {@code prototype}. */ 135 private RecordingBinder( 136 RecordingBinder prototype, Object source, SourceProvider sourceProvider) { 137 checkArgument(source == null ^ sourceProvider == null); 138 139 this.stage = prototype.stage; 140 this.modules = prototype.modules; 141 this.elements = prototype.elements; 142 this.source = source; 143 this.sourceProvider = sourceProvider; 144 this.parent = prototype.parent; 145 this.privateElements = prototype.privateElements; 146 } 147 148 /** Creates a private recording binder. */ 149 private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) { 150 this.stage = parent.stage; 151 this.modules = Sets.newHashSet(); 152 this.elements = privateElements.getElementsMutable(); 153 this.source = parent.source; 154 this.sourceProvider = parent.sourceProvider; 155 this.parent = parent; 156 this.privateElements = privateElements; 157 } 158 159 /*if[AOP]*/ 160 public void bindInterceptor( 161 Matcher<? super Class<?>> classMatcher, 162 Matcher<? super Method> methodMatcher, 163 MethodInterceptor... interceptors) { 164 elements.add(new InterceptorBinding(getSource(), classMatcher, methodMatcher, interceptors)); 165 } 166 /*end[AOP]*/ 167 168 public void bindScope(Class<? extends Annotation> annotationType, Scope scope) { 169 elements.add(new ScopeBinding(getSource(), annotationType, scope)); 170 } 171 172 public void requestInjection(Object... instances) { 173 for (Object instance : instances) { 174 elements.add(new InjectionRequest(getSource(), instance)); 175 } 176 } 177 178 public void requestStaticInjection(Class<?>... types) { 179 for (Class<?> type : types) { 180 elements.add(new StaticInjectionRequest(getSource(), type)); 181 } 182 } 183 184 public void install(Module module) { 185 if (modules.add(module)) { 186 Binder binder = this; 187 if (module instanceof PrivateModule) { 188 binder = binder.newPrivateBinder(); 189 } 190 191 try { 192 module.configure(binder); 193 } catch (RuntimeException e) { 194 Collection<Message> messages = Errors.getMessagesFromThrowable(e); 195 if (!messages.isEmpty()) { 196 elements.addAll(messages); 197 } else { 198 addError(e); 199 } 200 } 201 binder.install(ProviderMethodsModule.forModule(module)); 202 } 203 } 204 205 public Stage currentStage() { 206 return stage; 207 } 208 209 public void addError(String message, Object... arguments) { 210 elements.add(new Message(getSource(), Errors.format(message, arguments))); 211 } 212 213 public void addError(Throwable t) { 214 String message = "An exception was caught and reported. Message: " + t.getMessage(); 215 elements.add(new Message(ImmutableList.of(getSource()), message, t)); 216 } 217 218 public void addError(Message message) { 219 elements.add(message); 220 } 221 222 public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) { 223 return new BindingBuilder<T>(this, elements, getSource(), key); 224 } 225 226 public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) { 227 return bind(Key.get(typeLiteral)); 228 } 229 230 public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) { 231 return bind(Key.get(type)); 232 } 233 234 public AnnotatedConstantBindingBuilder bindConstant() { 235 return new ConstantBindingBuilderImpl<Void>(this, elements, getSource()); 236 } 237 238 public <T> Provider<T> getProvider(final Key<T> key) { 239 final ProviderLookup<T> command = new ProviderLookup<T>(getSource(), key); 240 elements.add(command); 241 return new Provider<T>() { 242 public T get() { 243 Provider<T> delegate = command.getDelegate(); 244 checkState(delegate != null, 245 "This provider cannot be used until the Injector has been created."); 246 return delegate.get(); 247 } 248 249 @Override public String toString() { 250 return "Provider<" + key.getTypeLiteral() + ">"; 251 } 252 }; 253 } 254 255 public <T> Provider<T> getProvider(Class<T> type) { 256 return getProvider(Key.get(type)); 257 } 258 259 public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher, 260 TypeConverter converter) { 261 elements.add(new TypeConverterBinding(getSource(), typeMatcher, converter)); 262 } 263 264 public RecordingBinder withSource(final Object source) { 265 return new RecordingBinder(this, source, null); 266 } 267 268 public RecordingBinder skipSources(Class... classesToSkip) { 269 // if a source is specified explicitly, we don't need to skip sources 270 if (source != null) { 271 return this; 272 } 273 274 SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip); 275 return new RecordingBinder(this, null, newSourceProvider); 276 } 277 278 public PrivateBinder newPrivateBinder() { 279 PrivateElementsImpl privateElements = new PrivateElementsImpl(getSource()); 280 elements.add(privateElements); 281 return new RecordingBinder(this, privateElements); 282 } 283 284 public void expose(Key<?> key) { 285 exposeInternal(key); 286 } 287 288 public AnnotatedElementBuilder expose(Class<?> type) { 289 return exposeInternal(Key.get(type)); 290 } 291 292 public AnnotatedElementBuilder expose(TypeLiteral<?> type) { 293 return exposeInternal(Key.get(type)); 294 } 295 296 private <T> AnnotatedElementBuilder exposeInternal(Key<T> key) { 297 if (privateElements == null) { 298 addError("Cannot expose %s on a standard binder. " 299 + "Exposed bindings are only applicable to private binders.", key); 300 return new AnnotatedElementBuilder() { 301 public void annotatedWith(Class<? extends Annotation> annotationType) {} 302 public void annotatedWith(Annotation annotation) {} 303 }; 304 } 305 306 BindingBuilder<T> exposeBinding = new BindingBuilder<T>( 307 this, parent.elements, getSource(), key); 308 309 BindingBuilder.ExposureBuilder<T> builder = exposeBinding.usingKeyFrom(privateElements); 310 privateElements.addExposureBuilder(builder); 311 return builder; 312 } 313 314 protected Object getSource() { 315 return sourceProvider != null 316 ? sourceProvider.get() 317 : source; 318 } 319 320 @Override public String toString() { 321 return "Binder"; 322 } 323 } 324} 325