From: jjenista Date: Wed, 21 Sep 2011 21:08:35 +0000 (+0000) Subject: A MultiViewMap is a generalization of the VarSrcTokTable in OoOJava. It is cumbersom... X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=377441d83b7624ae72886e6f1f6b8963d6a7b8b1;p=IRC.git A MultiViewMap is a generalization of the VarSrcTokTable in OoOJava. It is cumbersome to set up, but very powerful, and it will serve as the underlying implementation of the four maps in Definite Reachability Analysis. When that is implemented, check there for examples of MultiViewMap in the wild. --- diff --git a/Robust/src/Util/JoinOp.java b/Robust/src/Util/JoinOp.java new file mode 100644 index 00000000..360347cc --- /dev/null +++ b/Robust/src/Util/JoinOp.java @@ -0,0 +1,20 @@ +/////////////////////////////////////////// +// +// Just an interface for specifying +// how to join two instances of a +// type. This is useful within a +// generic structure, like MultiViewMap. +// +/////////////////////////////////////////// +package Util; + +public interface JoinOp { + + //////////////////////////////////////// + // + // join() should accept null values for + // the arguments! + // + //////////////////////////////////////// + T join( T a, T b ); +} diff --git a/Robust/src/Util/JoinOpInteger.java b/Robust/src/Util/JoinOpInteger.java new file mode 100644 index 00000000..3cf03d53 --- /dev/null +++ b/Robust/src/Util/JoinOpInteger.java @@ -0,0 +1,15 @@ +package Util; + +public class JoinOpInteger implements JoinOp { + public Integer join( Integer a, Integer b ) { + int aVal = 0; + if( a != null ) { + aVal = a; + } + int bVal = 0; + if( b != null ) { + bVal = b; + } + return aVal + bVal; + } +} diff --git a/Robust/src/Util/MultiKey.java b/Robust/src/Util/MultiKey.java new file mode 100644 index 00000000..16fd999b --- /dev/null +++ b/Robust/src/Util/MultiKey.java @@ -0,0 +1,94 @@ +//////////////////////////////////////////// +// +// A MultiKey is a vector of Objects that +// serves as a key. One MultiKey is equal +// to another if they have the same number +// of Objects, and Objects in corresponding +// positions are equal. +// +//////////////////////////////////////////// +package Util; + + +public class MultiKey { + + static public MultiKey factory( Object... keys ) { + return new MultiKey( keys ); + } + + + private Object[] keys; + + public MultiKey( Object[] keys ) { + if( keys.length == 0 ) { + throw new IllegalArgumentException( "A MultiKey must have at least one key." ); + } + for( int i = 0; i < keys.length; ++i ) { + if( keys[i] == null ) { + throw new IllegalArgumentException( "A MultiKey cannot have null elements." ); + } + } + this.keys = keys.clone(); + } + + public boolean typesMatch( Class[] keyTypes ) { + if( keys.length != keyTypes.length ) { + return false; + } + for( int i = 0; i < keys.length; ++i ) { + if( !(keyTypes[i].isInstance( keys[i] )) ) { + return false; + } + } + return true; + } + + public Object get( int index ) { + if( index < 0 || index >= keys.length ) { + throw new IllegalArgumentException( "Index "+index+" is out of range for this MultiKey." ); + } + return keys[index]; + } + + public boolean equals( Object o ) { + if( this == o ) { + return true; + } + if( o == null ) { + return false; + } + if( !(o instanceof MultiKey) ) { + return false; + } + MultiKey mk = (MultiKey) o; + if( this.keys.length != mk.keys.length ) { + return false; + } + for( int i = 0; i < keys.length; ++i ) { + if( !this.keys[i].equals( mk.keys[i] ) ) { + return false; + } + } + return true; + } + + public int hashCode() { + int hash = 1; + for( Object key : keys ) { + hash = hash*31 + key.hashCode(); + } + return hash; + } + + public String toString() { + StringBuilder s = new StringBuilder( "MK[" ); + for( int i = 0; i < keys.length; ++i ) { + s.append( keys[i].toString() ); + if( i < keys.length - 1 ) { + s.append( ", " ); + } + } + s.append( "]" ); + return s.toString(); + } +} diff --git a/Robust/src/Util/MultiViewMap.java b/Robust/src/Util/MultiViewMap.java new file mode 100644 index 00000000..2cf626d0 --- /dev/null +++ b/Robust/src/Util/MultiViewMap.java @@ -0,0 +1,288 @@ +/////////////////////////////////////////////////////////// +// +// MultiViewMap is like a straight-forward multiple-key +// map except that it supports retrieval and delete by +// subset views of the multikey. +// +// Ex: +// mvm.put(, V); +// mvm.put(, U); +// print( mvm.get(<*, Y, *>) ); // prints " -> V" +// mvm.remove(); // removes both entries +// +/////////////////////////////////////////////////////////// +package Util; + +import java.util.Set; +import java.util.HashSet; +import java.util.BitSet; +import java.util.Vector; +import java.util.Map; +import java.util.HashMap; + + +public class MultiViewMap { + + private Class[] keyTypes; + private Vector partialViews; + private BitSet fullView; + + private JoinOp joinOp; + + private boolean checkTypes; + private boolean checkConsistency; + + // If the entire contents of this map are fullKey -> value: + // -> 1 + // -> 2 + // -> 3 + private Map fullKey2value; + + // ...then this structure captures the partial views: + // view[1, 0] -> + // -> {} + // -> {} + // -> {} + // view[0, 1] -> + // <*,b> -> {, } + // <*,e> -> {} + private Map>> + view2partialKey2fullKeys; + + + // NOTE! Use MultiViewMapBuilder to get an + // instantiation of this class. + protected MultiViewMap( Class[] keyTypes, + JoinOp joinOp, + BitSet fullView, + Vector partialViews, + boolean checkTypes, + boolean checkConsistency ) { + + this.keyTypes = keyTypes; + this.joinOp = joinOp; + this.partialViews = partialViews; + this.fullView = fullView; + this.checkTypes = checkTypes; + this.checkConsistency = checkConsistency; + + fullKey2value = new HashMap(); + + view2partialKey2fullKeys = + new HashMap>>(); + } + + + public int size() { + return fullKey2value.size(); + } + + + public void put( MultiKey fullKey, T value ) { + assert( typesMatch( fullKey ) ); + + fullKey2value.put( fullKey, value ); + + for( BitSet view : partialViews ) { + MultiKey partialKey = makePartialKey( view, fullKey ); + getFullKeys( view, partialKey ).add( fullKey ); + } + + assert( isConsistent() ); + } + + + public Map get( final BitSet view, MultiKey partialKey ) { + checkView( view ); + + Map fullKey2valueBYVIEW = new HashMap(); + + Set fullKeys = getFullKeys( view, partialKey ); + for( MultiKey fullKey : fullKeys ) { + fullKey2valueBYVIEW.put( fullKey, + fullKey2value.get( fullKey ) ); + } + + return fullKey2valueBYVIEW; + } + + + public void remove( final BitSet viewForRemove, MultiKey fullOrPartialKey ) { + checkView( viewForRemove ); + + Set fullKeysToRemove = new HashSet(); + + if( viewForRemove.equals( fullView ) ) { + fullKeysToRemove.add( fullOrPartialKey ); + } else { + fullKeysToRemove.addAll( getFullKeys( viewForRemove, fullOrPartialKey ) ); + } + + for( MultiKey fullKeyToRemove : fullKeysToRemove ) { + for( BitSet view : partialViews ) { + MultiKey partialKey = makePartialKey( view, fullKeyToRemove ); + getFullKeys( view, partialKey ).remove( fullKeyToRemove ); + } + fullKey2value.remove( fullKeyToRemove ); + } + + assert( isConsistent() ); + } + + + public void merge( MultiViewMap in ) { + assert( isHomogenous( in ) ); + + Set mergedFullKeys = new HashSet(); + mergedFullKeys.addAll( this.fullKey2value.keySet() ); + mergedFullKeys.addAll( in.fullKey2value.keySet() ); + + for( MultiKey fullKey : mergedFullKeys ) { + fullKey2value.put( fullKey, + joinOp.join( this.fullKey2value.get( fullKey ), + in.fullKey2value.get( fullKey ) + ) ); + } + + for( MultiKey fullKey : in.fullKey2value.keySet() ) { + for( BitSet view : partialViews ) { + MultiKey partialKey = makePartialKey( view, fullKey ); + getFullKeys( view, partialKey ).add( fullKey ); + } + } + + assert( isConsistent() ); + } + + + private + Set getFullKeys( BitSet view, + MultiKey partialKey ) { + + Map> partialKey2fullKeys = + getPartialKey2fullKeys( view ); + return getFullKeys( partialKey2fullKeys, partialKey ); + } + + + private + Map> getPartialKey2fullKeys( BitSet view ) { + + Map> partialKey2fullKeys = + view2partialKey2fullKeys.get( view ); + if( partialKey2fullKeys == null ) { + partialKey2fullKeys = new HashMap>(); + view2partialKey2fullKeys.put( view, partialKey2fullKeys ); + } + return partialKey2fullKeys; + } + + + private + Set getFullKeys( Map> partialKey2fullKeys, + MultiKey partialKey ) { + + Set fullKeys = partialKey2fullKeys.get( partialKey ); + if( fullKeys == null ) { + fullKeys = new HashSet(); + partialKey2fullKeys.put( partialKey, fullKeys ); + } + return fullKeys; + } + + + private MultiKey makePartialKey( BitSet view, MultiKey fullKey ) { + Object[] partialKeys = new Object[view.cardinality()]; + int j = 0; + for( int i = 0; i < view.size(); ++i ) { + if( view.get( i ) ) { + partialKeys[j] = fullKey.get( i ); + ++j; + } + } + assert( j == view.cardinality() ); + return new MultiKey( partialKeys ); + } + + + private void checkView( BitSet view ) { + if( view != fullView && + !partialViews.contains( view ) ) { + throw new IllegalArgumentException( "This view is not supported." ); + } + } + + + private boolean typesMatch( MultiKey multiKey ) { + if( !checkTypes ) { + return true; + } + + return multiKey.typesMatch( keyTypes ); + } + + + private boolean isHomogenous( MultiViewMap in ) { + if( this.keyTypes.length != in.keyTypes.length ) { + return false; + } + for( int i = 0; i < this.keyTypes.length; ++i ) { + if( !this.keyTypes[i].equals( in.keyTypes[i] ) ) { + return false; + } + } + return + this.partialViews.equals( in.partialViews ) && + this.fullView.equals( in.fullView ); + } + + + private boolean isConsistent() { + if( !checkConsistency ) { + return true; + } + + // First, for every full key, make sure it is in every partial key + // set it should be in. + for( MultiKey fullKey : fullKey2value.keySet() ) { + for( BitSet view : partialViews ) { + MultiKey partialKey = makePartialKey( view, fullKey ); + if( !getFullKeys( view, partialKey ).contains( fullKey ) ) { + System.out.println( "Expected full key="+fullKey+ + " to be in view="+view+ + " and partial key="+partialKey+ + " but it is missing." ); + return false; + } + } + } + + // Second, for each partial key set, make sure every full key is + // a) a match for the partial key it is filed under and + // b) also in the full key->value set + for( BitSet view : partialViews ) { + Map> partialKey2fullKeys = getPartialKey2fullKeys( view ); + for( MultiKey partialKey : partialKey2fullKeys.keySet() ) { + Set fullKeys = partialKey2fullKeys.get( partialKey ); + for( MultiKey fullKey : fullKeys ) { + if( !partialKey.equals( makePartialKey( view, fullKey ) ) ) { + System.out.println( "Full key="+fullKey+ + " was filed under view="+view+ + " and partial key="+partialKey+ + " but the (fullKey, view)->partialKey mapping is inconsistent." ); + return false; + } + if( !fullKey2value.containsKey( fullKey ) ) { + System.out.println( "Full key="+fullKey+ + " was filed under view="+view+ + " and partial key="+partialKey+ + " but the fullKey is not in the fullKey->value map." ); + return false; + } + } + } + } + + return true; + } +} diff --git a/Robust/src/Util/MultiViewMapBuilder.java b/Robust/src/Util/MultiViewMapBuilder.java new file mode 100644 index 00000000..8cc74c8e --- /dev/null +++ b/Robust/src/Util/MultiViewMapBuilder.java @@ -0,0 +1,122 @@ +// This builder does several things: +// +// 1) The MultiViewMap constructor is private because +// there is a lot of consistency checking to do in +// the inputs (like whether a view is specified +// twice) so do all that in a builder and the map +// itself can assume valid inputs at the constructor. +// +// 2) The inputs to construct a MultiViewMap are tedious +// to code, so this builder lets you write the +// specifications succinctly. +// +// 3) If you are creating many MultiViewMap's of the same +// type and views, it is best to have one builder that +// generates each fresh map rather than build up all +// the small specification objects again. +// +package Util; + +import java.util.BitSet; +import java.util.Vector; + + +public class MultiViewMapBuilder { + + private Class[] keyTypes; + private Vector partialViews; + private BitSet fullView; + + private JoinOp joinOp; + + private boolean checkTypes; + private boolean checkConsistency; + + + // define the types and ordering of the multikey + public MultiViewMapBuilder( Class[] keyTypes, JoinOp joinOp ) { + assert( keyTypes != null ); + assert( joinOp != null ); + + if( keyTypes.length == 0 ) { + throw new IllegalArgumentException( "The multikey must have at least one key type." ); + } + + this.keyTypes = keyTypes; + this.partialViews = new Vector(); + this.joinOp = joinOp; + this.checkTypes = false; + this.checkConsistency = false; + + fullView = new BitSet( keyTypes.length ); + for( int i = 0; i < keyTypes.length; ++i ) { + fullView.set( i ); + } + } + + + public final BitSet addPartialView( Integer... keyIndices ) { + if( keyIndices.length == 0 ) { + throw new IllegalArgumentException( "A view must have at least one key index." ); + } + + // build a view from the arg list + BitSet view = new BitSet( keyTypes.length ); + for( Integer i : keyIndices ) { + if( i < 0 || i >= keyTypes.length ) { + throw new IllegalArgumentException( "Key index in view is out of bounds." ); + } + if( view.get( i ) ) { + throw new IllegalArgumentException( "Key index in view is specified more than once." ); + } + view.set( i ); + } + + if( keyIndices.length == keyTypes.length && + view.cardinality() == keyTypes.length ) { + throw new IllegalArgumentException( "The full view is always included implicitly." ); + } + + for( BitSet existingView : partialViews ) { + if( view.equals( existingView ) ) { + throw new IllegalArgumentException( "View matches an existing view." ); + } + } + + return addPartialView( view ); + } + + + public final BitSet addPartialView( BitSet view ) { + partialViews.add( view ); + return (BitSet) view.clone(); + } + + + public final BitSet getFullView() { + return fullView; + } + + + public void setCheckTypes( boolean checkTypes ) { + this.checkTypes = checkTypes; + } + + + public void setCheckConsistency( boolean checkConsistency ) { + this.checkConsistency = checkConsistency; + } + + + public MultiViewMap build() { + if( partialViews.isEmpty() ) { + throw new IllegalArgumentException( "No partial views specified for this builder." ); + } + return new MultiViewMap( keyTypes, + joinOp, + fullView, + partialViews, + checkTypes, + checkConsistency ); + } +} diff --git a/Robust/src/Util/UnitTests/MultiViewMapTest.java b/Robust/src/Util/UnitTests/MultiViewMapTest.java new file mode 100644 index 00000000..fc4a004d --- /dev/null +++ b/Robust/src/Util/UnitTests/MultiViewMapTest.java @@ -0,0 +1,368 @@ +package Util.UnitTests; + +import Util.*; + +import java.util.Set; +import java.util.HashSet; +import java.util.BitSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Random; +import java.lang.reflect.Array; + + +public class MultiViewMapTest { + + private static JoinOpInteger joinOp; + + private static Random random; + + + private static void p() { System.out.println( " passed" ); } + private static void f() { System.out.println( " !!!FAILED!!!" ); } + + private static void verify( Map expected, + Map actual ) { + if( expected.equals( actual ) ) { + p(); + } else { + f(); + System.out.println( "expected = "+expected ); + System.out.println( "actual = "+actual ); + } + } + + + public static void main(String[] args) { + + int randomSeed = 12345; + if( args.length > 0 ) { + randomSeed = Integer.parseInt( args[0] ); + } + random = new Random( randomSeed ); + + joinOp = new JoinOpInteger(); + + testBuilder(); + System.out.println(""); + testMap(); + System.out.println(""); + stressTest(); + System.out.println(""); + } + + + public static void testBuilder() { + System.out.println( "Testing MultiViewMapBuilder..." ); + + MultiViewMapBuilder builder; + + try { + builder = new MultiViewMapBuilder( new Class[]{}, joinOp ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + builder = + new MultiViewMapBuilder( new Class[] + {Integer.class, + Boolean.class, + Integer.class, + String.class}, + joinOp ); + p(); + } catch( Exception e ) { + f(); + } + + + builder = + new MultiViewMapBuilder( new Class[] + {Integer.class, + Boolean.class, + Integer.class, + String.class}, + joinOp ); + + try { + // can't build a map with no views yet + builder.build(); + f(); + } catch( Exception expected ) { + p(); + } + + try { + builder.addPartialView(); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + // an index is out of bounds for this multikey (0-3) + builder.addPartialView( 1, 6 ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + // an index is out of bounds for this multikey (0-3) + builder.addPartialView( 2, -1, 3 ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + // an index is specified more than once + builder.addPartialView( 0, 3, 0 ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + // the full view is implicit + builder.addPartialView( 0, 1, 2, 3 ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + + try { + builder.addPartialView( 1, 3 ); + p(); + } catch( Exception e ) { + f(); + } + + try { + builder.addPartialView( 0, 1, 3 ); + p(); + } catch( Exception e ) { + f(); + } + + try { + // duplicate view + builder.addPartialView( 1, 3 ); + f(); + } catch( IllegalArgumentException expected ) { + p(); + } catch( Exception e ) { + f(); + } + System.out.println( "DONE" ); + } + + + public static void testMap() { + System.out.println( "Testing MultiViewMap..." ); + + Map expected; + + MultiViewMapBuilder builder = + new MultiViewMapBuilder( new Class[] + {Integer.class, // key0 + Boolean.class, // key1 + String.class}, // key2 + joinOp ); + final BitSet view012 = builder.getFullView(); + final BitSet view01 = builder.addPartialView( 0, 1 ); + final BitSet view0 = builder.addPartialView( 0 ); + final BitSet view2 = builder.addPartialView( 2 ); + builder.setCheckTypes( true ); + builder.setCheckConsistency( true ); + + MultiViewMap mapA = builder.build(); + + + // Simple put and remove + MultiKey partialKey4 = MultiKey.factory( 4 ); + expected = new HashMap(); + verify( expected, mapA.get( view0, partialKey4 ) ); + + MultiKey vader = MultiKey.factory( 4, true, "Vader" ); + mapA.put( vader, 1001 ); + expected = new HashMap(); + expected.put( vader, 1001 ); + verify( expected, mapA.get( view0, partialKey4 ) ); + + mapA.remove( view0, partialKey4 ); + expected = new HashMap(); + verify( expected, mapA.get( view0, partialKey4 ) ); + + + // Try across a merge + mapA.put( vader, 1001 ); + expected = new HashMap(); + expected.put( vader, 1001 ); + verify( expected, mapA.get( view0, partialKey4 ) ); + + MultiViewMap mapB = builder.build(); + expected = new HashMap(); + verify( expected, mapB.get( view0, partialKey4 ) ); + + mapB.merge( mapA ); + expected = new HashMap(); + expected.put( vader, 1001 ); + verify( expected, mapB.get( view0, partialKey4 ) ); + + + // Remove the correct entries + MultiKey luke = MultiKey.factory( 5, true, "Luke" ); + MultiKey han = MultiKey.factory( 4, true, "Han Solo" ); + MultiKey r2 = MultiKey.factory( 4, false, "R2-D2" ); + + mapA.put( luke, 1002 ); + mapA.put( han, 1003 ); + mapA.put( r2, 1004 ); + expected = new HashMap(); + expected.put( vader, 1001 ); + expected.put( han, 1003 ); + expected.put( r2, 1004 ); + verify( expected, mapA.get( view0, partialKey4 ) ); + + // removes vader and han + MultiKey partialKey4true = MultiKey.factory( 4, true ); + mapA.remove( view01, partialKey4true ); + + expected = new HashMap(); + expected.put( r2, 1004 ); + verify( expected, mapA.get( view0, partialKey4 ) ); + + MultiKey partialKeyLuke = MultiKey.factory( "Luke" ); + expected = new HashMap(); + expected.put( luke, 1002 ); + verify( expected, mapA.get( view2, partialKeyLuke ) ); + + + System.out.println( "DONE" ); + } + + + public static void stressTest() { + System.out.println( "Stressing MultiViewMap..." ); + + // Just pound away with operations to see if we can crash anything. + + MultiViewMapBuilder builder = + new MultiViewMapBuilder( new Class[] + {Integer.class, // key0 + Boolean.class, // key1 + String.class}, // key2 + joinOp ); + builder.setCheckTypes( true ); + builder.setCheckConsistency( true ); + + Integer[] ints = new Integer[] { + 1, 2, 3, 4, 5, 6, 7, + }; + + Boolean[] bools = new Boolean[] { + true, false, false, // skew distribution + }; + + String[] strs = new String[] { + "Vader", "Han Solo", "R2-D2", "Luke", "Leia", + }; + + final BitSet view012 = builder.getFullView(); + final BitSet view01 = builder.addPartialView( 0, 1 ); + final BitSet view12 = builder.addPartialView( 1, 2 ); + final BitSet view02 = builder.addPartialView( 0, 2 ); + final BitSet view0 = builder.addPartialView( 0 ); + final BitSet view1 = builder.addPartialView( 1 ); + final BitSet view2 = builder.addPartialView( 2 ); + + // This might be the ugliest line of Java I've ever written. BARF + @SuppressWarnings({"unchecked"}) + MultiViewMap[] maps = + (MultiViewMap[]) Array.newInstance( builder.build().getClass(), 8 ); + + for( int i = 0; i < maps.length; ++i ) { + maps[i] = builder.build(); + } + + + System.out.println( " Number of full keys in each table per op cycle:" ); + + for( int reps = 0; reps < 100; ++reps ) { + int nextOp = random.nextInt( 100 ); + + System.out.print( " Op: " ); + + if( nextOp < 15 ) { + // put some new values in + System.out.print( "PT " ); + int numNewValues = 1 + random.nextInt( 8 ); + for( int i = 0; i < numNewValues; ++i ) { + MultiKey newKey = MultiKey.factory( getInt( ints ), + getBool( bools ), + getStr( strs ) ); + getMap( maps ).put( newKey, random.nextInt() ); + } + + } else if( nextOp < 70 ) { + // remove values by a random view + System.out.print( "RM " ); + MultiViewMap map = getMap( maps ); + + switch( random.nextInt( 6 ) ) { + case 0: { map.remove( view0, MultiKey.factory( getInt (ints ) ) ); } break; + case 1: { map.remove( view1, MultiKey.factory( getBool(bools) ) ); } break; + case 2: { map.remove( view2, MultiKey.factory( getStr (strs ) ) ); } break; + case 3: { map.remove( view01, MultiKey.factory( getInt (ints ), getBool(bools) ) ); } break; + case 4: { map.remove( view12, MultiKey.factory( getBool(bools), getStr (strs ) ) ); } break; + case 5: { map.remove( view02, MultiKey.factory( getInt (ints ), getStr (strs ) ) ); } break; + } + + } else { + // merge two tables + System.out.print( "MG " ); + getMap( maps ).merge( getMap( maps ) ); + } + + for( int i = 0; i < maps.length - 1; ++i ) { + if( i < maps.length - 1 ) { + System.out.print( maps[i].size() + ", " ); + } + } + System.out.println( maps[maps.length-1].size() ); + } + + System.out.println( "DONE" ); + } + + private static Integer getInt( Integer[] ints ) { + return ints[random.nextInt( ints.length )]; + } + + private static Boolean getBool( Boolean[] bools ) { + return bools[random.nextInt( bools.length )]; + } + + private static String getStr( String[] strs ) { + return strs[random.nextInt( strs.length )]; + } + + private static MultiViewMap getMap( MultiViewMap[] maps ) { + return maps[random.nextInt( maps.length )]; + } +} diff --git a/Robust/src/Util/UnitTests/makefile b/Robust/src/Util/UnitTests/makefile new file mode 100644 index 00000000..caf78a2e --- /dev/null +++ b/Robust/src/Util/UnitTests/makefile @@ -0,0 +1,12 @@ +CLASSPATH=../.. +MAINCLASS=MultiViewMapTest + +all: $(MAINCLASS).java + javac -classpath $(CLASSPATH) -Xlint $(MAINCLASS).java + +run: + java -classpath $(CLASSPATH) Util.UnitTests.$(MAINCLASS) + +clean: + rm -f *.class + rm -f *~