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