1 package org.lcsim.event.base;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.LinkedHashMap;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.TreeSet;
12
13 import org.lcsim.event.LCRelation;
14 import org.lcsim.event.RelationalTable;
15
16
17
18
19
20 public class BaseRelationalTable<F,T> implements RelationalTable<F, T>
21 {
22 private enum OneMany { ONE, MANY };
23 private final static double WEIGHT_ONE = 1.0;
24 private final static double WEIGHT_ZERO = 0.0;
25
26 private final Mode mode;
27 private final Weighting weighting;
28
29 private Map<Relation<F,T>,Double> weightedRelations;
30 private Set<Relation<F,T>> unweightedRelations;
31
32 private Map<F, Set<T>> fromMultiMap;
33 private Map<T, Set<F>> toMultiMap;
34
35 private Map<F,T> fromMap;
36 private Map<T,F> toMap;
37
38 private OneMany fromMode()
39 {
40 return mode == Mode.ONE_TO_MANY || mode == Mode.ONE_TO_ONE ? OneMany.ONE : OneMany.MANY;
41 }
42 private OneMany toMode()
43 {
44 return mode == Mode.MANY_TO_ONE || mode == Mode.ONE_TO_ONE ? OneMany.ONE : OneMany.MANY;
45 }
46
47
48
49 public BaseRelationalTable()
50 {
51 this(Mode.MANY_TO_MANY,Weighting.WEIGHTED);
52 }
53
54 public BaseRelationalTable(Mode mode, Weighting weighting)
55 {
56 this.mode = mode;
57 this.weighting = weighting;
58
59 if (weighting == Weighting.WEIGHTED) weightedRelations = new HashMap<Relation<F,T>,Double>();
60 else unweightedRelations = new HashSet<Relation<F,T>>();
61
62 if (toMode() == OneMany.ONE) fromMap = new HashMap<F,T>();
63 else fromMultiMap = new HashMap<F,Set<T>>();
64
65 if (fromMode() == OneMany.ONE) toMap = new HashMap<T,F>();
66 else toMultiMap = new HashMap<T,Set<F>>();
67 }
68
69 public boolean add(F from, T to)
70 {
71 return add(from,to,WEIGHT_ONE);
72 }
73
74 public boolean add(F from, T to, double weight)
75 {
76 if (from == null || to == null) throw new IllegalArgumentException("Argument to RelationalTable.add cannot be null");
77 if (weighting == Weighting.UNWEIGHTED && weight != WEIGHT_ONE) throw new IllegalArgumentException("Weight must be 1 for unweighted relational table");
78
79 boolean result = false;
80
81 if (toMode() == OneMany.ONE)
82 {
83 T oldTo = fromMap.put(from,to);
84 if (oldTo != null)
85 {
86 removeRelation(from,oldTo);
87 result |= true;
88 }
89 }
90 if (fromMode() == OneMany.ONE)
91 {
92 F oldFrom = toMap.put(to,from);
93 if (oldFrom != null)
94 {
95 removeRelation(oldFrom,to);
96 result |= true;
97 }
98 }
99
100
101 result |= addRelation(from,to,weight);
102
103 if (toMode() == OneMany.MANY)
104 {
105 Set<T> toList = fromMultiMap.get(from);
106 if (toList == null)
107 {
108 toList = weighting == Weighting.UNWEIGHTED ? new HashSet<T>() : new TreeSet<T>(new FromWeightComparator(from));
109 fromMultiMap.put(from,toList);
110 }
111 toList.add(to);
112 }
113
114
115 if (fromMode() == OneMany.MANY)
116 {
117 Set<F> fromList = toMultiMap.get(to);
118 if (fromList == null)
119 {
120 fromList = weighting == Weighting.UNWEIGHTED ? new HashSet<F>() : new TreeSet<F>(new ToWeightComparator(to));
121 toMultiMap.put(to,fromList);
122 }
123 fromList.add(from);
124 }
125
126 return result;
127 }
128 private Set<Relation<F,T>> relations()
129 {
130 return weighting == Weighting.UNWEIGHTED ? unweightedRelations : weightedRelations.keySet();
131 }
132 private boolean removeRelation(F from, T to)
133 {
134 Relation relation = new Relation(from,to);
135 return relations().remove(relation);
136 }
137 private boolean addRelation(F from, T to, double weight)
138 {
139 Relation relation = new Relation<F,T>(from,to);
140 return weighting == Weighting.UNWEIGHTED ? !unweightedRelations.add(relation) : weightedRelations.put(relation,weight) != null;
141 }
142
143 public boolean remove(F from, T to)
144 {
145 boolean result = removeRelation(from,to);
146 if (result)
147 {
148 if (toMode() == OneMany.ONE)
149 {
150 fromMap.remove(from);
151 }
152 else
153 {
154 Set<T> toList = fromMultiMap.get(from);
155 toList.remove(from);
156 }
157
158 if (fromMode() == OneMany.ONE)
159 {
160 toMap.remove(to);
161 }
162 else
163 {
164 Set<T> fromList = fromMultiMap.get(to);
165 fromList.remove(to);
166 }
167 }
168 return result;
169 }
170
171 public T to(F from)
172 {
173 if (toMode() == OneMany.ONE) return fromMap.get(from);
174 else
175 {
176 Set<T> all = allFrom(from);
177 if (all.size() > 2) throw new IllegalArgumentException("Ambiguous relationship for "+from);
178 return all.size() == 1 ? all.iterator().next() : null;
179 }
180 }
181
182 public F from(T to)
183 {
184 if (fromMode() == OneMany.ONE) return toMap.get(to);
185 else
186 {
187 Set<F> all = allTo(to);
188 if (all.size() > 2) throw new IllegalArgumentException("Ambiguous relationship for "+to);
189 return all.size() == 1 ? all.iterator().next() : null;
190 }
191 }
192
193 public Set<T> allFrom(F from)
194 {
195 if (toMode() == OneMany.ONE)
196 {
197 T to = fromMap.get(from);
198 return to == null ? Collections.<T>emptySet() : Collections.singleton(to);
199 }
200 else
201 {
202 Set<T> result = fromMultiMap.get(from);
203 return result == null ? Collections.<T>emptySet() : result;
204 }
205 }
206
207 public Map<T,Double> allFromWithWeights(F from)
208 {
209 if (toMode() == OneMany.ONE)
210 {
211 T to = fromMap.get(from);
212 return to == null ? Collections.<T,Double>emptyMap() : Collections.singletonMap(to,weightFromTo(from,to));
213 }
214 else
215 {
216 Set<T> toList = fromMultiMap.get(from);
217 if (toList == null || toList.isEmpty()) return Collections.<T,Double>emptyMap();
218 Map<T,Double> result = new LinkedHashMap<T,Double>();
219 for (T to : toList) result.put(to,weightFromTo(from,to));
220 return result;
221 }
222 }
223
224 public Set<F> allTo(T to)
225 {
226 if (fromMode() == OneMany.ONE)
227 {
228 F from = toMap.get(to);
229 return from == null ? Collections.<F>emptySet() : Collections.singleton(from);
230 }
231 else
232 {
233 Set<F> result = toMultiMap.get(to);
234 return result == null ? Collections.<F>emptySet() : result;
235 }
236 }
237
238 public Map<F,Double> allToWithWeights(T to)
239 {
240 if (fromMode() == OneMany.ONE)
241 {
242 F from = toMap.get(to);
243 return from == null ? Collections.<F,Double>emptyMap() : Collections.singletonMap(from,weightFromTo(from,to));
244 }
245 else
246 {
247 Set<F> fromList = toMultiMap.get(to);
248 if (fromList == null || fromList.isEmpty()) return Collections.<F,Double>emptyMap();
249 Map<F,Double> result = new LinkedHashMap<F,Double>();
250 for (F from : fromList) result.put(from,weightFromTo(from,to));
251 return result;
252 }
253 }
254
255 public double weightFromTo(F from, T to)
256 {
257 Relation relation = new Relation(from,to);
258 if (weighting == Weighting.UNWEIGHTED) return unweightedRelations.contains(relation) ? WEIGHT_ONE : WEIGHT_ZERO;
259 else
260 {
261 Double d = weightedRelations.get(relation);
262 return d == null ? WEIGHT_ZERO : d.doubleValue();
263 }
264 }
265
266 public Mode getMode()
267 {
268 return mode;
269 }
270 public Weighting getWeighting()
271 {
272 return weighting;
273 }
274 public int size()
275 {
276 return relations().size();
277 }
278
279 private static class Relation<F,T>
280 {
281 private F from;
282 private T to;
283
284 Relation(F from, T to)
285 {
286 this.from = from;
287 this.to = to;
288 }
289 F getFrom()
290 {
291 return from;
292 }
293 T getTo()
294 {
295 return to;
296 }
297
298 public int hashCode()
299 {
300 return to.hashCode() + 37*from.hashCode();
301 }
302
303 public boolean equals(Object obj)
304 {
305 if (obj instanceof Relation)
306 {
307 Relation that = (Relation) obj;
308 return this.from.equals(that.from) && this.to.equals(that.to);
309 }
310 else return false;
311 }
312 }
313 private class FromWeightComparator implements Comparator<T>
314 {
315 private F from;
316 FromWeightComparator(F from)
317 {
318 this.from = from;
319 }
320 public int compare(T o1, T o2)
321 {
322 double w1 = weightFromTo(from,o1);
323 double w2 = weightFromTo(from,o2);
324 if (w1 == w2)
325 {
326
327
328
329 return o1.hashCode() - o2.hashCode();
330 }
331 else return (int) Math.signum(w2 - w1);
332 }
333 }
334 private class ToWeightComparator implements Comparator<F>
335 {
336 private T to;
337 ToWeightComparator(T to)
338 {
339 this.to = to;
340 }
341 public int compare(F o1, F o2)
342 {
343 double w1 = weightFromTo(o1,to);
344 double w2 = weightFromTo(o2,to);
345 if (w1 == w2)
346 {
347
348
349
350 return o1.hashCode() - o2.hashCode();
351 }
352 else return (int) Math.signum(w2 - w1);
353 }
354 }
355
356 public void addRelations(Collection<LCRelation> relations)
357 {
358 for (LCRelation r : relations) {
359 this.add((F)r.getFrom(), (T)r.getTo(), r.getWeight());
360 }
361 }
362 }