1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.onami.factoryannotation;
20
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static com.google.common.base.Preconditions.checkState;
23
24 import java.lang.annotation.Annotation;
25 import java.util.HashMap;
26 import java.util.Map;
27
28 import org.apache.onami.factoryannotation.PrivilegedHelper.PrivilegedHelperCallback;
29 import org.apache.onami.factoryannotation.builder.BindFactoryAnnotationBindingBuilder;
30 import org.apache.onami.factoryannotation.builder.CombinedListenerCacheableBindingBuilder;
31 import org.apache.onami.factoryannotation.builder.FactoryAnnotationAnnotationBindingBuilder;
32 import org.apache.onami.factoryannotation.builder.FactoryAnnotationBaseProviderBindingBuilder;
33
34 import com.google.common.base.Preconditions;
35 import com.google.inject.Binder;
36 import com.google.inject.BindingAnnotation;
37 import com.google.inject.Key;
38 import com.google.inject.Module;
39 import com.google.inject.Scope;
40 import com.google.inject.ScopeAnnotation;
41 import com.google.inject.TypeLiteral;
42 import com.google.inject.binder.ScopedBindingBuilder;
43 import com.google.inject.internal.Annotations;
44 import com.google.inject.internal.Errors;
45 import com.google.inject.internal.Scoping;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public abstract class FactoryAnnotationModule
116 implements Module
117 {
118
119 private final Map<ScopedKey<?>, FactoryAnnotationProviderFactory<?, ?>> providerFactoryCache =
120 new HashMap<ScopedKey<?>, FactoryAnnotationProviderFactory<?, ?>>();
121
122 private Binder binder;
123
124
125
126
127
128
129 protected <T> FactoryAnnotationAnnotationBindingBuilder bindType( final Class<T> injectionType )
130 {
131
132 return bindIdentity( TypeLiteral.get( injectionType ) );
133 }
134
135
136
137
138
139
140 protected <T> FactoryAnnotationAnnotationBindingBuilder bindIdentity( final TypeLiteral<T> injectionType )
141 {
142
143 return new IdentityAnnotationBindingBuilderImpl<T>( injectionType, null, null );
144
145 }
146
147
148
149
150
151
152
153 protected <A extends Annotation> BindFactoryAnnotationBindingBuilder in( final Class<A> scopeAnnotation )
154 {
155
156 Preconditions.checkNotNull( scopeAnnotation, "scopeAnnotation" );
157
158 return new BindIdentityBindingBuilderImpl( scopeAnnotation, null );
159 }
160
161
162
163
164
165
166 protected BindFactoryAnnotationBindingBuilder in( final Scope scope )
167 {
168 Preconditions.checkNotNull( scope, "scope" );
169
170 return new BindIdentityBindingBuilderImpl( null, scope );
171 }
172
173 public void configure( final Binder binder )
174 {
175 checkState( this.binder == null, "Re-entry is not allowed." );
176
177 this.binder = checkNotNull( binder, "binder" );
178 try
179 {
180 configure();
181 }
182 finally
183 {
184 this.binder = null;
185 }
186 }
187
188 protected Binder binder()
189 {
190 return binder;
191 }
192
193 protected abstract void configure();
194
195 private final class BindIdentityBindingBuilderImpl
196 implements BindFactoryAnnotationBindingBuilder
197 {
198
199 private final Scope scope;
200
201 private final Class<? extends Annotation> scopeAnnotation;
202
203 private BindIdentityBindingBuilderImpl( final Class<? extends Annotation> scopeAnnotation, final Scope scope )
204 {
205
206 this.scopeAnnotation = scopeAnnotation;
207 this.scope = scope;
208 }
209
210 public <T> FactoryAnnotationAnnotationBindingBuilder bindIdentity( final Class<T> injectionType )
211 {
212
213 return new IdentityAnnotationBindingBuilderImpl<T>( TypeLiteral.get( injectionType ), scopeAnnotation,
214 scope );
215 }
216
217 public <T> FactoryAnnotationAnnotationBindingBuilder bindIdentity( final TypeLiteral<T> injectionType )
218 {
219
220 return new IdentityAnnotationBindingBuilderImpl<T>( injectionType, scopeAnnotation, scope );
221 }
222 }
223
224 private final class IdentityAnnotationBindingBuilderImpl<T>
225 implements FactoryAnnotationAnnotationBindingBuilder
226 {
227
228 private final TypeLiteral<T> injectionType;
229
230 private final Scope scope;
231
232 private final Class<? extends Annotation> scopeAnnotation;
233
234 private IdentityAnnotationBindingBuilderImpl( final TypeLiteral<T> injectionType,
235 final Class<? extends Annotation> scopeAnnotation,
236 final Scope scope )
237 {
238
239 Preconditions.checkNotNull( injectionType );
240
241 this.injectionType = injectionType;
242 this.scopeAnnotation = scopeAnnotation;
243 this.scope = scope;
244 }
245
246 public <I extends Annotation> CombinedListenerCacheableBindingBuilder annotatedWith( final Class<I> annotationType )
247 {
248
249 Preconditions.checkNotNull( annotationType, "annotationType" );
250
251 Preconditions.checkArgument( Annotations.isRetainedAtRuntime( annotationType ),
252 "%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
253 annotationType.getName() );
254
255 Preconditions.checkArgument( Annotations.isBindingAnnotation( annotationType ),
256 "%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
257 annotationType.getName() );
258
259 final boolean cacheable = annotationType.isAnnotationPresent( FactoryAnnotationCacheable.class );
260
261 return new IdentityProviderBindingBuilderImpl<T>( injectionType, scopeAnnotation, scope, null,
262 annotationType, cacheable );
263 }
264 }
265
266 private final class IdentityProviderBindingBuilderImpl<T>
267 implements CombinedListenerCacheableBindingBuilder
268 {
269
270 private final TypeLiteral<T> injectionType;
271
272 private final Scope scope;
273
274 private final Class<? extends Annotation> scopeAnnotation;
275
276 private final Class<? extends Annotation> annotationType;
277
278 private final FactoryAnnotationProvisionListener<?, ?> provisionListener;
279
280 private final boolean cacheable;
281
282 private IdentityProviderBindingBuilderImpl( final TypeLiteral<T> injectionType,
283 final Class<? extends Annotation> scopeAnnotation,
284 final Scope scope,
285 final FactoryAnnotationProvisionListener<?, ?> provisionListener,
286 final Class<? extends Annotation> annotationType,
287 final boolean cacheable )
288 {
289
290 this.injectionType = injectionType;
291 this.scopeAnnotation = scopeAnnotation;
292 this.scope = scope;
293 this.provisionListener = provisionListener;
294 this.annotationType = annotationType;
295 this.cacheable = cacheable;
296 }
297
298 @SuppressWarnings( "unchecked" )
299 public <I, A extends Annotation> void toAnnotationFactory( final FactoryAnnotationProvider<I, A> factoryAnnotationProvider )
300 {
301
302 Preconditions.checkNotNull( factoryAnnotationProvider, "factoryAnnotationProvider" );
303
304 final Class<I> keyType = factoryAnnotationProvider.getInjectionType();
305
306 if ( !keyType.equals( injectionType.getRawType() ) )
307 {
308 new Errors().cannotInjectTypeLiteralOf( keyType ).throwConfigurationExceptionIfErrorsExist();
309 }
310
311 final ScopedKey<T> cacheKey =
312 buildScopedKey( factoryAnnotationProvider.getClass(), annotationType, scopeAnnotation, scope );
313
314 final FactoryAnnotationProviderFactory<I, A> provider;
315 if ( providerFactoryCache.containsKey( cacheKey ) )
316 {
317 provider = (FactoryAnnotationProviderFactory<I, A>) providerFactoryCache.get( cacheKey );
318
319 }
320 else
321 {
322 provider =
323 new FactoryAnnotationProviderFactory<I, A>(
324 binder(),
325 (Class<A>) annotationType,
326 factoryAnnotationProvider,
327 (FactoryAnnotationProvisionListener<I, A>) provisionListener,
328 cacheable );
329
330 providerFactoryCache.put( cacheKey, provider );
331
332 final ScopedBindingBuilder scopeBuilder =
333 binder().bind( keyType ).annotatedWith( annotationType ).toProvider( provider );
334
335 if ( scope != null )
336 {
337 scopeBuilder.in( scope );
338
339 }
340 else if ( scopeAnnotation != null )
341 {
342 scopeBuilder.in( scopeAnnotation );
343 }
344 }
345 }
346
347 public <I, A extends Annotation> void toIdentityProvider( final Class<FactoryAnnotationProvider<I, A>> factoryAnnotationProviderType )
348 {
349
350 Preconditions.checkNotNull( factoryAnnotationProviderType, "factoryAnnotationProviderType" );
351
352 final FactoryAnnotationProvider<I, A> identityProvider =
353 PrivilegedHelper.executePrivileged( factoryAnnotationProviderType,
354 new PrivilegedHelperCallback<FactoryAnnotationProvider<I, A>>()
355 {
356
357 public FactoryAnnotationProvider<I, A> execute( final Class<FactoryAnnotationProvider<I, A>> clazz )
358 {
359
360 try
361 {
362 return clazz.newInstance();
363
364 }
365 catch ( final InstantiationException e )
366 {
367 throw new RuntimeException( e );
368
369 }
370 catch ( final IllegalAccessException e )
371 {
372 throw new RuntimeException( e );
373 }
374 }
375 } );
376
377 toAnnotationFactory( identityProvider );
378 }
379
380 @SuppressWarnings( "unchecked" )
381 public <I, A extends Annotation> FactoryAnnotationBaseProviderBindingBuilder applyListener( final FactoryAnnotationProvisionListener<I, A> provisionListener )
382 {
383
384 Preconditions.checkNotNull( provisionListener, "provisionListener" );
385
386 return new IdentityProviderBindingBuilderImpl<I>( (TypeLiteral<I>) injectionType, scopeAnnotation, scope,
387 provisionListener, annotationType, cacheable );
388 }
389
390 @SuppressWarnings( "unchecked" )
391 public <I, A extends Annotation> CombinedListenerCacheableBindingBuilder cacheable()
392 {
393 return new IdentityProviderBindingBuilderImpl<I>( (TypeLiteral<I>) injectionType, scopeAnnotation, scope,
394 provisionListener, annotationType, true );
395 }
396
397 @SuppressWarnings( "unchecked" )
398 public <I, A extends Annotation> CombinedListenerCacheableBindingBuilder nonCacheable()
399 {
400 return new IdentityProviderBindingBuilderImpl<I>( (TypeLiteral<I>) injectionType, scopeAnnotation, scope,
401 provisionListener, annotationType, false );
402 }
403
404 @SuppressWarnings( "unchecked" )
405 private ScopedKey<T> buildScopedKey( final Class<?> type, final Class<? extends Annotation> annotationType,
406 final Class<? extends Annotation> scopeAnnotation, final Scope scope )
407 {
408
409 final Scoping scoping;
410 if ( scopeAnnotation != null )
411 {
412 scoping = Scoping.forAnnotation( scopeAnnotation );
413
414 }
415 else if ( scope != null )
416 {
417 scoping = Scoping.forInstance( scope );
418
419 }
420 else
421 {
422 scoping = null;
423 }
424
425 return new ScopedKey<T>( (Key<T>) Key.get( type, annotationType ), scoping );
426 }
427 }
428
429 private class ScopedKey<T>
430 {
431 private final Key<T> key;
432
433 private final Scoping scoping;
434
435 private ScopedKey( final Key<T> key, final Scoping scoping )
436 {
437 this.key = key;
438 this.scoping = scoping;
439 }
440
441 @Override
442 public int hashCode()
443 {
444 final int prime = 31;
445 int result = 1;
446 result = prime * result + getOuterType().hashCode();
447 result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
448 result = prime * result + ( ( scoping == null ) ? 0 : scoping.hashCode() );
449 return result;
450 }
451
452 @Override
453 public boolean equals( final Object obj )
454 {
455 if ( this == obj )
456 {
457 return true;
458 }
459 if ( obj == null )
460 {
461 return false;
462 }
463 if ( getClass() != obj.getClass() )
464 {
465 return false;
466 }
467 final ScopedKey<?> other = (ScopedKey<?>) obj;
468 if ( !getOuterType().equals( other.getOuterType() ) )
469 {
470 return false;
471 }
472 if ( key == null )
473 {
474 if ( other.key != null )
475 {
476 return false;
477 }
478 }
479 else if ( !key.equals( other.key ) )
480 {
481 return false;
482 }
483 if ( scoping == null )
484 {
485 if ( other.scoping != null )
486 {
487 return false;
488 }
489 }
490 else if ( !scoping.equals( other.scoping ) )
491 {
492 return false;
493 }
494 return true;
495 }
496
497 private FactoryAnnotationModule getOuterType()
498 {
499 return FactoryAnnotationModule.this;
500 }
501
502 }
503
504 }