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 }