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