View Javadoc
1   /**
2    * This file Copyright (c) 2017-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.framework.ioc;
35  
36  import java.lang.annotation.Annotation;
37  import java.util.Collection;
38  import java.util.Optional;
39  import java.util.function.Consumer;
40  import java.util.function.Function;
41  import java.util.stream.Stream;
42  
43  import com.google.inject.Binding;
44  import com.google.inject.Key;
45  import com.google.inject.Scope;
46  import com.google.inject.internal.Scoping;
47  import com.google.inject.spi.ConstructorBinding;
48  import com.google.inject.spi.DefaultBindingScopingVisitor;
49  import com.google.inject.spi.DefaultBindingTargetVisitor;
50  import com.google.inject.spi.Element;
51  import com.google.inject.spi.LinkedKeyBinding;
52  import com.google.inject.spi.ProviderInstanceBinding;
53  import com.google.inject.spi.ProviderKeyBinding;
54  import com.google.inject.spi.UntargettedBinding;
55  
56  /**
57   * Utility class which helps to write a slightly more compact and
58   * modern-looking code when dealing with Guice SPI API's. The
59   * latter are quite powerful but utilise the visitor pattern quite
60   * extensively forcing the developer to use multiple inline vistor
61   * classes which makes the code noisy.
62   * <p>
63   * Current utility allows to traverse and inspect the {@link Element elements}
64   * and {@link Binding bindings} with Java 8 lambdas and streams.
65   * </p>
66   */
67  public class GuiceSpi {
68  
69      /**
70       * Takes a collection of {@link Element elements} and presents it
71       * a stream of {@link Binding bindings} skipping all the elements
72       * that are not bindings on the way. This method is useful since
73       * most of the time when we are processing the Guice module elements
74       * we are only interested in bindings.
75       */
76      public static Stream<Binding> getBindings(Collection<Element> elements) {
77          return elements.stream()
78                  .filter(element -> element instanceof Binding)
79                  .map(element -> (Binding) element);
80      }
81  
82      public static <T> Optional<Binding<T>> findBinding(Collection<Element> elements, Key<T> key) {
83          return getBindings(elements).filter(binding -> binding.getKey().equals(key)).map(binding -> (Binding<T>) binding).findFirst();
84      }
85  
86      public static <T> Class<? super T> rawType(Key<T> key) {
87          return key.getTypeLiteral().getRawType();
88      }
89  
90      /**
91       * Resolves the {@link Scoping scoping} information of
92       * the {@link Binding binding}.
93       */
94      public static Scoping resolveScope(Binding<?> currentBinding) {
95          return currentBinding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scoping>() {
96              @Override
97              public Scoping visitEagerSingleton() {
98                  return Scoping.EAGER_SINGLETON;
99              }
100 
101             @Override
102             public Scoping visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
103                 return Scoping.forAnnotation(scopeAnnotation);
104             }
105 
106             @Override
107             public Scoping visitScope(Scope scope) {
108                 return Scoping.forInstance(scope);
109             }
110 
111             @Override
112             public Scoping visitNoScoping() {
113                 return Scoping.UNSCOPED;
114             }
115         });
116     }
117 
118     /**
119      * Shortcut provider of {@link BindingVisitor} instance.
120      */
121     public static <R> BindingVisitor<R> inspect(Binding<?> binding) {
122         return new BindingVisitor<>(binding);
123     }
124 
125     /**
126      * Wraps {@link DefaultBindingTargetVisitor} class into a lambda-friendly fluent API.
127      * Provides methods to register handlers of the {@link LinkedKeyBinding}, {@link ProviderKeyBinding} and
128      * {@link ProviderInstanceBinding} since those are the only that Magnolia UI IoC implementation is
129      * currently interested at. Handlers can be registered as functions (i.e. will return a value) or as
130      * consumers (i.e. not required to return anything).
131      * <p>
132      * <strong>NOTE:</strong> feel free to add handlers for other types of bindings should that be needed!
133      * </p>
134      *
135      * @param <R>
136      *     type of the result returned by SPI call
137      */
138     public static class BindingVisitor<R> {
139 
140         private final Binding<?> binding;
141         private Function<LinkedKeyBinding, R> linkedKeyBindingHandler = binding -> null;
142         private Function<ProviderKeyBinding, R> providerKeyBindingHandler = binding -> null;
143         private Function<ProviderInstanceBinding, R> providerInstanceBindingHandler = binding -> null;
144         private Function<UntargettedBinding, R> untargettedBindingHandler = binding -> null;
145         private Function<ConstructorBinding<?>,R> constructorBindingHandler = binding -> null;
146 
147         private BindingVisitor(Binding<?> binding) {
148             this.binding = binding;
149         }
150 
151         public BindingVisitor<R> onConstructorBinding(Function<ConstructorBinding<?>, R> handler) {
152             this.constructorBindingHandler = handler;
153             return this;
154         }
155 
156         public BindingVisitor<R> consumeConstructorBinding(Consumer<ConstructorBinding<?>> handler) {
157             this.constructorBindingHandler = constructorBinding -> {
158                 handler.accept(constructorBinding);
159                 return null;
160             };
161 
162             return this;
163         }
164 
165         public BindingVisitor<R> onLinkedBinding(Function<LinkedKeyBinding, R> handler) {
166             this.linkedKeyBindingHandler = handler;
167             return this;
168         }
169 
170         public BindingVisitor<R> consumeLinkedBinding(Consumer<LinkedKeyBinding> handler) {
171             this.linkedKeyBindingHandler = binding -> {
172                 handler.accept(binding);
173                 return null;
174             };
175             return this;
176         }
177 
178         public BindingVisitor<R> onProviderKeyBinding(Function<ProviderKeyBinding, R> handler) {
179             this.providerKeyBindingHandler = handler;
180             return this;
181         }
182 
183         public BindingVisitor<R> onProviderInstanceBinding(Function<ProviderInstanceBinding, R> handler) {
184             this.providerInstanceBindingHandler = handler;
185             return this;
186         }
187 
188         public BindingVisitor<R> onUntargettedBinding(Function<UntargettedBinding, R> handler) {
189             this.untargettedBindingHandler = handler;
190             return this;
191         }
192 
193         public BindingVisitor<R> consumeUntargettedBinding(Consumer<UntargettedBinding<?>> handler) {
194             this.untargettedBindingHandler = untargettedBinding -> {
195                 handler.accept(untargettedBinding);
196                 return null;
197             };
198 
199             return this;
200         }
201 
202         public BindingVisitor<R> consumeProviderInstanceBinding(Consumer<ProviderInstanceBinding> handler) {
203             this.providerInstanceBindingHandler = binding -> {
204                 handler.accept(binding);
205                 return null;
206             };
207             return this;
208         }
209 
210         public <T> BindingVisitor<R> consumeProviderKeyBinding(Consumer<ProviderKeyBinding<T>> handler) {
211             this.providerKeyBindingHandler = binding -> {
212                 handler.accept(binding);
213                 return null;
214             };
215             return this;
216         }
217 
218         public Optional<R> visit() {
219             return binding.acceptTargetVisitor(new DefaultBindingTargetVisitor<Object, Optional<R>>() {
220                 @Override
221                 public Optional<R> visit(ConstructorBinding<?> constructorBinding) {
222                     return Optional.ofNullable(constructorBindingHandler.apply(constructorBinding));
223                 }
224 
225                 @Override
226                 public Optional<R> visit(ProviderKeyBinding<?> providerKeyBinding) {
227                     return Optional.ofNullable(providerKeyBindingHandler.apply(providerKeyBinding));
228                 }
229 
230                 @Override
231                 public Optional<R> visit(LinkedKeyBinding<?> linkedKeyBinding) {
232                     return Optional.ofNullable(linkedKeyBindingHandler.apply(linkedKeyBinding));
233                 }
234 
235                 @Override
236                 public Optional<R> visit(ProviderInstanceBinding<?> providerInstanceBinding) {
237                     return Optional.ofNullable(providerInstanceBindingHandler.apply(providerInstanceBinding));
238                 }
239 
240                 @Override
241                 public Optional<R> visit(UntargettedBinding<?> untargettedBinding) {
242                     return Optional.ofNullable(untargettedBindingHandler.apply(untargettedBinding));
243                 }
244 
245                 @Override
246                 protected Optional<R> visitOther(Binding<?> binding) {
247                     return Optional.empty();
248                 }
249             });
250         };
251     }
252 }