Coverage Report - org.apache.onami.factoryannotation.FactoryAnnotationProviderFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
FactoryAnnotationProviderFactory
88%
55/62
90%
29/32
3
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.onami.factoryannotation;
 20  
 
 21  
 import static com.google.common.base.Preconditions.checkNotNull;
 22  
 
 23  
 import java.lang.annotation.Annotation;
 24  
 import java.lang.reflect.Field;
 25  
 import java.util.List;
 26  
 import java.util.Set;
 27  
 
 28  
 import com.google.common.collect.ImmutableSet;
 29  
 import com.google.inject.Binder;
 30  
 import com.google.inject.Key;
 31  
 import com.google.inject.ProvisionException;
 32  
 import com.google.inject.TypeLiteral;
 33  
 import com.google.inject.matcher.Matchers;
 34  
 import com.google.inject.spi.BindingTargetVisitor;
 35  
 import com.google.inject.spi.DefaultBindingTargetVisitor;
 36  
 import com.google.inject.spi.Dependency;
 37  
 import com.google.inject.spi.DependencyAndSource;
 38  
 import com.google.inject.spi.HasDependencies;
 39  
 import com.google.inject.spi.InjectionPoint;
 40  
 import com.google.inject.spi.ProviderInstanceBinding;
 41  
 import com.google.inject.spi.ProviderWithExtensionVisitor;
 42  
 import com.google.inject.spi.ProvisionListener;
 43  
 import com.google.inject.spi.TypeEncounter;
 44  
 import com.google.inject.spi.TypeListener;
 45  
 
 46  
 final class FactoryAnnotationProviderFactory<T, A extends Annotation>
 47  
     extends DefaultBindingTargetVisitor<T, Object>
 48  
     implements TypeListener, ProviderWithExtensionVisitor<T>, HasDependencies, ProvisionListener,
 49  
     FactoryAnnotationBinding<T, A>
 50  
 {
 51  
 
 52  
     private final Class<A> annotationType;
 53  
 
 54  
     private final FactoryAnnotationProvider<T, A> factoryAnnotationProvider;
 55  
 
 56  
     private final Key<FactoryAnnotationProvider<T, A>> targetKey;
 57  
 
 58  
     private final TypeLiteral<T> elementTypeLiteral;
 59  
 
 60  
     private final FactoryAnnotationProvisionListener<T, A> provisionListener;
 61  
 
 62  140
     private final ThreadLocal<A> provision = new ThreadLocal<A>();
 63  
 
 64  
     @SuppressWarnings( "unchecked" )
 65  
     FactoryAnnotationProviderFactory( final Binder binder, final Class<A> annotationType,
 66  
                                       final FactoryAnnotationProvider<T, A> factoryAnnotationProvider,
 67  
                                       final FactoryAnnotationProvisionListener<T, A> listener, final boolean cacheable )
 68  140
     {
 69  
 
 70  140
         this.annotationType = annotationType;
 71  
 
 72  140
         this.provisionListener = listener;
 73  
 
 74  140
         this.targetKey = (Key<FactoryAnnotationProvider<T, A>>) Key.get( factoryAnnotationProvider.getClass() );
 75  
 
 76  140
         this.elementTypeLiteral = TypeLiteral.get( factoryAnnotationProvider.getInjectionType() );
 77  
 
 78  140
         final FactoryAnnotationProvider<T, A> adapter =
 79  
             new FactoryAnnotationProviderInjectionAdapter<T, A>( factoryAnnotationProvider );
 80  
 
 81  
         // Request injection of the FactoryAnnotationProvider and the adapter before we
 82  
         // want to use it to meet possible requirements of the inner
 83  
         // implementation
 84  140
         binder.requestInjection( adapter );
 85  140
         binder.requestInjection( factoryAnnotationProvider );
 86  
 
 87  
         // If values can be cached initialize the caching map
 88  140
         if ( cacheable )
 89  
         {
 90  8
             this.factoryAnnotationProvider = new ProvisionCacheIdentityProviderFacade<T, A>( adapter, elementTypeLiteral );
 91  
 
 92  
             // Now we need to request injection for cache provider as well
 93  8
             binder.requestInjection( this.factoryAnnotationProvider );
 94  
 
 95  
         }
 96  
         else
 97  
         {
 98  132
             this.factoryAnnotationProvider = adapter;
 99  
         }
 100  
 
 101  140
         binder.bindListener( Matchers.any(), this );
 102  140
         binder.bindListener( Matchers.any(), (ProvisionListener) this );
 103  140
     }
 104  
 
 105  
     public T get()
 106  
     {
 107  104
         final A annotation = provision.get();
 108  104
         if ( annotation == null )
 109  
         {
 110  0
             throw new ProvisionException( "No legal provision" );
 111  
         }
 112  
 
 113  
         // Delete value from ThreadLocal store
 114  104
         provision.set( null );
 115  
 
 116  
         // Return the provisioned (real) value
 117  104
         return factoryAnnotationProvider.buildValue( annotation );
 118  
     }
 119  
 
 120  
     public <I> void hear( final TypeLiteral<I> type, final TypeEncounter<I> encounter )
 121  
     {
 122  
 
 123  
         // Search for fields that needs to be injected
 124  3308
         for ( final Field field : type.getRawType().getDeclaredFields() )
 125  
         {
 126  2380
             if ( field.isAnnotationPresent( annotationType ) )
 127  
             {
 128  176
                 final A annotation = annotationType.cast( field.getAnnotation( annotationType ) );
 129  
 
 130  176
                 final Set<InjectionPoint> injectionPoints =
 131  
                     InjectionPoint.forInstanceMethodsAndFields( type.getRawType() );
 132  
 
 133  176
                 FactoryAnnotationProvision<T, A> identityProvision = null;
 134  176
                 if ( injectionPoints.size() > 0 )
 135  
                 {
 136  20
                     for ( final InjectionPoint ip : injectionPoints )
 137  
                     {
 138  36
                         if ( ip.getMember().equals( field ) )
 139  
                         {
 140  20
                             identityProvision = FactoryAnnotationProvisionImpl.wrapInjectionPoint( ip, annotation );
 141  
                         }
 142  
                     }
 143  
 
 144  
                 }
 145  
 
 146  176
                 if ( identityProvision == null )
 147  
                 {
 148  156
                     identityProvision =
 149  
                         new FactoryAnnotationProvisionImpl<T, A>( field, annotation, field.getAnnotations(),
 150  
                                                                   elementTypeLiteral );
 151  
                 }
 152  
 
 153  
                 // Register a MembersInjector per field which actually will do
 154  
                 // the injection for Guice
 155  176
                 encounter.register( FactoryAnnotationSingleFieldMemberInjector.buildMemberInjector( factoryAnnotationProvider,
 156  
                                                                                                     field,
 157  
                                                                                                     provisionListener,
 158  
                                                                                                     annotation,
 159  
                                                                                                     identityProvision ) );
 160  
 
 161  
             }
 162  
         }
 163  928
     }
 164  
 
 165  
     public <I> void onProvision( final ProvisionInvocation<I> provision )
 166  
     {
 167  184
         final List<DependencyAndSource> dependencies = provision.getDependencyChain();
 168  
 
 169  
         // OK here we are at constructor or method injection so let's get the
 170  
         // real annotation at the type
 171  184
         for ( final DependencyAndSource dependency : dependencies )
 172  
         {
 173  316
             if ( dependency.getDependency() == null )
 174  
             {
 175  40
                 continue;
 176  
             }
 177  
 
 178  276
             final Annotation annotation = dependency.getDependency().getKey().getAnnotation();
 179  
 
 180  276
             if ( annotation != null )
 181  
             {
 182  132
                 if ( annotation.annotationType().equals( annotationType ) )
 183  
                 {
 184  
                     // We found the real annotation, let's save it for the
 185  
                     // actual provision call!
 186  104
                     final A specificAnnotation = annotationType.cast( annotation );
 187  104
                     this.provision.set( specificAnnotation );
 188  
 
 189  104
                     final InjectionPoint injectionPoint = dependency.getDependency().getInjectionPoint();
 190  
 
 191  104
                     if ( injectionPoint == null )
 192  
                     {
 193  0
                         checkNotNull( injectionPoint );
 194  
                     }
 195  
 
 196  104
                     final FactoryAnnotationProvision<T, A> identityProvision =
 197  
                         FactoryAnnotationProvisionImpl.wrapInjectionPoint( injectionPoint, specificAnnotation );
 198  
 
 199  
                     // Notify listener that an provision will occur
 200  104
                     if ( provisionListener != null )
 201  
                     {
 202  36
                         provisionListener.beforeInjection( identityProvision, specificAnnotation );
 203  
                     }
 204  
 
 205  
                     // Execute the provision;
 206  104
                     T value = factoryAnnotationProvider.getInjectionType().cast( provision.provision() );
 207  
 
 208  
                     // Notify listener about the provisioned value
 209  104
                     if ( provisionListener != null )
 210  
                     {
 211  36
                         provisionListener.afterInjection( identityProvision, specificAnnotation, value );
 212  
                     }
 213  
 
 214  
                     // Explicitly return at this point
 215  104
                     return;
 216  
                 }
 217  
             }
 218  172
         }
 219  80
     }
 220  
 
 221  
     public Set<Dependency<?>> getDependencies()
 222  
     {
 223  0
         return ImmutableSet.<Dependency<?>> of( Dependency.get( targetKey ) );
 224  
     }
 225  
 
 226  
     @SuppressWarnings( "unchecked" )
 227  
     public <B, V> V acceptExtensionVisitor( final BindingTargetVisitor<B, V> visitor,
 228  
                                             final ProviderInstanceBinding<? extends B> binding )
 229  
     {
 230  
 
 231  140
         if ( visitor instanceof FactoryAnnotationBindungTargetVisitor )
 232  
         {
 233  0
             return ( (FactoryAnnotationBindungTargetVisitor<T, V>) visitor ).visit( this );
 234  
 
 235  
         }
 236  
         else
 237  
         {
 238  140
             return visitor.visit( binding );
 239  
         }
 240  
     }
 241  
 
 242  
     public TypeLiteral<T> getElementTypeLiteral()
 243  
     {
 244  0
         return elementTypeLiteral;
 245  
     }
 246  
 
 247  
     public Class<A> getAnnotationType()
 248  
     {
 249  0
         return annotationType;
 250  
     }
 251  
 
 252  
     public boolean acceptAnnotation( A annotation )
 253  
     {
 254  0
         return getAnnotationType().equals( annotation.annotationType() );
 255  
     }
 256  
 
 257  
 }