View Javadoc
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.maven.di.impl;
20  
21  import java.lang.annotation.Annotation;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.HashSet;
26  import java.util.Set;
27  import java.util.function.Consumer;
28  import java.util.function.Function;
29  import java.util.function.Supplier;
30  import java.util.stream.Collectors;
31  import java.util.stream.Stream;
32  
33  import org.apache.maven.di.Key;
34  
35  public abstract class Binding<T> {
36      private final Set<Dependency<?>> dependencies;
37      private Annotation scope;
38      private int priority;
39      private Key<?> originalKey;
40  
41      protected Binding(Key<? extends T> originalKey, Set<Dependency<?>> dependencies) {
42          this(originalKey, dependencies, null, 0);
43      }
44  
45      protected Binding(Key<?> originalKey, Set<Dependency<?>> dependencies, Annotation scope, int priority) {
46          this.originalKey = originalKey;
47          this.dependencies = dependencies;
48          this.scope = scope;
49          this.priority = priority;
50      }
51  
52      public static <T> Binding<T> toInstance(T instance) {
53          return new BindingToInstance<>(instance);
54      }
55  
56      public static <T> Binding<T> toSupplier(Supplier<T> supplier) {
57          return new BindingToSupplier<>(supplier);
58      }
59  
60      public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> constructor, Class<?>[] types) {
61          return Binding.to(
62                  originalKey,
63                  constructor,
64                  Stream.of(types).map(c -> new Dependency<>(Key.of(c), false)).toArray(Dependency<?>[]::new));
65      }
66  
67      public static <R> Binding<R> to(
68              Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies) {
69          return to(originalKey, constructor, dependencies, 0);
70      }
71  
72      public static <R> Binding<R> to(
73              Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies, int priority) {
74          return new BindingToConstructor<>(originalKey, constructor, dependencies, priority);
75      }
76  
77      // endregion
78  
79      public Binding<T> scope(Annotation scope) {
80          this.scope = scope;
81          return this;
82      }
83  
84      public Binding<T> prioritize(int priority) {
85          this.priority = priority;
86          return this;
87      }
88  
89      public Binding<T> withKey(Key<?> key) {
90          this.originalKey = key;
91          return this;
92      }
93  
94      public Binding<T> initializeWith(BindingInitializer<T> bindingInitializer) {
95          return new Binding<T>(
96                  this.originalKey,
97                  Stream.of(this.dependencies, bindingInitializer.getDependencies())
98                          .flatMap(Set::stream)
99                          .collect(Collectors.toSet()),
100                 this.scope,
101                 this.priority) {
102             @Override
103             public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
104                 final Supplier<T> compiledBinding = Binding.this.compile(compiler);
105                 final Consumer<T> consumer = bindingInitializer.compile(compiler);
106                 return () -> {
107                     try {
108                         T instance = compiledBinding.get();
109                         consumer.accept(instance);
110                         return instance;
111                     } catch (DIException e) {
112                         throw new DIException("Error while initializing binding " + Binding.this, e);
113                     }
114                 };
115             }
116 
117             @Override
118             public String toString() {
119                 return Binding.this.toString();
120             }
121         };
122     }
123 
124     public abstract Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler);
125 
126     public Set<Dependency<?>> getDependencies() {
127         return dependencies;
128     }
129 
130     public Annotation getScope() {
131         return scope;
132     }
133 
134     public Key<?> getOriginalKey() {
135         return originalKey;
136     }
137 
138     public int getPriority() {
139         return priority;
140     }
141 
142     @Override
143     public String toString() {
144         return "Binding" + dependencies.toString();
145     }
146 
147     @FunctionalInterface
148     public interface TupleConstructorN<R> {
149         R create(Object... args);
150     }
151 
152     public static Comparator<Binding<?>> getPriorityComparator() {
153         return Comparator.<Binding<?>>comparingInt(Binding::getPriority).reversed();
154     }
155 
156     public static class BindingToInstance<T> extends Binding<T> {
157         final T instance;
158 
159         public BindingToInstance(T instance) {
160             super(null, Collections.emptySet());
161             this.instance = instance;
162         }
163 
164         @Override
165         public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
166             return () -> instance;
167         }
168 
169         @Override
170         public String toString() {
171             return "BindingToInstance[" + instance + "]" + getDependencies();
172         }
173     }
174 
175     public static class BindingToSupplier<T> extends Binding<T> {
176         final Supplier<T> supplier;
177 
178         public BindingToSupplier(Supplier<T> supplier) {
179             super(null, Collections.emptySet());
180             this.supplier = supplier;
181         }
182 
183         @Override
184         public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
185             return supplier;
186         }
187 
188         @Override
189         public String toString() {
190             return "BindingToSupplier[" + supplier + "]" + getDependencies();
191         }
192     }
193 
194     public static class BindingToConstructor<T> extends Binding<T> {
195         final TupleConstructorN<T> constructor;
196         final Dependency<?>[] args;
197 
198         BindingToConstructor(
199                 Key<? extends T> key, TupleConstructorN<T> constructor, Dependency<?>[] dependencies, int priority) {
200             super(key, new HashSet<>(Arrays.asList(dependencies)), null, priority);
201             this.constructor = constructor;
202             this.args = dependencies;
203         }
204 
205         @Override
206         public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
207             return () -> {
208                 Object[] args =
209                         Stream.of(this.args).map(compiler).map(Supplier::get).toArray();
210                 return constructor.create(args);
211             };
212         }
213 
214         @Override
215         public String toString() {
216             return "BindingToConstructor[" + getOriginalKey() + "]" + getDependencies();
217         }
218     }
219 }