1 ///////////////////////////////////////////////////////////
\r
3 // MultiViewMap is like a straight-forward multiple-key
\r
4 // map except that it supports retrieval and delete by
\r
5 // subset views of the multikey.
\r
8 // mvm.put(<X, Y, Z>, V);
\r
9 // mvm.put(<X, W, T>, U);
\r
10 // print( mvm.get(<*, Y, *>) ); // prints "<X, Y, Z> -> V"
\r
11 // mvm.remove(<X, *, *>); // removes both entries
\r
13 ///////////////////////////////////////////////////////////
\r
16 import java.util.Set;
\r
17 import java.util.HashSet;
\r
18 import java.util.BitSet;
\r
19 import java.util.Vector;
\r
20 import java.util.Map;
\r
21 import java.util.HashMap;
\r
24 public class MultiViewMap<T> {
\r
26 private Class[] keyTypes;
\r
27 private Vector<BitSet> partialViews;
\r
28 private BitSet fullView;
\r
30 private JoinOp<T> joinOp;
\r
32 private boolean checkTypes;
\r
33 private boolean checkConsistency;
\r
35 // If the entire contents of this map are fullKey -> value:
\r
39 private Map<MultiKey, T> fullKey2value;
\r
41 // ...then this structure captures the partial views:
\r
47 // <*,b> -> {<a,b>, <c,b>}
\r
49 private Map<BitSet, Map<MultiKey, Set<MultiKey>>>
\r
50 view2partialKey2fullKeys;
\r
53 // NOTE! Use MultiViewMapBuilder to get an
\r
54 // instantiation of this class.
\r
55 protected MultiViewMap( Class[] keyTypes,
\r
58 Vector<BitSet> partialViews,
\r
60 boolean checkConsistency ) {
\r
62 this.keyTypes = keyTypes;
\r
63 this.joinOp = joinOp;
\r
64 this.partialViews = partialViews;
\r
65 this.fullView = fullView;
\r
66 this.checkTypes = checkTypes;
\r
67 this.checkConsistency = checkConsistency;
\r
69 fullKey2value = new HashMap<MultiKey, T>();
\r
71 view2partialKey2fullKeys =
\r
72 new HashMap<BitSet, Map<MultiKey, Set<MultiKey>>>();
\r
77 return fullKey2value.size();
\r
80 public boolean equals( Object o ) {
\r
87 if( !(o instanceof MultiViewMap) ) {
\r
90 MultiViewMap that = (MultiViewMap) o;
\r
92 // check whether key types and views match
\r
93 if( !this.isHomogenous( that ) ) {
\r
98 return fullKey2value.equals( that.fullKey2value ) &&
\r
99 view2partialKey2fullKeys.equals( that.view2partialKey2fullKeys );
\r
102 public int hashCode() {
\r
104 hash = hash*31 + keyTypes.hashCode();
\r
105 hash = hash*31 + joinOp.hashCode();
\r
106 hash = hash*31 + fullView.hashCode();
\r
107 hash = hash*31 + partialViews.hashCode();
\r
108 hash = hash*31 + fullKey2value.hashCode();
\r
109 hash = hash*31 + view2partialKey2fullKeys.hashCode();
\r
115 public void put( MultiKey fullKey, T value ) {
\r
116 assert( typesMatch( fullKey ) );
\r
118 fullKey2value.put( fullKey, value );
\r
120 for( BitSet view : partialViews ) {
\r
121 MultiKey partialKey = makePartialKey( view, fullKey );
\r
122 getFullKeys( view, partialKey ).add( fullKey );
\r
125 assert( isConsistent() );
\r
129 public Map<MultiKey, T> get( final BitSet view, MultiKey partialKey ) {
\r
132 Map<MultiKey, T> fullKey2valueBYVIEW = new HashMap<MultiKey, T>();
\r
134 Set<MultiKey> fullKeys = getFullKeys( view, partialKey );
\r
135 for( MultiKey fullKey : fullKeys ) {
\r
136 fullKey2valueBYVIEW.put( fullKey,
\r
137 fullKey2value.get( fullKey ) );
\r
140 return fullKey2valueBYVIEW;
\r
144 public void remove( final BitSet viewForRemove, MultiKey fullOrPartialKey ) {
\r
145 checkView( viewForRemove );
\r
147 Set<MultiKey> fullKeysToRemove = new HashSet<MultiKey>();
\r
149 if( viewForRemove.equals( fullView ) ) {
\r
150 fullKeysToRemove.add( fullOrPartialKey );
\r
152 fullKeysToRemove.addAll( getFullKeys( viewForRemove, fullOrPartialKey ) );
\r
155 for( MultiKey fullKeyToRemove : fullKeysToRemove ) {
\r
156 for( BitSet view : partialViews ) {
\r
157 MultiKey partialKey = makePartialKey( view, fullKeyToRemove );
\r
158 getFullKeys( view, partialKey ).remove( fullKeyToRemove );
\r
160 fullKey2value.remove( fullKeyToRemove );
\r
163 assert( isConsistent() );
\r
167 public void merge( MultiViewMap<T> in ) {
\r
168 assert( isHomogenous( in ) );
\r
170 Set<MultiKey> mergedFullKeys = new HashSet<MultiKey>();
\r
171 mergedFullKeys.addAll( this.fullKey2value.keySet() );
\r
172 mergedFullKeys.addAll( in.fullKey2value.keySet() );
\r
174 for( MultiKey fullKey : mergedFullKeys ) {
\r
175 fullKey2value.put( fullKey,
\r
176 joinOp.join( this.fullKey2value.get( fullKey ),
\r
177 in.fullKey2value.get( fullKey )
\r
181 for( MultiKey fullKey : in.fullKey2value.keySet() ) {
\r
182 for( BitSet view : partialViews ) {
\r
183 MultiKey partialKey = makePartialKey( view, fullKey );
\r
184 getFullKeys( view, partialKey ).add( fullKey );
\r
188 assert( isConsistent() );
\r
193 Set<MultiKey> getFullKeys( BitSet view,
\r
194 MultiKey partialKey ) {
\r
196 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =
\r
197 getPartialKey2fullKeys( view );
\r
198 return getFullKeys( partialKey2fullKeys, partialKey );
\r
203 Map<MultiKey, Set<MultiKey>> getPartialKey2fullKeys( BitSet view ) {
\r
205 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =
\r
206 view2partialKey2fullKeys.get( view );
\r
207 if( partialKey2fullKeys == null ) {
\r
208 partialKey2fullKeys = new HashMap<MultiKey, Set<MultiKey>>();
\r
209 view2partialKey2fullKeys.put( view, partialKey2fullKeys );
\r
211 return partialKey2fullKeys;
\r
216 Set<MultiKey> getFullKeys( Map<MultiKey, Set<MultiKey>> partialKey2fullKeys,
\r
217 MultiKey partialKey ) {
\r
219 Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );
\r
220 if( fullKeys == null ) {
\r
221 fullKeys = new HashSet<MultiKey>();
\r
222 partialKey2fullKeys.put( partialKey, fullKeys );
\r
228 private MultiKey makePartialKey( BitSet view, MultiKey fullKey ) {
\r
229 Object[] partialKeys = new Object[view.cardinality()];
\r
231 for( int i = 0; i < view.size(); ++i ) {
\r
232 if( view.get( i ) ) {
\r
233 partialKeys[j] = fullKey.get( i );
\r
237 assert( j == view.cardinality() );
\r
238 return new MultiKey( partialKeys );
\r
242 private void checkView( BitSet view ) {
\r
243 if( view != fullView &&
\r
244 !partialViews.contains( view ) ) {
\r
245 throw new IllegalArgumentException( "This view is not supported." );
\r
250 private boolean typesMatch( MultiKey multiKey ) {
\r
251 if( !checkTypes ) {
\r
255 return multiKey.typesMatch( keyTypes );
\r
259 private boolean isHomogenous( MultiViewMap in ) {
\r
260 if( this.keyTypes.length != in.keyTypes.length ) {
\r
263 for( int i = 0; i < this.keyTypes.length; ++i ) {
\r
264 if( !this.keyTypes[i].equals( in.keyTypes[i] ) ) {
\r
269 this.partialViews.equals( in.partialViews ) &&
\r
270 this.fullView.equals( in.fullView ) &&
\r
271 this.joinOp.equals( in.joinOp );
\r
275 private boolean isConsistent() {
\r
276 if( !checkConsistency ) {
\r
280 // First, for every full key, make sure it is in every partial key
\r
281 // set it should be in.
\r
282 for( MultiKey fullKey : fullKey2value.keySet() ) {
\r
283 for( BitSet view : partialViews ) {
\r
284 MultiKey partialKey = makePartialKey( view, fullKey );
\r
285 if( !getFullKeys( view, partialKey ).contains( fullKey ) ) {
\r
286 System.out.println( "Expected full key="+fullKey+
\r
287 " to be in view="+view+
\r
288 " and partial key="+partialKey+
\r
289 " but it is missing." );
\r
295 // Second, for each partial key set, make sure every full key is
\r
296 // a) a match for the partial key it is filed under and
\r
297 // b) also in the full key->value set
\r
298 for( BitSet view : partialViews ) {
\r
299 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys = getPartialKey2fullKeys( view );
\r
300 for( MultiKey partialKey : partialKey2fullKeys.keySet() ) {
\r
301 Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );
\r
302 for( MultiKey fullKey : fullKeys ) {
\r
303 if( !partialKey.equals( makePartialKey( view, fullKey ) ) ) {
\r
304 System.out.println( "Full key="+fullKey+
\r
305 " was filed under view="+view+
\r
306 " and partial key="+partialKey+
\r
307 " but the (fullKey, view)->partialKey mapping is inconsistent." );
\r
310 if( !fullKey2value.containsKey( fullKey ) ) {
\r
311 System.out.println( "Full key="+fullKey+
\r
312 " was filed under view="+view+
\r
313 " and partial key="+partialKey+
\r
314 " but the fullKey is not in the fullKey->value map." );
\r