summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/esodata/PairMap.java
blob: ba13a2ff4fe9331b4ea79b706ac4f7e5acff7697 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package bjc.esodata;

import java.util.*;

import bjc.data.Pair;

/**
 * Represents a map keyed by pairs, where you can look up all the items that
 * have a particular left or right key value.
 * 
 * NOTE: Using the keySet/values/entrySet views to modify the map will break the
 * tracking invariants.
 * 
 * @author bjcul
 *
 * @param <Left>
 * @param <Right>
 * @param <Value>
 */
public class PairMap<Left, Right, Value> implements Map<Pair<Left, Right>, Value> {
	private Map<Pair<Left, Right>, Value> backing;

	private Multimap<Left, Pair<Left, Right>> leftTracker;
	private Multimap<Right, Pair<Left, Right>> rightTracker;

	/**
	 * Create a new pair map.
	 */
	public PairMap() {
		this.backing = new HashMap<>();
		
		this.leftTracker = new Multimap<>();
		this.rightTracker = new Multimap<>();
	}
	
	/**
	 * Get all of the key-pairs which contain the given left value.
	 * 
	 * @param val The value to search for.
	 * 
	 * @return All of the key-pairs containing the given value.
	 */
	public Set<Pair<Left, Right>> getLeft(Left val) {
		return leftTracker.get(val);
	}
	
	/**
	 * Get all of the key-pairs which contain the given right value.
	 * 
	 * @param val The value to search for.
	 * 
	 * @return All of the key-pairs containing the given value.
	 */
	public Set<Pair<Left, Right>> getRight(Right val) {
		return rightTracker.get(val);
	}
	
	/**
	 * Get a value without having to construct a pair callee-side
	 * 
	 * @param lft The left value
	 * @param rght The right value.
	 * 
	 * @return The value corresponding to the given key-pair, if one exists
	 */
	public Value biget(Left lft, Right rght) {
		return backing.get(Pair.pair(lft, rght));
	}
	
	@Override
	public int size() {
		return backing.size();
	}

	@Override
	public boolean isEmpty() {
		return backing.isEmpty();
	}

	@Override
	public boolean containsKey(Object key) {
		return backing.containsKey(key);
	}

	@Override
	public boolean containsValue(Object value) {
		return backing.containsKey(value);
	}

	@Override
	public Value get(Object key) {
		return backing.get(key);
	}

	@Override
	public Value put(Pair<Left, Right> key, Value value) {
		Value ret = backing.put(key, value);
		leftTracker.add(key.getLeft(), key);
		rightTracker.add(key.getRight(), key);
		return ret;
	}

	@Override
	public Value remove(Object key) {
		@SuppressWarnings("unchecked")
		Pair<Left, Right> actKey = (Pair<Left, Right>) key;

		leftTracker.remove(actKey.getLeft(), actKey);
		rightTracker.remove(actKey.getRight(), actKey);

		return backing.remove(key);
	}

	@Override
	public void putAll(Map<? extends Pair<Left, Right>, ? extends Value> m) {
		for (Entry<? extends Pair<Left, Right>, ? extends Value> entry : m.entrySet()) {
			put(entry.getKey(), entry.getValue());
		}
	}

	@Override
	public void clear() {
		backing.clear();

		leftTracker = new Multimap<>();
		rightTracker = new Multimap<>();
	}

	// TODO: Update these to not break the tracking invariants
	@Override
	public Set<Pair<Left, Right>> keySet() {
		return backing.keySet();
	}

	@Override
	public Collection<Value> values() {
		return backing.values();
	}

	@Override
	public Set<Entry<Pair<Left, Right>, Value>> entrySet() {
		return backing.entrySet();
	}

}