]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/util/RandomValue.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / util / RandomValue.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/util/RandomValue.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/util/RandomValue.java
new file mode 100644 (file)
index 0000000..0936392
--- /dev/null
@@ -0,0 +1,504 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.binding.util;
+
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Random;\r
+import java.util.WeakHashMap;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.Binding.Visitor;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.ByteBinding;\r
+import org.simantics.databoard.binding.DoubleBinding;\r
+import org.simantics.databoard.binding.FloatBinding;\r
+import org.simantics.databoard.binding.IntegerBinding;\r
+import org.simantics.databoard.binding.LongBinding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.OptionalBinding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.StringBinding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.factory.BindingScheme;\r
+import org.simantics.databoard.binding.factory.MutableBindingFactory;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.BooleanType;\r
+import org.simantics.databoard.type.ByteType;\r
+import org.simantics.databoard.type.Component;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.DoubleType;\r
+import org.simantics.databoard.type.FloatType;\r
+import org.simantics.databoard.type.IntegerType;\r
+import org.simantics.databoard.type.LongType;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.type.VariantType;\r
+import org.simantics.databoard.util.Limit;\r
+import org.simantics.databoard.util.Range;\r
+
+/**
+ * Visitor that creates a instance with random value.
+ * This visitor may throw RuntimeBindingException. 
+ * 
+ * Type                     Value
+ * ------------------------------------------------------
+ * Boolean                  false/true
+ * Byte, Integer, Long      value between limits
+ * Float, Double            0..1 if no range, otherwise a valid value in range 
+ * String                   random string of length [0..1024]
+ * Optional                 novalue / random value
+ * Union                    random tag / random value
+ * Record                   each field with random value
+ * Array                    random elements between 0..1024 unless lower bound is higher
+ * Map                      0..1024 random entries with random keys and value
+ * Variant                  random type (excluding variant) with random value
+ * 
+ * TODO Create String according to the pattern
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class RandomValue implements Visitor<Object> {
+       
+       public boolean refereableRecords = true;
+       
+       public Random random;
+       
+       /** Map of default values already created. Used to link back to recursive records */
+       Map<Binding, Object> map = new WeakHashMap<Binding, Object>(1);\r
+       \r
+       BindingScheme scheme = new MutableBindingFactory( new HashMap<Datatype, Binding>() );
+
+       static String CHARS = "abcdefghijklmnopqrstuvwxyz ,.-'/-+ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"#�%&/()=\n\\\\r'";
+       
+       public RandomValue() {
+               this.random = new Random();
+       }
+
+       public RandomValue(Random random) {
+               this.random = random;
+       }
+
+       public RandomValue(int seed) {
+               this.random = new Random(seed);
+       }
+       
+       public Random getRandom() {
+               return random;
+       }
+
+       @Override
+       public Object visit(ArrayBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               
+               ArrayType at = b.type();
+               
+               long min = at.minLength();
+               long max = Math.max(min, Math.min(32, at.maxLength()));
+               int c = (int) (min + nextRandom(max-min+1));
+               
+               Binding componentBinding = b.getComponentBinding();
+               Object[] array = new Object[c];
+               for (int i=0; i<array.length; i++) {
+                       array[i] = componentBinding.accept(this); 
+               }
+               result = b.createUnchecked(array);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(BooleanBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               result = b.createUnchecked( random.nextBoolean() );
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(DoubleBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               DoubleType type = b.type();
+               Range range = type.getRange();
+               double min = type.minValue();
+               double max = type.maxValue();
+               double value = (range==null) ? random.nextDouble() : min+random.nextDouble() * (max-min); 
+               result = b.createUnchecked(value);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(FloatBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               FloatType type = b.type();
+               Range range = type.getRange();
+               double min = type.minValue();
+               double max = type.maxValue();
+               double value = (range==null) ? random.nextDouble() : min+random.nextDouble() * (max-min); 
+               result = b.createUnchecked(value);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(IntegerBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               IntegerType type = b.type();
+               Range range = type.getRange();
+               long min = type.minValue();
+               long max = type.maxValue();
+               long value = (range==null) ? random.nextInt() : min + Math.abs(random.nextLong() % (max-min));
+               result = b.createUnchecked(value);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(ByteBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               ByteType type = b.type();
+               Range range = type.getRange();
+               int min = type.minValue();
+               int max = type.maxValue();
+               int value = (range==null) ? random.nextInt(256)-128 : min + random.nextInt(max-min+1);
+               result = b.createUnchecked(value);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(LongBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               LongType type = b.type();
+               Range range = type.getRange();
+               long min = type.minValue();
+               long max = type.maxValue();
+               long value = (range==null) ? random.nextLong() : min + Math.abs(random.nextLong() % (max-min));
+               result = b.createUnchecked(value);
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(OptionalBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               Binding componentBinding = b.getComponentBinding();             
+               result = random.nextBoolean() ? b.createNoValueUnchecked() : b.createValueUnchecked( componentBinding.accept(this) );
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(RecordBinding b) {
+               try {
+                       Object result = pickCached() ? map.get(b) : null;
+                       if (result!=null) return result;
+                       
+                       Object[] values = new Object[ b.getComponentCount() ];
+                       
+                       if (b.type().isReferable()) {
+                               result = b.createPartial();
+                               map.put(b, result);
+                               for (int i=0; i<values.length; i++) {
+                                       Binding cb = b.getComponentBinding(i);                  
+                                       values[i] = cb.accept(this);
+                               }
+                               b.setComponents(result, values);
+                       } else {
+                               
+                               for (int i=0; i<values.length; i++) {
+                                       Binding cb = b.getComponentBinding(i);                  
+                                       values[i] = cb.accept(this);
+                               }
+                               result = b.create(values);
+                               map.put(b, result);
+                       }
+                       return result;
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               }
+       }
+
+       @Override
+       public Object visit(StringBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               StringType st = b.type();
+               int min = st.minLength();
+               int max = Math.max(min, Math.min(64, st.maxLength()));
+               int c = (int) ( min + nextRandom(max-min+1) ); 
+               
+               StringBuilder sb = new StringBuilder(c);
+               for (int i=0; i<c; i++)
+                       sb.append( CHARS.charAt( random.nextInt(CHARS.length()) ) );
+               
+               result = b.createUnchecked(sb.toString());
+               
+               map.put(b, result);
+               return result;
+       }
+
+       @Override
+       public Object visit(UnionBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+               UnionType ut = b.type();
+
+               int tag = random.nextInt( ut.getComponentCount() );             
+               
+               Binding componentBinding = b.getComponentBinding( tag );
+               Object randomValue = componentBinding.accept(this);
+               result = b.createUnchecked(tag, randomValue);
+               map.put(b, result);
+               return result;
+       }
+       
+       @Override
+       public Object visit(VariantBinding b) {
+               try {\r
+                       Object result = pickCached() ? map.get(b) : null;
+                       if (result!=null) return result;                
+                       
+                       int maxDepth = random.nextInt(3)+1;
+                       Datatype randomType = randomType(0, maxDepth);
+                       Binding randomBinding;\r
+                       randomBinding = scheme.getBinding( randomType );\r
+                       Object randomValue = randomBinding.accept(this);  
+                       
+                       result = b.createUnchecked(randomBinding, randomValue);
+                       map.put(b, result);
+               return result;
+               } catch (BindingConstructionException e) {\r
+                       throw new RuntimeBindingConstructionException(e);\r
+               }\r
+       }
+\r
+       boolean isKeyShortEnough(Binding binding, Object value) {\r
+               try {\r
+                       String key;\r
+                       key = (String) Bindings.adapt(value, binding, Bindings.STR_VARIANT);\r
+                       return key.length()<=200;\r
+               } catch (AdaptException e) {\r
+                       throw new RuntimeException(e);\r
+               } \r
+       }\r
+       
+       @Override
+       public Object visit(MapBinding b) {
+               Object result = pickCached() ? map.get(b) : null;
+               if (result!=null) return result;
+                       
+               int c = random.nextInt(32);
+               
+               Binding keyBinding = b.getKeyBinding();
+               Binding valueBinding = b.getValueBinding();
+               Object[] keys = new Object[c];
+               Object[] values = new Object[c];
+               for (int i=0; i<c; i++) {\r
+                       \r
+                       \r
+                       Object key = null;\r
+                       if (keyBinding.type().equals(Datatypes.VARIANT)) {\r
+                               do {\r
+                                       key = keyBinding.accept(this);\r
+                               } while( !keyBinding.type().equals(Datatypes.VARIANT) || !isKeyShortEnough(keyBinding, key) );                  \r
+                       } else {\r
+                               key = keyBinding.accept(this);                          \r
+                       }\r
+                       
+                       keys[i] = key;\r
+                       
+                       values[i] = valueBinding.accept(this);
+               }               
+               
+               result = b.createUnchecked(keys, values);
+               map.put(b, result);
+               return result;
+       }
+
+       public Datatype randomType(int depth, int maxDepth) {
+               // Random until non variant 
+               int tag = 0;
+               if (depth<maxDepth) {
+                       tag = random.nextInt( 12 );
+                       if (random.nextInt(500)==0) tag=12;
+               } else {
+                       tag = random.nextInt( 7 );
+               }
+               
+               if (tag==0) {
+                       return new BooleanType();
+               }               
+               if (tag==1) {
+                       Limit lowerLimit = Limit.exclusive((random.nextInt() & 255) - 128);
+                       Limit upperLimit = Limit.exclusive((random.nextInt() & 255) - 128);
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       ByteType result = new ByteType(null, range);
+                       if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
+                       return result;
+               }
+                               
+               if (tag==2) {
+                       Limit lowerLimit = Limit.inclusive(0);
+                       Limit upperLimit = Limit.exclusive(random.nextInt());
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       IntegerType result = new IntegerType(null, range);
+                       if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
+                       return result;
+               }               
+
+               if (tag==3) {
+                       Limit lowerLimit = Limit.inclusive(0);
+                       Limit upperLimit = Limit.exclusive(random.nextLong());
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       LongType result = new LongType(null, range);
+                       if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
+                       return result;
+               }
+
+               if (tag==4) {
+                       Limit lowerLimit = Limit.inclusive(0);
+                       Limit upperLimit = Limit.exclusive(random.nextDouble() * random.nextInt());
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       FloatType result = new FloatType(null, range);
+                       if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
+                       return result;
+               }
+               
+               if (tag==5) {
+                       Limit lowerLimit = Limit.inclusive(0);
+                       Limit upperLimit = Limit.exclusive(random.nextDouble() * random.nextInt());
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       DoubleType result = new DoubleType(null, range);
+                       if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
+                       return result;
+               }
+
+               if (tag==6) {
+                       Limit lowerLimit = Limit.inclusive(0);
+                       Limit upperLimit = Limit.exclusive(random.nextInt(1024));
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       StringType result = new StringType(null, null, range); 
+                       if (result.minLength()>result.maxLength()) result.setLength( (Range) null );
+                       return result;
+               }               
+
+               if (tag==7) {
+                       int c = random.nextInt(16);
+                       Component[] components = new Component[c];
+                       String[] names = randomUniqueNames(c);
+                       for (int i=0; i<c; i++) {
+                               components[i] = new Component(names[i], randomType(depth+1, maxDepth));
+                       }
+                       return new RecordType(refereableRecords ? random.nextBoolean() : false, components);
+               }
+               
+               if (tag==8) {
+                       Limit lowerLimit = Limit.inclusive(random.nextInt(16));
+                       Limit upperLimit = Limit.exclusive(random.nextInt(16));
+                       Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
+                       ArrayType result = new ArrayType(randomType(depth+1, maxDepth), range); 
+                       if (result.minLength()>result.maxLength()) result.setLength( (Range) null );            
+                       return result;
+               }               
+               
+               if (tag==9) {
+                       return new MapType(randomType(depth+1, maxDepth), randomType(depth+1, maxDepth));
+               }               
+               
+               if (tag==10) {
+                       return new OptionalType( randomType(depth+1, maxDepth) );
+               }               
+               
+               if (tag==11) {
+                       int c = random.nextInt(16)+1;
+                       Component[] components = new Component[c];
+                       String[] names = randomUniqueNames(c);
+                       for (int i=0; i<c; i++) {
+                               components[i] = new Component(names[i], randomType(depth+1, maxDepth));
+                       }
+                       return new UnionType(components);
+               }
+               
+               if (tag==12) {
+                       return new VariantType();
+               }
+
+               return null;
+       }
+
+       /**
+        * Answers to the question weather we should pick a cached value.
+        * There is 10% if referable records is enabled.
+        * 
+        * @return
+        */
+       boolean pickCached() {
+               if (!refereableRecords) return false;
+               return refereableRecords && random.nextInt(10)==0;  
+       }
+    
+       String randomName() {
+               int nameLength = random.nextInt(32)+1;
+               StringBuilder sb = new StringBuilder(nameLength);
+               for (int j=0; j<nameLength; j++)
+                       sb.append( CHARS.charAt( random.nextInt(CHARS.length()) ) );
+               return sb.toString(); 
+       }
+       
+       /**
+        * Create <code>count</code> unique random names. 
+        * 
+        * @param count
+        * @return
+        */
+       String[] randomUniqueNames(int count) {
+               HashSet<String> result = new HashSet<String>(count);
+               for (int i=0; i<count; i++) {
+                       String name = null;
+                       do {                            
+                               name = randomName();
+                       } while (result.contains(name));
+                       result.add(name);
+               }
+               return result.toArray(new String[count]);
+       }
+               
+       long nextRandom(long n) {
+               long v = random.nextLong();
+               v = Math.abs(v);
+               return v % n;
+       }
+       
+}
+