1 | package org.mapdb;
|
---|
2 |
|
---|
3 | import java.util.Iterator;
|
---|
4 | import java.util.Map;
|
---|
5 | import java.util.NavigableSet;
|
---|
6 | import java.util.Set;
|
---|
7 | import java.util.concurrent.ConcurrentMap;
|
---|
8 |
|
---|
9 | /**
|
---|
10 | * Collection binding
|
---|
11 | *
|
---|
12 | * @author Jan Kotek
|
---|
13 | */
|
---|
14 | public final class Bind {
|
---|
15 |
|
---|
16 | private Bind(){}
|
---|
17 |
|
---|
18 | public static <K2,K1> Iterable<K1> findSecondaryKeys(final NavigableSet<Fun.Tuple2<K2,K1>> secondaryKeys, final K2 secondaryKey) {
|
---|
19 | return new Iterable<K1>(){
|
---|
20 | @Override
|
---|
21 | public Iterator<K1> iterator() {
|
---|
22 | //use range query to get all values
|
---|
23 | final Iterator<Fun.Tuple2<K2,K1>> iter =
|
---|
24 | ((NavigableSet)secondaryKeys) //cast is workaround for generics
|
---|
25 | .subSet(
|
---|
26 | Fun.t2(secondaryKey,null), //NULL represents lower bound, everything is larger than null
|
---|
27 | Fun.t2(secondaryKey,Fun.HI) // HI is upper bound everything is smaller then HI
|
---|
28 | ).iterator();
|
---|
29 |
|
---|
30 | return new Iterator<K1>() {
|
---|
31 | @Override
|
---|
32 | public boolean hasNext() {
|
---|
33 | return iter.hasNext();
|
---|
34 | }
|
---|
35 |
|
---|
36 | @Override
|
---|
37 | public K1 next() {
|
---|
38 | return iter.next().b;
|
---|
39 | }
|
---|
40 |
|
---|
41 | @Override
|
---|
42 | public void remove() {
|
---|
43 | iter.remove();
|
---|
44 | }
|
---|
45 | };
|
---|
46 | }
|
---|
47 | };
|
---|
48 |
|
---|
49 | }
|
---|
50 |
|
---|
51 |
|
---|
52 | public interface MapListener<K,V>{
|
---|
53 | void update(K key, V oldVal, V newVal);
|
---|
54 | }
|
---|
55 |
|
---|
56 | public interface MapWithModificationListener<K,V> extends Map<K,V> {
|
---|
57 | public void addModificationListener(MapListener<K,V> listener);
|
---|
58 | public void removeModificationListener(MapListener<K,V> listener);
|
---|
59 | }
|
---|
60 |
|
---|
61 | public static void size(MapWithModificationListener map, final Atomic.Long size){
|
---|
62 | //set initial value first if necessary
|
---|
63 | if(size.get() == 0 && map.isEmpty())
|
---|
64 | size.set(map.size()); //TODO long overflow?
|
---|
65 |
|
---|
66 | map.addModificationListener(new MapListener() {
|
---|
67 | @Override
|
---|
68 | public void update(Object key, Object oldVal, Object newVal) {
|
---|
69 | if(oldVal == null && newVal!=null)
|
---|
70 | size.incrementAndGet();
|
---|
71 | else if(oldVal!=null && newVal == null)
|
---|
72 | size.decrementAndGet();
|
---|
73 | else{
|
---|
74 | //update does not change collection size
|
---|
75 | }
|
---|
76 | }
|
---|
77 | });
|
---|
78 | }
|
---|
79 |
|
---|
80 | public static <K,V, V2> void secondaryValue(MapWithModificationListener<K, V> map,
|
---|
81 | final Map<K, V2> secondary,
|
---|
82 | final Fun.Function2<V2, K, V> fun){
|
---|
83 | //fill if empty
|
---|
84 | if(secondary.isEmpty()){
|
---|
85 | for(Map.Entry<K,V> e:map.entrySet())
|
---|
86 | secondary.put(e.getKey(), fun.run(e.getKey(),e.getValue()));
|
---|
87 | }
|
---|
88 | //hook listener
|
---|
89 | map.addModificationListener(new MapListener<K, V>() {
|
---|
90 | @Override
|
---|
91 | public void update(K key, V oldVal, V newVal) {
|
---|
92 | if(newVal == null){
|
---|
93 | //removal
|
---|
94 | secondary.remove(key);
|
---|
95 | }else{
|
---|
96 | secondary.put(key, fun.run(key,newVal));
|
---|
97 | }
|
---|
98 | }
|
---|
99 | });
|
---|
100 | }
|
---|
101 |
|
---|
102 | public static <K,V, K2> void secondaryKey(MapWithModificationListener<K, V> map,
|
---|
103 | final NavigableSet<Fun.Tuple2<K2, K>> secondary,
|
---|
104 | final Fun.Function2<K2, K, V> fun){
|
---|
105 | //fill if empty
|
---|
106 | if(secondary.isEmpty()){
|
---|
107 | for(Map.Entry<K,V> e:map.entrySet()){
|
---|
108 | secondary.add(Fun.t2(fun.run(e.getKey(),e.getValue()), e.getKey()));
|
---|
109 | }
|
---|
110 | }
|
---|
111 | //hook listener
|
---|
112 | map.addModificationListener(new MapListener<K, V>() {
|
---|
113 | @Override
|
---|
114 | public void update(K key, V oldVal, V newVal) {
|
---|
115 | if(newVal == null){
|
---|
116 | //removal
|
---|
117 | secondary.remove(Fun.t2(fun.run(key, oldVal), key));
|
---|
118 | }else if(oldVal==null){
|
---|
119 | //insert
|
---|
120 | secondary.add(Fun.t2(fun.run(key,newVal), key));
|
---|
121 | }else{
|
---|
122 | //update, must remove old key and insert new
|
---|
123 | K2 oldKey = fun.run(key, oldVal);
|
---|
124 | K2 newKey = fun.run(key, newVal);
|
---|
125 | if(oldKey == newKey || oldKey.equals(newKey)) return;
|
---|
126 | secondary.remove(Fun.t2(oldKey, key));
|
---|
127 | secondary.add(Fun.t2(newKey,key));
|
---|
128 | }
|
---|
129 | }
|
---|
130 | });
|
---|
131 | }
|
---|
132 |
|
---|
133 | public static <K,V> void mapInverse(MapWithModificationListener<K,V> primary,
|
---|
134 | NavigableSet<Fun.Tuple2<V, K>> inverse) {
|
---|
135 | Bind.secondaryKey(primary,inverse, new Fun.Function2<V, K,V>(){
|
---|
136 | @Override public V run(K key, V value) {
|
---|
137 | return value;
|
---|
138 | }
|
---|
139 | });
|
---|
140 | }
|
---|
141 |
|
---|
142 |
|
---|
143 | public static <K,V,C> void histogram(MapWithModificationListener<K,V> primary, final ConcurrentMap<C,Long> histogram,
|
---|
144 | final Fun.Function2<C, K, V> entryToCategory){
|
---|
145 |
|
---|
146 | MapListener<K,V> listener = new MapListener<K, V>() {
|
---|
147 | @Override public void update(K key, V oldVal, V newVal) {
|
---|
148 | if(newVal == null){
|
---|
149 | //removal
|
---|
150 | C category = entryToCategory.run(key,oldVal);
|
---|
151 | incrementHistogram(category, -1);
|
---|
152 | }else if(oldVal==null){
|
---|
153 | //insert
|
---|
154 | C category = entryToCategory.run(key,newVal);
|
---|
155 | incrementHistogram(category, 1);
|
---|
156 | }else{
|
---|
157 | //update, must remove old key and insert new
|
---|
158 | C oldCat = entryToCategory.run(key, oldVal);
|
---|
159 | C newCat = entryToCategory.run(key, newVal);
|
---|
160 | if(oldCat == newCat || oldCat.equals(newCat)) return;
|
---|
161 | incrementHistogram(oldCat,-1);
|
---|
162 | incrementHistogram(oldCat,1);
|
---|
163 | }
|
---|
164 |
|
---|
165 | }
|
---|
166 |
|
---|
167 | /** atomically update counter in histogram*/
|
---|
168 | private void incrementHistogram(C category, long i) {
|
---|
169 | for(;;){
|
---|
170 | Long oldCount = histogram.get(category);
|
---|
171 | if(oldCount == null){
|
---|
172 | //insert new count
|
---|
173 | if(histogram.putIfAbsent(category,i) == null)
|
---|
174 | return;
|
---|
175 | }else{
|
---|
176 | //increase existing count
|
---|
177 | Long newCount = oldCount+i;
|
---|
178 | if(histogram.replace(category,oldCount, newCount))
|
---|
179 | return;
|
---|
180 | }
|
---|
181 | }
|
---|
182 | }
|
---|
183 | };
|
---|
184 |
|
---|
185 | primary.addModificationListener(listener);
|
---|
186 | }
|
---|
187 |
|
---|
188 |
|
---|
189 |
|
---|
190 |
|
---|
191 | }
|
---|