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 com.google.common.collect.ImmutableListMultimap;
23 import com.google.common.collect.ListMultimap;
24 import com.google.common.collect.Multimap;
25 import org.apache.commons.lang3.tuple.Pair;
26 import org.grouplens.grapht.ResolutionException;
27 import org.grouplens.grapht.context.ContextMatch;
28 import org.grouplens.grapht.context.ContextMatcher;
29 import org.grouplens.grapht.reflect.QualifierMatcher;
30 import org.grouplens.grapht.util.Preconditions;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import java.util.*;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class RuleBasedBindingFunction implements BindingFunction {
60 private static final Map<Object,Set<BindRule>> bindRuleMemory
61 = new WeakHashMap<Object, Set<BindRule>>();
62
63 private static final Logger logger = LoggerFactory.getLogger(RuleBasedBindingFunction.class);
64
65 private final ImmutableListMultimap<ContextMatcher, BindRule> rules;
66
67 public RuleBasedBindingFunction(Multimap<ContextMatcher, BindRule> rules) {
68 Preconditions.notNull("rules", rules);
69
70 this.rules = ImmutableListMultimap.copyOf(rules);
71 }
72
73
74
75
76
77 public ListMultimap<ContextMatcher, BindRule> getRules() {
78 return rules;
79 }
80
81 @Override
82 public BindingResult bind(InjectionContext context, DesireChain desire) throws ResolutionException {
83
84 Set<BindRule> appliedRules;
85 synchronized (bindRuleMemory) {
86 appliedRules = bindRuleMemory.get(desire.getKey());
87 if (appliedRules == null) {
88 appliedRules = new HashSet<BindRule>();
89 bindRuleMemory.put(desire.getKey(), appliedRules);
90 }
91 }
92
93
94 List<Pair<ContextMatch, BindRule>> validRules = new ArrayList<Pair<ContextMatch, BindRule>>();
95 for (ContextMatcher matcher: rules.keySet()) {
96 ContextMatch match = matcher.matches(context);
97 if (match != null) {
98
99
100 for (BindRule br: rules.get(matcher)) {
101 if (br.matches(desire.getCurrentDesire()) && !appliedRules.contains(br)) {
102 validRules.add(Pair.of(match, br));
103 logger.trace("Matching rule, context: {}, rule: {}", matcher, br);
104 }
105 }
106 }
107 }
108
109 if (!validRules.isEmpty()) {
110
111
112 Collections.sort(validRules);
113
114 if (validRules.size() > 1) {
115
116
117 List<BindRule> topRules = new ArrayList<BindRule>();
118 topRules.add(validRules.get(0).getRight());
119 for (int i = 1; i < validRules.size(); i++) {
120 if (validRules.get(0).compareTo(validRules.get(i)) == 0) {
121 topRules.add(validRules.get(i).getRight());
122 }
123 }
124
125 if (topRules.size() > 1) {
126 logger.error("{} bindings for {} in {}", topRules.size(),
127 desire, context);
128 for (BindRule rule: topRules) {
129 logger.info("matching rule: {}", rule);
130 }
131
132 throw new MultipleBindingsException(desire, context, topRules);
133 }
134 }
135
136
137 BindRule selectedRule = validRules.get(0).getRight();
138 appliedRules.add(selectedRule);
139
140 logger.debug("Applying rule: {} to desire: {}", selectedRule, desire);
141 return BindingResult.newBuilder()
142 .setDesire(selectedRule.apply(desire.getCurrentDesire()))
143 .setCachePolicy(selectedRule.getCachePolicy())
144 .setFlags(selectedRule.getFlags())
145 .build();
146 }
147
148
149 return null;
150 }
151 }