1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.binding.util;
14 import java.util.HashMap;
15 import java.util.HashSet;
17 import java.util.Random;
18 import java.util.WeakHashMap;
20 import org.simantics.databoard.Bindings;
21 import org.simantics.databoard.Datatypes;
22 import org.simantics.databoard.adapter.AdaptException;
23 import org.simantics.databoard.binding.ArrayBinding;
24 import org.simantics.databoard.binding.Binding;
25 import org.simantics.databoard.binding.Binding.Visitor;
26 import org.simantics.databoard.binding.BooleanBinding;
27 import org.simantics.databoard.binding.ByteBinding;
28 import org.simantics.databoard.binding.DoubleBinding;
29 import org.simantics.databoard.binding.FloatBinding;
30 import org.simantics.databoard.binding.IntegerBinding;
31 import org.simantics.databoard.binding.LongBinding;
32 import org.simantics.databoard.binding.MapBinding;
33 import org.simantics.databoard.binding.OptionalBinding;
34 import org.simantics.databoard.binding.RecordBinding;
35 import org.simantics.databoard.binding.StringBinding;
36 import org.simantics.databoard.binding.UnionBinding;
37 import org.simantics.databoard.binding.VariantBinding;
38 import org.simantics.databoard.binding.error.BindingConstructionException;
39 import org.simantics.databoard.binding.error.BindingException;
40 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
41 import org.simantics.databoard.binding.error.RuntimeBindingException;
42 import org.simantics.databoard.binding.factory.BindingScheme;
43 import org.simantics.databoard.binding.factory.MutableBindingFactory;
44 import org.simantics.databoard.type.ArrayType;
45 import org.simantics.databoard.type.BooleanType;
46 import org.simantics.databoard.type.ByteType;
47 import org.simantics.databoard.type.Component;
48 import org.simantics.databoard.type.Datatype;
49 import org.simantics.databoard.type.DoubleType;
50 import org.simantics.databoard.type.FloatType;
51 import org.simantics.databoard.type.IntegerType;
52 import org.simantics.databoard.type.LongType;
53 import org.simantics.databoard.type.MapType;
54 import org.simantics.databoard.type.OptionalType;
55 import org.simantics.databoard.type.RecordType;
56 import org.simantics.databoard.type.StringType;
57 import org.simantics.databoard.type.UnionType;
58 import org.simantics.databoard.type.VariantType;
59 import org.simantics.databoard.util.Limit;
60 import org.simantics.databoard.util.Range;
63 * Visitor that creates a instance with random value.
64 * This visitor may throw RuntimeBindingException.
67 * ------------------------------------------------------
69 * Byte, Integer, Long value between limits
70 * Float, Double 0..1 if no range, otherwise a valid value in range
71 * String random string of length [0..1024]
72 * Optional novalue / random value
73 * Union random tag / random value
74 * Record each field with random value
75 * Array random elements between 0..1024 unless lower bound is higher
76 * Map 0..1024 random entries with random keys and value
77 * Variant random type (excluding variant) with random value
79 * TODO Create String according to the pattern
80 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
82 public class RandomValue implements Visitor<Object> {
84 public boolean refereableRecords = true;
88 /** Map of default values already created. Used to link back to recursive records */
89 Map<Binding, Object> map = new WeakHashMap<Binding, Object>(1);
91 BindingScheme scheme = new MutableBindingFactory( new HashMap<Datatype, Binding>() );
93 static String CHARS = "abcdefghijklmnopqrstuvwxyz ,.-'/-+ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"#�%&/()=\n\\\\r'";
95 public RandomValue() {
96 this.random = new Random();
99 public RandomValue(Random random) {
100 this.random = random;
103 public RandomValue(int seed) {
104 this.random = new Random(seed);
107 public Random getRandom() {
112 public Object visit(ArrayBinding b) {
113 Object result = pickCached() ? map.get(b) : null;
114 if (result!=null) return result;
116 ArrayType at = b.type();
118 long min = at.minLength();
119 long max = Math.max(min, Math.min(32, at.maxLength()));
120 int c = (int) (min + nextRandom(max-min+1));
122 Binding componentBinding = b.getComponentBinding();
123 Object[] array = new Object[c];
124 for (int i=0; i<array.length; i++) {
125 array[i] = componentBinding.accept(this);
127 result = b.createUnchecked(array);
133 public Object visit(BooleanBinding b) {
134 Object result = pickCached() ? map.get(b) : null;
135 if (result!=null) return result;
136 result = b.createUnchecked( random.nextBoolean() );
142 public Object visit(DoubleBinding b) {
143 Object result = pickCached() ? map.get(b) : null;
144 if (result!=null) return result;
145 DoubleType type = b.type();
146 Range range = type.getRange();
147 double min = type.minValue();
148 double max = type.maxValue();
149 double value = (range==null) ? random.nextDouble() : min+random.nextDouble() * (max-min);
150 result = b.createUnchecked(value);
156 public Object visit(FloatBinding b) {
157 Object result = pickCached() ? map.get(b) : null;
158 if (result!=null) return result;
159 FloatType type = b.type();
160 Range range = type.getRange();
161 double min = type.minValue();
162 double max = type.maxValue();
163 double value = (range==null) ? random.nextDouble() : min+random.nextDouble() * (max-min);
164 result = b.createUnchecked(value);
170 public Object visit(IntegerBinding b) {
171 Object result = pickCached() ? map.get(b) : null;
172 if (result!=null) return result;
173 IntegerType type = b.type();
174 Range range = type.getRange();
175 long min = type.minValue();
176 long max = type.maxValue();
177 long value = (range==null) ? random.nextInt() : min + Math.abs(random.nextLong() % (max-min));
178 result = b.createUnchecked(value);
184 public Object visit(ByteBinding b) {
185 Object result = pickCached() ? map.get(b) : null;
186 if (result!=null) return result;
187 ByteType type = b.type();
188 Range range = type.getRange();
189 int min = type.minValue();
190 int max = type.maxValue();
191 int value = (range==null) ? random.nextInt(256)-128 : min + random.nextInt(max-min+1);
192 result = b.createUnchecked(value);
198 public Object visit(LongBinding b) {
199 Object result = pickCached() ? map.get(b) : null;
200 if (result!=null) return result;
201 LongType type = b.type();
202 Range range = type.getRange();
203 long min = type.minValue();
204 long max = type.maxValue();
205 long value = (range==null) ? random.nextLong() : min + Math.abs(random.nextLong() % (max-min));
206 result = b.createUnchecked(value);
212 public Object visit(OptionalBinding b) {
213 Object result = pickCached() ? map.get(b) : null;
214 if (result!=null) return result;
215 Binding componentBinding = b.getComponentBinding();
216 result = random.nextBoolean() ? b.createNoValueUnchecked() : b.createValueUnchecked( componentBinding.accept(this) );
222 public Object visit(RecordBinding b) {
224 Object result = pickCached() ? map.get(b) : null;
225 if (result!=null) return result;
227 Object[] values = new Object[ b.getComponentCount() ];
229 if (b.type().isReferable()) {
230 result = b.createPartial();
232 for (int i=0; i<values.length; i++) {
233 Binding cb = b.getComponentBinding(i);
234 values[i] = cb.accept(this);
236 b.setComponents(result, values);
239 for (int i=0; i<values.length; i++) {
240 Binding cb = b.getComponentBinding(i);
241 values[i] = cb.accept(this);
243 result = b.create(values);
247 } catch (BindingException e) {
248 throw new RuntimeBindingException(e);
253 public Object visit(StringBinding b) {
254 Object result = pickCached() ? map.get(b) : null;
255 if (result!=null) return result;
256 StringType st = b.type();
257 int min = st.minLength();
258 int max = Math.max(min, Math.min(64, st.maxLength()));
259 int c = (int) ( min + nextRandom(max-min+1) );
261 StringBuilder sb = new StringBuilder(c);
262 for (int i=0; i<c; i++)
263 sb.append( CHARS.charAt( random.nextInt(CHARS.length()) ) );
265 result = b.createUnchecked(sb.toString());
272 public Object visit(UnionBinding b) {
273 Object result = pickCached() ? map.get(b) : null;
274 if (result!=null) return result;
275 UnionType ut = b.type();
277 int tag = random.nextInt( ut.getComponentCount() );
279 Binding componentBinding = b.getComponentBinding( tag );
280 Object randomValue = componentBinding.accept(this);
281 result = b.createUnchecked(tag, randomValue);
287 public Object visit(VariantBinding b) {
289 Object result = pickCached() ? map.get(b) : null;
290 if (result!=null) return result;
292 int maxDepth = random.nextInt(3)+1;
293 Datatype randomType = randomType(0, maxDepth);
294 Binding randomBinding;
295 randomBinding = scheme.getBinding( randomType );
296 Object randomValue = randomBinding.accept(this);
298 result = b.createUnchecked(randomBinding, randomValue);
301 } catch (BindingConstructionException e) {
302 throw new RuntimeBindingConstructionException(e);
306 boolean isKeyShortEnough(Binding binding, Object value) {
309 key = (String) Bindings.adapt(value, binding, Bindings.STR_VARIANT);
310 return key.length()<=200;
311 } catch (AdaptException e) {
312 throw new RuntimeException(e);
317 public Object visit(MapBinding b) {
318 Object result = pickCached() ? map.get(b) : null;
319 if (result!=null) return result;
321 int c = random.nextInt(32);
323 Binding keyBinding = b.getKeyBinding();
324 Binding valueBinding = b.getValueBinding();
325 Object[] keys = new Object[c];
326 Object[] values = new Object[c];
327 for (int i=0; i<c; i++) {
331 if (keyBinding.type().equals(Datatypes.VARIANT)) {
333 key = keyBinding.accept(this);
334 } while( !keyBinding.type().equals(Datatypes.VARIANT) || !isKeyShortEnough(keyBinding, key) );
336 key = keyBinding.accept(this);
341 values[i] = valueBinding.accept(this);
344 result = b.createUnchecked(keys, values);
349 public Datatype randomType(int depth, int maxDepth) {
350 // Random until non variant
352 if (depth<maxDepth) {
353 tag = random.nextInt( 12 );
354 if (random.nextInt(500)==0) tag=12;
356 tag = random.nextInt( 7 );
360 return new BooleanType();
363 Limit lowerLimit = Limit.exclusive((random.nextInt() & 255) - 128);
364 Limit upperLimit = Limit.exclusive((random.nextInt() & 255) - 128);
365 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
366 ByteType result = new ByteType(null, range);
367 if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
372 Limit lowerLimit = Limit.inclusive(0);
373 Limit upperLimit = Limit.exclusive(random.nextInt());
374 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
375 IntegerType result = new IntegerType(null, range);
376 if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
381 Limit lowerLimit = Limit.inclusive(0);
382 Limit upperLimit = Limit.exclusive(random.nextLong());
383 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
384 LongType result = new LongType(null, range);
385 if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
390 Limit lowerLimit = Limit.inclusive(0);
391 Limit upperLimit = Limit.exclusive(random.nextDouble() * random.nextInt());
392 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
393 FloatType result = new FloatType(null, range);
394 if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
399 Limit lowerLimit = Limit.inclusive(0);
400 Limit upperLimit = Limit.exclusive(random.nextDouble() * random.nextInt());
401 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
402 DoubleType result = new DoubleType(null, range);
403 if (result.minValue()>result.maxValue()) result.setRange( (Range) null );
408 Limit lowerLimit = Limit.inclusive(0);
409 Limit upperLimit = Limit.exclusive(random.nextInt(1024));
410 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
411 StringType result = new StringType(null, null, range);
412 if (result.minLength()>result.maxLength()) result.setLength( (Range) null );
417 int c = random.nextInt(16);
418 Component[] components = new Component[c];
419 String[] names = randomUniqueNames(c);
420 for (int i=0; i<c; i++) {
421 components[i] = new Component(names[i], randomType(depth+1, maxDepth));
423 return new RecordType(refereableRecords ? random.nextBoolean() : false, components);
427 Limit lowerLimit = Limit.inclusive(random.nextInt(16));
428 Limit upperLimit = Limit.exclusive(random.nextInt(16));
429 Range range = random.nextBoolean() ? Range.between(lowerLimit, upperLimit) : null;
430 ArrayType result = new ArrayType(randomType(depth+1, maxDepth), range);
431 if (result.minLength()>result.maxLength()) result.setLength( (Range) null );
436 return new MapType(randomType(depth+1, maxDepth), randomType(depth+1, maxDepth));
440 return new OptionalType( randomType(depth+1, maxDepth) );
444 int c = random.nextInt(16)+1;
445 Component[] components = new Component[c];
446 String[] names = randomUniqueNames(c);
447 for (int i=0; i<c; i++) {
448 components[i] = new Component(names[i], randomType(depth+1, maxDepth));
450 return new UnionType(components);
454 return new VariantType();
461 * Answers to the question weather we should pick a cached value.
462 * There is 10% if referable records is enabled.
466 boolean pickCached() {
467 if (!refereableRecords) return false;
468 return refereableRecords && random.nextInt(10)==0;
471 String randomName() {
472 int nameLength = random.nextInt(32)+1;
473 StringBuilder sb = new StringBuilder(nameLength);
474 for (int j=0; j<nameLength; j++)
475 sb.append( CHARS.charAt( random.nextInt(CHARS.length()) ) );
476 return sb.toString();
480 * Create <code>count</code> unique random names.
485 String[] randomUniqueNames(int count) {
486 HashSet<String> result = new HashSet<String>(count);
487 for (int i=0; i<count; i++) {
491 } while (result.contains(name));
494 return result.toArray(new String[count]);
497 long nextRandom(long n) {
498 long v = random.nextLong();