Bindings.java
001 /*
002  * Copyright 2008-2017 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.injection;
017 
018 import griffon.core.injection.Binding;
019 import griffon.core.injection.InstanceBinding;
020 import griffon.core.injection.ProviderBinding;
021 import griffon.core.injection.ProviderTypeBinding;
022 import griffon.core.injection.TargetBinding;
023 import griffon.util.AnnotationUtils;
024 
025 import javax.annotation.Nonnull;
026 import javax.annotation.Nullable;
027 import javax.inject.Provider;
028 import javax.inject.Qualifier;
029 import java.lang.annotation.Annotation;
030 import java.util.List;
031 
032 import static griffon.util.AnnotationUtils.harvestQualifiers;
033 import static java.util.Objects.requireNonNull;
034 
035 /**
036  @author Andres Almiray
037  @since 2.0.0
038  */
039 public class Bindings {
040     private Bindings() {
041         // prevent instantiation
042     }
043 
044     public static <T> AnnotatedBindingBuilder<T> bind(@Nonnull Class<T> clazz) {
045         requireNonNull(clazz, "Argument 'class' must not be null");
046         return new AnnotatedBindingBuilderImpl<>(clazz);
047     }
048 
049     private abstract static class SingletonBindingBuilderImpl<T> implements SingletonBindingBuilder<T> {
050         protected boolean singleton;
051 
052         @Override
053         public void asSingleton() {
054             singleton = true;
055         }
056     }
057 
058     private abstract static class LinkedBindingBuilderImpl<T> extends SingletonBindingBuilderImpl<T> implements LinkedBindingBuilder<T> {
059         protected Class<? extends T> target;
060         protected T instance;
061         protected Provider<T> provider;
062         protected Class<? extends Provider<T>> providerType;
063 
064         @Nonnull
065         @Override
066         public SingletonBindingBuilder<T> to(@Nonnull Class<? extends T> target) {
067             this.target = requireNonNull(target, "Argument 'target' must not be null");
068             return this;
069         }
070 
071         @Override
072         public void toInstance(@Nonnull T instance) {
073             this.instance = requireNonNull(instance, "Argument 'instance' must not be null");
074         }
075 
076         @Nonnull
077         @Override
078         public SingletonBindingBuilder<T> toProvider(@Nonnull Provider<T> provider) {
079             this.provider = requireNonNull(provider, "Argument 'provider' must not be null");
080             return this;
081         }
082 
083         @Nonnull
084         @Override
085         public SingletonBindingBuilder<T> toProvider(@Nonnull Class<? extends Provider<T>> providerType) {
086             this.providerType = requireNonNull(providerType, "Argument 'providerType' must not be null");
087             return this;
088         }
089     }
090 
091     private static class AnnotatedBindingBuilderImpl<T> extends LinkedBindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> {
092         private final Class<T> source;
093         private Annotation classifier;
094         private Class<? extends Annotation> classifierType;
095 
096         private AnnotatedBindingBuilderImpl(@Nonnull Class<T> source) {
097             this.source = requireNonNull(source, "Argument 'source' must not be null");
098         }
099 
100         @Nonnull
101         @Override
102         public Binding<T> getBinding() {
103             if (instance != null) {
104                 return classifier != null new InstanceBindingImpl<>(source, classifier, instancenew InstanceBindingImpl<>(source, classifierType, instance);
105             else if (providerType != null) {
106                 return classifier != null new ProviderTypeBindingImpl<>(source, providerType, classifier, singletonnew ProviderTypeBindingImpl<>(source, providerType, classifierType, singleton);
107             else if (provider != null) {
108                 return classifier != null new ProviderBindingImpl<>(source, provider, classifier, singletonnew ProviderBindingImpl<>(source, provider, classifierType, singleton);
109             else if (target != null) {
110                 return classifier != null new TargetBindingImpl<>(source, target, classifier, singletonnew TargetBindingImpl<>(source, target, classifierType, singleton);
111             }
112 
113             return classifier != null new TargetBindingImpl<>(source, source, classifier, singletonnew TargetBindingImpl<>(source, source, classifierType, singleton);
114         }
115 
116         @Nonnull
117         @Override
118         public LinkedBindingBuilder<T> withClassifier(@Nonnull Class<? extends Annotation> annotationType) {
119             requireNonNull(annotationType, "Argument 'annotationType' must not be null");
120             AnnotationUtils.requireAnnotation(annotationType, Qualifier.class);
121             this.classifierType = annotationType;
122             return this;
123         }
124 
125         @Nonnull
126         @Override
127         public LinkedBindingBuilder<T> withClassifier(@Nonnull Annotation annotation) {
128             requireNonNull(annotation, "Argument 'annotation' must not be null");
129             this.classifier = annotation;
130             withClassifier(annotation.getClass());
131             return this;
132         }
133     }
134 
135     private abstract static class AbstractBindingImpl<T> implements Binding<T> {
136         protected final Class<T> source;
137         protected final boolean singleton;
138         protected Annotation classifier;
139         protected Class<? extends Annotation> classifierType;
140 
141         protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, boolean singleton) {
142             this.source = source;
143             this.singleton = singleton;
144             this.classifier = classifier;
145             this.classifierType = classifier.getClass();
146         }
147 
148         protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
149             this.source = source;
150             this.singleton = singleton;
151             this.classifierType = classifierType;
152         }
153 
154         @Nonnull
155         @Override
156         public Class<T> getSource() {
157             return source;
158         }
159 
160         @Nullable
161         @Override
162         public Class<? extends Annotation> getClassifierType() {
163             return classifierType;
164         }
165 
166         @Nullable
167         @Override
168         public Annotation getClassifier() {
169             return classifier;
170         }
171 
172         @Override
173         public boolean isSingleton() {
174             return singleton;
175         }
176 
177         protected void updateClassifier(Class<?> klass) {
178             if (this.classifier == null) {
179                 List<Annotation> qualifiers = harvestQualifiers(klass);
180                 if (!qualifiers.isEmpty()) {
181                     this.classifier = qualifiers.get(0);
182                 }
183             }
184         }
185 
186         protected void updateClassifierType(Class<?> klass) {
187             if (this.classifierType == null) {
188                 List<Annotation> qualifiers = harvestQualifiers(klass);
189                 if (!qualifiers.isEmpty()) {
190                     this.classifier = qualifiers.get(0);
191                 }
192             }
193         }
194     }
195 
196     private static class TargetBindingImpl<T> extends AbstractBindingImpl<T> implements TargetBinding<T> {
197         private final Class<? extends T> target;
198 
199         private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Annotation classifier, boolean singleton) {
200             super(source, classifier, singleton);
201             this.target = target;
202             updateClassifier(target);
203         }
204 
205         private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
206             super(source, classifierType, singleton);
207             this.target = target;
208             updateClassifierType(target);
209         }
210 
211         @Nonnull
212         @Override
213         public Class<? extends T> getTarget() {
214             return target;
215         }
216 
217         @Override
218         public String toString() {
219             final StringBuilder sb = new StringBuilder("TargetBinding[");
220             sb.append("source=").append(source.getName());
221             if (classifier != null) {
222                 sb.append(", classifier=").append(classifier);
223             else if (classifierType != null) {
224                 sb.append(", classifierType=").append(classifierType.getName());
225             }
226             sb.append(", target=").append(target.getName());
227             sb.append(", singleton=").append(singleton);
228             sb.append(']');
229             return sb.toString();
230         }
231     }
232 
233     private static class InstanceBindingImpl<T> extends AbstractBindingImpl<T> implements InstanceBinding<T> {
234         private final T instance;
235 
236         protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, @Nonnull T instance) {
237             super(source, classifier, true);
238             this.instance = instance;
239             updateClassifier(instance.getClass());
240         }
241 
242         protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, @Nonnull T instance) {
243             super(source, classifierType, true);
244             this.instance = instance;
245             updateClassifierType(instance.getClass());
246         }
247 
248         @Nonnull
249         @Override
250         public T getInstance() {
251             return instance;
252         }
253 
254         @Override
255         public String toString() {
256             final StringBuilder sb = new StringBuilder("InstanceBinding[");
257             sb.append("source=").append(source.getName());
258             if (classifier != null) {
259                 sb.append(", classifier=").append(classifier);
260             else if (classifierType != null) {
261                 sb.append(", classifierType=").append(classifierType.getName());
262             }
263             sb.append(", instance=").append(instance);
264             sb.append(", singleton=").append(singleton);
265             sb.append(']');
266             return sb.toString();
267         }
268     }
269 
270     private static class ProviderBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderBinding<T> {
271         private final Provider<T> provider;
272 
273         private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Annotation classifier, boolean singleton) {
274             super(source, classifier, singleton);
275             this.provider = provider;
276             updateClassifier(provider.getClass());
277         }
278 
279 
280         private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
281             super(source, classifierType, singleton);
282             this.provider = provider;
283             updateClassifierType(provider.getClass());
284         }
285 
286         @Nonnull
287         @Override
288         public Provider<T> getProvider() {
289             return provider;
290         }
291 
292         @Override
293         public String toString() {
294             final StringBuilder sb = new StringBuilder("ProviderBinding[");
295             sb.append("source=").append(source.getName());
296             if (classifier != null) {
297                 sb.append(", classifier=").append(classifier);
298             else if (classifierType != null) {
299                 sb.append(", classifierType=").append(classifierType.getName());
300             }
301             sb.append(", provider=").append(provider);
302             sb.append(", singleton=").append(singleton);
303             sb.append(']');
304             return sb.toString();
305         }
306     }
307 
308     private static class ProviderTypeBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderTypeBinding<T> {
309         private final Class<? extends Provider<T>> providerType;
310 
311         private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Annotation classifier, boolean singleton) {
312             super(source, classifier, singleton);
313             this.providerType = providerType;
314             updateClassifier(providerType);
315         }
316 
317 
318         private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
319             super(source, classifierType, singleton);
320             this.providerType = providerType;
321             updateClassifierType(providerType);
322         }
323 
324         @Nonnull
325         @Override
326         public Class<? extends Provider<T>> getProviderType() {
327             return providerType;
328         }
329 
330         @Override
331         public String toString() {
332             final StringBuilder sb = new StringBuilder("ProviderTypeBinding[");
333             sb.append("source=").append(source.getName());
334             if (classifier != null) {
335                 sb.append(", classifier=").append(classifier);
336             else if (classifierType != null) {
337                 sb.append(", classifierType=").append(classifierType.getName());
338             }
339             sb.append(", providerType=").append(providerType.getName());
340             sb.append(", singleton=").append(singleton);
341             sb.append(']');
342             return sb.toString();
343         }
344     }
345 }