1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.solver;
21
22 import org.apache.commons.lang3.builder.EqualsBuilder;
23 import org.apache.commons.lang3.builder.HashCodeBuilder;
24 import org.grouplens.grapht.CachePolicy;
25 import org.grouplens.grapht.reflect.Desire;
26 import org.grouplens.grapht.reflect.QualifierMatcher;
27 import org.grouplens.grapht.reflect.Satisfaction;
28 import org.grouplens.grapht.util.ClassProxy;
29 import org.grouplens.grapht.util.Preconditions;
30 import org.grouplens.grapht.util.Types;
31
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 import java.io.InvalidObjectException;
35 import java.io.ObjectInputStream;
36 import java.io.ObjectStreamException;
37 import java.io.Serializable;
38 import java.util.EnumSet;
39
40
41
42
43
44
45 final class BindRuleImpl implements BindRule, Serializable {
46 private static final long serialVersionUID = -1L;
47
48 private final Satisfaction satisfaction;
49 private final EnumSet<BindingFlag> flags;
50
51 private final QualifierMatcher qualifier;
52 private final Class<?> depType;
53 private final Class<?> implType;
54
55 private final CachePolicy policy;
56
57 private transient volatile int hashCode;
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public BindRuleImpl(@Nonnull Class<?> depType,
72 @Nonnull Satisfaction satisfaction,
73 @Nonnull CachePolicy policy,
74 @Nonnull QualifierMatcher qualifier,
75 EnumSet<BindingFlag> flags) {
76 Preconditions.notNull("dependency type", depType);
77 Preconditions.notNull("satisfaction", satisfaction);
78 Preconditions.notNull("policy", policy);
79 Preconditions.notNull("qualifier matcher", qualifier);
80
81 this.qualifier = qualifier;
82 this.satisfaction = satisfaction;
83 this.implType = satisfaction.getErasedType();
84 this.policy = policy;
85 this.depType = Types.box(depType);
86 this.flags = flags.clone();
87
88
89 Preconditions.isAssignable(this.depType, this.implType);
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public BindRuleImpl(@Nonnull Class<?> depType,
105 @Nonnull Class<?> implType,
106 @Nonnull CachePolicy policy,
107 @Nonnull QualifierMatcher qualifier,
108 EnumSet<BindingFlag> flags) {
109 Preconditions.notNull("dependency type", depType);
110 Preconditions.notNull("implementation type", implType);
111 Preconditions.notNull("policy", policy);
112 Preconditions.notNull("qualifier matcher", qualifier);
113
114 this.qualifier = qualifier;
115 this.satisfaction = null;
116 this.implType = Types.box(implType);
117 this.policy = policy;
118 this.depType = Types.box(depType);
119 this.flags = flags.clone();
120
121
122 Preconditions.isAssignable(this.depType, this.implType);
123 }
124
125
126
127
128
129
130 public QualifierMatcher getQualifierMatcher() {
131 return qualifier;
132 }
133
134 @Override
135 public CachePolicy getCachePolicy() {
136 return policy;
137 }
138
139 @Override
140 public Desire apply(Desire desire) {
141
142 if (satisfaction != null) {
143 return desire.restrict(satisfaction);
144 } else {
145 return desire.restrict(implType);
146 }
147 }
148
149 @Override
150 public EnumSet<BindingFlag> getFlags() {
151 return flags;
152 }
153
154 @Override
155 public boolean isTerminal() {
156 return flags.contains(BindingFlag.TERMINAL);
157 }
158
159 @Override
160 public boolean matches(Desire desire) {
161
162 if (desire.getDesiredType().equals(depType)) {
163
164 return qualifier.matches(desire.getInjectionPoint().getQualifier());
165 }
166
167
168 return false;
169 }
170
171 @Override
172 public BindRuleBuilder newCopyBuilder() {
173 BindRuleBuilder bld = new BindRuleBuilder();
174 bld.setDependencyType(depType)
175 .setQualifierMatcher(qualifier)
176 .setCachePolicy(policy)
177 .setFlags(flags);
178 if (satisfaction != null) {
179 bld.setSatisfaction(satisfaction);
180 } else {
181 bld.setImplementation(implType);
182 }
183 return bld;
184 }
185
186 @Override
187 public int compareTo(BindRule other) {
188 if (other instanceof BindRuleImpl) {
189 return qualifier.compareTo(((BindRuleImpl) other).qualifier);
190 } else {
191 throw new IllegalArgumentException("incompatible bind rule");
192 }
193 }
194
195 @Override
196 public boolean equals(Object o) {
197 if (o == this) {
198 return true;
199 } else if (o instanceof BindRuleImpl) {
200 EqualsBuilder eq = new EqualsBuilder();
201 BindRuleImpl or = (BindRuleImpl) o;
202 return eq.append(depType, or.depType)
203 .append(implType, or.implType)
204 .append(flags, or.flags)
205 .append(qualifier, or.qualifier)
206 .append(policy, or.policy)
207 .append(satisfaction, or.satisfaction)
208 .isEquals();
209 } else {
210 return false;
211 }
212 }
213
214 @Override
215 public int hashCode() {
216 if (hashCode == 0) {
217 HashCodeBuilder hcb = new HashCodeBuilder();
218 hashCode = hcb.append(flags)
219 .append(depType)
220 .append(implType)
221 .append(qualifier)
222 .append(policy)
223 .append(satisfaction)
224 .toHashCode();
225 }
226 return hashCode;
227 }
228
229 @Override
230 public String toString() {
231 String i = (satisfaction == null ? implType.getSimpleName() : satisfaction.toString());
232 return "Bind(" + qualifier + ":" + depType.getSimpleName() + " -> " + i + ", " + flags + ")";
233 }
234
235 private Object writeReplace() {
236 return new SerialProxy(satisfaction, flags, qualifier,
237 depType, implType, policy);
238 }
239
240 private void readObject(ObjectInputStream stream) throws ObjectStreamException {
241 throw new InvalidObjectException("must use serialization proxy");
242 }
243
244
245
246
247 private static class SerialProxy implements Serializable {
248 private static final long serialVersionUID = 2L;
249
250 private final ClassProxy depType;
251 private final QualifierMatcher qualifier;
252 private final ClassProxy implType;
253
254 @Nullable
255 private final Satisfaction satisfaction;
256 private final EnumSet<BindingFlag> flags;
257 private final CachePolicy cachePolicy;
258
259 private SerialProxy(@Nullable Satisfaction sat, EnumSet<BindingFlag> flags, QualifierMatcher qmatch,
260 Class<?> stype, Class<?> itype, CachePolicy policy) {
261 satisfaction = sat;
262 this.flags = flags;
263 qualifier = qmatch;
264 depType = ClassProxy.of(stype);
265 implType = ClassProxy.of(itype);
266 cachePolicy = policy;
267 }
268
269 private Object readResolve() throws ObjectStreamException {
270 try {
271 if (satisfaction == null) {
272 return new BindRuleImpl(depType.resolve(), implType.resolve(),
273 cachePolicy, qualifier, flags);
274 } else {
275 return new BindRuleImpl(depType.resolve(), satisfaction,
276 cachePolicy, qualifier, flags);
277 }
278 } catch (ClassNotFoundException e) {
279 InvalidObjectException ex = new InvalidObjectException("cannot resolve type");
280 ex.initCause(e);
281 throw ex;
282 }
283 }
284 }
285 }