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
36 // for MultiViewMaps that don't need to use the value,
\r
37 // template on type Object and map every key to this dummy
\r
38 public static Object dummy = new Integer( -12345 );
\r
41 // If the entire contents of this map are fullKey -> value:
\r
45 private Map<MultiKey, T> fullKey2value;
\r
47 // ...then this structure captures the partial views:
\r
53 // <*,b> -> {<a,b>, <c,b>}
\r
55 private Map<BitSet, Map<MultiKey, Set<MultiKey>>>
\r
56 view2partialKey2fullKeys;
\r
59 // NOTE! Use MultiViewMapBuilder to get an
\r
60 // instantiation of this class.
\r
61 protected MultiViewMap( Class[] keyTypes,
\r
64 Vector<BitSet> partialViews,
\r
66 boolean checkConsistency ) {
\r
68 this.keyTypes = keyTypes;
\r
69 this.joinOp = joinOp;
\r
70 this.partialViews = partialViews;
\r
71 this.fullView = fullView;
\r
72 this.checkTypes = checkTypes;
\r
73 this.checkConsistency = checkConsistency;
\r
75 fullKey2value = new HashMap<MultiKey, T>();
\r
77 view2partialKey2fullKeys =
\r
78 new HashMap<BitSet, Map<MultiKey, Set<MultiKey>>>();
\r
82 public boolean equals( Object o ) {
\r
89 if( !(o instanceof MultiViewMap) ) {
\r
92 MultiViewMap that = (MultiViewMap) o;
\r
94 // check whether key types and views match
\r
95 if( !this.isHomogenous( that ) ) {
\r
100 return fullKey2value.equals( that.fullKey2value ) &&
\r
101 view2partialKey2fullKeys.equals( that.view2partialKey2fullKeys );
\r
104 public int hashCode() {
\r
106 hash = hash*31 + keyTypes.hashCode();
\r
107 hash = hash*31 + joinOp.hashCode();
\r
108 hash = hash*31 + fullView.hashCode();
\r
109 hash = hash*31 + partialViews.hashCode();
\r
110 hash = hash*31 + fullKey2value.hashCode();
\r
111 hash = hash*31 + view2partialKey2fullKeys.hashCode();
\r
116 public int size() {
\r
117 return fullKey2value.size();
\r
121 public void clear() {
\r
122 fullKey2value.clear();
\r
123 view2partialKey2fullKeys.clear();
\r
127 public void put( MultiKey fullKey, T value ) {
\r
128 assert( typesMatch( fullKey ) );
\r
130 fullKey2value.put( fullKey, value );
\r
132 for( BitSet view : partialViews ) {
\r
133 MultiKey partialKey = makePartialKey( view, fullKey );
\r
134 getFullKeys( view, partialKey ).add( fullKey );
\r
137 assert( isConsistent() );
\r
141 public Map<MultiKey, T> get( final BitSet view, MultiKey partialKey ) {
\r
144 Map<MultiKey, T> fullKey2valueBYVIEW = new HashMap<MultiKey, T>();
\r
146 Set<MultiKey> fullKeys = getFullKeys( view, partialKey );
\r
147 for( MultiKey fullKey : fullKeys ) {
\r
148 fullKey2valueBYVIEW.put( fullKey,
\r
149 fullKey2value.get( fullKey ) );
\r
152 return fullKey2valueBYVIEW;
\r
156 public void remove( final BitSet viewForRemove, MultiKey fullOrPartialKey ) {
\r
157 checkView( viewForRemove );
\r
159 Set<MultiKey> fullKeysToRemove = new HashSet<MultiKey>();
\r
161 if( viewForRemove.equals( fullView ) ) {
\r
162 fullKeysToRemove.add( fullOrPartialKey );
\r
164 fullKeysToRemove.addAll( getFullKeys( viewForRemove, fullOrPartialKey ) );
\r
167 for( MultiKey fullKeyToRemove : fullKeysToRemove ) {
\r
168 for( BitSet view : partialViews ) {
\r
169 MultiKey partialKey = makePartialKey( view, fullKeyToRemove );
\r
170 getFullKeys( view, partialKey ).remove( fullKeyToRemove );
\r
172 fullKey2value.remove( fullKeyToRemove );
\r
175 assert( isConsistent() );
\r
179 public void merge( MultiViewMap<T> in ) {
\r
180 assert( isHomogenous( in ) );
\r
182 Set<MultiKey> mergedFullKeys = new HashSet<MultiKey>();
\r
183 mergedFullKeys.addAll( this.fullKey2value.keySet() );
\r
184 mergedFullKeys.addAll( in.fullKey2value.keySet() );
\r
186 for( MultiKey fullKey : mergedFullKeys ) {
\r
187 fullKey2value.put( fullKey,
\r
188 joinOp.join( this.fullKey2value.get( fullKey ),
\r
189 in.fullKey2value.get( fullKey )
\r
193 for( MultiKey fullKey : in.fullKey2value.keySet() ) {
\r
194 for( BitSet view : partialViews ) {
\r
195 MultiKey partialKey = makePartialKey( view, fullKey );
\r
196 getFullKeys( view, partialKey ).add( fullKey );
\r
200 assert( isConsistent() );
\r
205 Set<MultiKey> getFullKeys( BitSet view,
\r
206 MultiKey partialKey ) {
\r
208 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =
\r
209 getPartialKey2fullKeys( view );
\r
210 return getFullKeys( partialKey2fullKeys, partialKey );
\r
215 Map<MultiKey, Set<MultiKey>> getPartialKey2fullKeys( BitSet view ) {
\r
217 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =
\r
218 view2partialKey2fullKeys.get( view );
\r
219 if( partialKey2fullKeys == null ) {
\r
220 partialKey2fullKeys = new HashMap<MultiKey, Set<MultiKey>>();
\r
221 view2partialKey2fullKeys.put( view, partialKey2fullKeys );
\r
223 return partialKey2fullKeys;
\r
228 Set<MultiKey> getFullKeys( Map<MultiKey, Set<MultiKey>> partialKey2fullKeys,
\r
229 MultiKey partialKey ) {
\r
231 Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );
\r
232 if( fullKeys == null ) {
\r
233 fullKeys = new HashSet<MultiKey>();
\r
234 partialKey2fullKeys.put( partialKey, fullKeys );
\r
240 private MultiKey makePartialKey( BitSet view, MultiKey fullKey ) {
\r
241 Object[] partialKeys = new Object[view.cardinality()];
\r
243 for( int i = 0; i < view.size(); ++i ) {
\r
244 if( view.get( i ) ) {
\r
245 partialKeys[j] = fullKey.get( i );
\r
249 assert( j == view.cardinality() );
\r
250 return new MultiKey( partialKeys );
\r
254 private void checkView( BitSet view ) {
\r
255 if( view != fullView &&
\r
256 !partialViews.contains( view ) ) {
\r
257 throw new IllegalArgumentException( "This view is not supported." );
\r
262 private boolean typesMatch( MultiKey multiKey ) {
\r
263 if( !checkTypes ) {
\r
267 return multiKey.typesMatch( keyTypes );
\r
271 private boolean isHomogenous( MultiViewMap in ) {
\r
272 if( this.keyTypes.length != in.keyTypes.length ) {
\r
275 for( int i = 0; i < this.keyTypes.length; ++i ) {
\r
276 if( !this.keyTypes[i].equals( in.keyTypes[i] ) ) {
\r
281 this.partialViews.equals( in.partialViews ) &&
\r
282 this.fullView.equals( in.fullView ) &&
\r
283 this.joinOp.equals( in.joinOp );
\r
287 private boolean isConsistent() {
\r
288 if( !checkConsistency ) {
\r
292 // First, for every full key, make sure it is in every partial key
\r
293 // set it should be in.
\r
294 for( MultiKey fullKey : fullKey2value.keySet() ) {
\r
295 for( BitSet view : partialViews ) {
\r
296 MultiKey partialKey = makePartialKey( view, fullKey );
\r
297 if( !getFullKeys( view, partialKey ).contains( fullKey ) ) {
\r
298 System.out.println( "Expected full key="+fullKey+
\r
299 " to be in view="+view+
\r
300 " and partial key="+partialKey+
\r
301 " but it is missing." );
\r
307 // Second, for each partial key set, make sure every full key is
\r
308 // a) a match for the partial key it is filed under and
\r
309 // b) also in the full key->value set
\r
310 for( BitSet view : partialViews ) {
\r
311 Map<MultiKey, Set<MultiKey>> partialKey2fullKeys = getPartialKey2fullKeys( view );
\r
312 for( MultiKey partialKey : partialKey2fullKeys.keySet() ) {
\r
313 Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );
\r
314 for( MultiKey fullKey : fullKeys ) {
\r
315 if( !partialKey.equals( makePartialKey( view, fullKey ) ) ) {
\r
316 System.out.println( "Full key="+fullKey+
\r
317 " was filed under view="+view+
\r
318 " and partial key="+partialKey+
\r
319 " but the (fullKey, view)->partialKey mapping is inconsistent." );
\r
322 if( !fullKey2value.containsKey( fullKey ) ) {
\r
323 System.out.println( "Full key="+fullKey+
\r
324 " was filed under view="+view+
\r
325 " and partial key="+partialKey+
\r
326 " but the fullKey is not in the fullKey->value map." );
\r