View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht;
21  
22  import com.google.common.collect.ArrayListMultimap;
23  import com.google.common.collect.Multimap;
24  import org.grouplens.grapht.context.ContextMatcher;
25  import org.grouplens.grapht.solver.BindRule;
26  import org.grouplens.grapht.solver.BindingFunction;
27  import org.grouplens.grapht.solver.RuleBasedBindingFunction;
28  
29  import java.io.Externalizable;
30  import java.io.Serializable;
31  import java.util.Collections;
32  import java.util.HashSet;
33  import java.util.Set;
34  
35  /**
36   * BindingFunctionBuilder provides a convenient access to the fluent API and
37   * converts calls to {@link Context} and {@link Binding} methods into multiple
38   * {@link BindingFunction BindingFunctions}.
39   * 
40   * @author <a href="http://grouplens.org">GroupLens Research</a>
41   */
42  public class BindingFunctionBuilder implements Cloneable {
43      /**
44       * BindingFunctionBuilder generates three binding functions at separate
45       * priorities.
46       */
47      public static enum RuleSet {
48          /**
49           * Rule set for the explicitly configured rules with the fluent API.
50           */
51          EXPLICIT,
52          /**
53           * Rule set for the intermediate types between a source type (
54           * {@link Context#bind(Class)}) and the target type (
55           * {@link Binding#to(Class)})
56           */
57          INTERMEDIATE_TYPES,
58          /**
59           * Rule set for the super types of the source types of bindings (e.g.
60           * {@link Context#bind(Class)})
61           */
62          SUPER_TYPES
63      }
64      
65      private final Context root;
66      
67      private final Set<Class<?>> defaultExcludes;
68      private final boolean generateRules;
69      
70      private final Multimap<ContextMatcher,BindRule> manualRules;
71      private final Multimap<ContextMatcher,BindRule> intermediateRules; // "generated"
72      private final Multimap<ContextMatcher,BindRule> superRules; // "generated"
73  
74      /**
75       * Create a new InjectorConfigurationBuilder that automatically generates bind rules for
76       * super and intermediate types.
77       */
78      public BindingFunctionBuilder() {
79          this(true);
80      }
81  
82      /**
83       * Create a new InjectorConfigurationBuilder. If <tt>generateRules</tt> is true, bind
84       * rules for super and intermediate types are generated. If it is false,
85       * only one bind rule is created per binding.
86       * 
87       * @param generateRules True if additional bind rules should be generated
88       */
89      public BindingFunctionBuilder(boolean generateRules) {
90          this.generateRules = generateRules;
91  
92          defaultExcludes = new HashSet<Class<?>>();
93          defaultExcludes.add(Object.class);
94          defaultExcludes.add(Comparable.class);
95          defaultExcludes.add(Serializable.class);
96          defaultExcludes.add(Externalizable.class);
97          defaultExcludes.add(Cloneable.class);
98  
99          manualRules = ArrayListMultimap.create();
100         intermediateRules = ArrayListMultimap.create();
101         superRules = ArrayListMultimap.create();
102 
103         root = ContextImpl.root(this);
104     }
105     
106     private BindingFunctionBuilder(BindingFunctionBuilder clone) {
107         generateRules = clone.generateRules;
108         defaultExcludes = new HashSet<Class<?>>(clone.defaultExcludes);
109         manualRules = ArrayListMultimap.create(clone.manualRules);
110         intermediateRules = ArrayListMultimap.create(clone.intermediateRules);
111         superRules = ArrayListMultimap.create(clone.superRules);
112         root = ContextImpl.root(this);
113     }
114     
115     @Override
116     public BindingFunctionBuilder clone() {
117         return new BindingFunctionBuilder(this);
118     }
119     
120     /**
121      * @return True if bind rules for super and intermediate types should be
122      *         generated
123      */
124     public boolean getGenerateRules() {
125         return generateRules;
126     }
127     
128     /**
129      * @return The root context managed by this builder
130      */
131     public Context getRootContext() {
132         return root;
133     }
134 
135     /**
136      * Run the module's {@link Module#configure(Context) bind()} method on the root
137      * context of this builder.
138      * 
139      * @param module The module to apply
140      */
141     public void applyModule(Module module) {
142         module.configure(getRootContext());
143     }
144 
145     /**
146      * Add a type to be excluded from when generating bind rules. This does not
147      * invalidate bindings that bind directly to this type.
148      * 
149      * @param type The type to exclude
150      * @throws NullPointerException if type is null
151      */
152     public void addDefaultExclusion(Class<?> type) {
153         if (type == null) {
154             throw new NullPointerException("Exclusion type cannot be null");
155         }
156         defaultExcludes.add(type);
157     }
158 
159     /**
160      * Remove a type that is currently being excluded.
161      * 
162      * @see #addDefaultExclusion(Class)
163      * @param type The type that should no longer be excluded
164      * @throws NullPointerException if type is null
165      */
166     public void removeDefaultExclusion(Class<?> type) {
167         if (type == null) {
168             throw new NullPointerException("Exclusion type cannot be null");
169         }
170         defaultExcludes.remove(type);
171     }
172     
173     /**
174      * Return the built BindingFunction for the given RuleSet.
175      * 
176      * @param set
177      * @return
178      */
179     public BindingFunction build(RuleSet set) {
180         return new RuleBasedBindingFunction(getMap(set));
181     }
182     
183     void addBindRule(RuleSet set, ContextMatcher context, BindRule rule) {
184         Multimap<ContextMatcher, BindRule> map = getMap(set);
185         map.put(context, rule);
186     }
187 
188     Set<Class<?>> getDefaultExclusions() {
189         return Collections.unmodifiableSet(defaultExcludes);
190     }
191 
192     private Multimap<ContextMatcher, BindRule> getMap(RuleSet set) {
193         switch(set) {
194         case EXPLICIT:
195             return manualRules;
196         case INTERMEDIATE_TYPES:
197             return intermediateRules;
198         case SUPER_TYPES:
199             return superRules;
200         default:
201             throw new RuntimeException("Should not happen");
202         }
203     }
204 }