]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/util/RandomValue.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / util / RandomValue.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
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
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.binding.util;
13
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Map;
17 import java.util.Random;
18 import java.util.WeakHashMap;
19
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;
61
62 /**
63  * Visitor that creates a instance with random value.
64  * This visitor may throw RuntimeBindingException. 
65  * 
66  * Type                     Value
67  * ------------------------------------------------------
68  * Boolean                  false/true
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
78  * 
79  * TODO Create String according to the pattern
80  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
81  */
82 public class RandomValue implements Visitor<Object> {
83         
84         public boolean refereableRecords = true;
85         
86         public Random random;
87         
88         /** Map of default values already created. Used to link back to recursive records */
89         Map<Binding, Object> map = new WeakHashMap<Binding, Object>(1);
90         
91         BindingScheme scheme = new MutableBindingFactory( new HashMap<Datatype, Binding>() );
92
93         static String CHARS = "abcdefghijklmnopqrstuvwxyz ,.-'/-+ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"#�%&/()=\n\\\\r'";
94         
95         public RandomValue() {
96                 this.random = new Random();
97         }
98
99         public RandomValue(Random random) {
100                 this.random = random;
101         }
102
103         public RandomValue(int seed) {
104                 this.random = new Random(seed);
105         }
106         
107         public Random getRandom() {
108                 return random;
109         }
110
111         @Override
112         public Object visit(ArrayBinding b) {
113                 Object result = pickCached() ? map.get(b) : null;
114                 if (result!=null) return result;
115                 
116                 ArrayType at = b.type();
117                 
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));
121                 
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); 
126                 }
127                 result = b.createUnchecked(array);
128                 map.put(b, result);
129                 return result;
130         }
131
132         @Override
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() );
137                 map.put(b, result);
138                 return result;
139         }
140
141         @Override
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);
151                 map.put(b, result);
152                 return result;
153         }
154
155         @Override
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);
165                 map.put(b, result);
166                 return result;
167         }
168
169         @Override
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);
179                 map.put(b, result);
180                 return result;
181         }
182
183         @Override
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);
193                 map.put(b, result);
194                 return result;
195         }
196
197         @Override
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);
207                 map.put(b, result);
208                 return result;
209         }
210
211         @Override
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) );
217                 map.put(b, result);
218                 return result;
219         }
220
221         @Override
222         public Object visit(RecordBinding b) {
223                 try {
224                         Object result = pickCached() ? map.get(b) : null;
225                         if (result!=null) return result;
226                         
227                         Object[] values = new Object[ b.getComponentCount() ];
228                         
229                         if (b.type().isReferable()) {
230                                 result = b.createPartial();
231                                 map.put(b, result);
232                                 for (int i=0; i<values.length; i++) {
233                                         Binding cb = b.getComponentBinding(i);                  
234                                         values[i] = cb.accept(this);
235                                 }
236                                 b.setComponents(result, values);
237                         } else {
238                                 
239                                 for (int i=0; i<values.length; i++) {
240                                         Binding cb = b.getComponentBinding(i);                  
241                                         values[i] = cb.accept(this);
242                                 }
243                                 result = b.create(values);
244                                 map.put(b, result);
245                         }
246                         return result;
247                 } catch (BindingException e) {
248                         throw new RuntimeBindingException(e);
249                 }
250         }
251
252         @Override
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) ); 
260                 
261                 StringBuilder sb = new StringBuilder(c);
262                 for (int i=0; i<c; i++)
263                         sb.append( CHARS.charAt( random.nextInt(CHARS.length()) ) );
264                 
265                 result = b.createUnchecked(sb.toString());
266                 
267                 map.put(b, result);
268                 return result;
269         }
270
271         @Override
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();
276
277                 int tag = random.nextInt( ut.getComponentCount() );             
278                 
279                 Binding componentBinding = b.getComponentBinding( tag );
280                 Object randomValue = componentBinding.accept(this);
281                 result = b.createUnchecked(tag, randomValue);
282                 map.put(b, result);
283                 return result;
284         }
285         
286         @Override
287         public Object visit(VariantBinding b) {
288                 try {
289                         Object result = pickCached() ? map.get(b) : null;
290                         if (result!=null) return result;                
291                         
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);  
297                         
298                         result = b.createUnchecked(randomBinding, randomValue);
299                         map.put(b, result);
300                 return result;
301                 } catch (BindingConstructionException e) {
302                         throw new RuntimeBindingConstructionException(e);
303                 }
304         }
305
306         boolean isKeyShortEnough(Binding binding, Object value) {
307                 try {
308                         String key;
309                         key = (String) Bindings.adapt(value, binding, Bindings.STR_VARIANT);
310                         return key.length()<=200;
311                 } catch (AdaptException e) {
312                         throw new RuntimeException(e);
313                 } 
314         }
315         
316         @Override
317         public Object visit(MapBinding b) {
318                 Object result = pickCached() ? map.get(b) : null;
319                 if (result!=null) return result;
320                         
321                 int c = random.nextInt(32);
322                 
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++) {
328                         
329                         
330                         Object key = null;
331                         if (keyBinding.type().equals(Datatypes.VARIANT)) {
332                                 do {
333                                         key = keyBinding.accept(this);
334                                 } while( !keyBinding.type().equals(Datatypes.VARIANT) || !isKeyShortEnough(keyBinding, key) );                  
335                         } else {
336                                 key = keyBinding.accept(this);                          
337                         }
338                         
339                         keys[i] = key;
340                         
341                         values[i] = valueBinding.accept(this);
342                 }               
343                 
344                 result = b.createUnchecked(keys, values);
345                 map.put(b, result);
346                 return result;
347         }
348
349         public Datatype randomType(int depth, int maxDepth) {
350                 // Random until non variant 
351                 int tag = 0;
352                 if (depth<maxDepth) {
353                         tag = random.nextInt( 12 );
354                         if (random.nextInt(500)==0) tag=12;
355                 } else {
356                         tag = random.nextInt( 7 );
357                 }
358                 
359                 if (tag==0) {
360                         return new BooleanType();
361                 }               
362                 if (tag==1) {
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 );
368                         return result;
369                 }
370                                 
371                 if (tag==2) {
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 );
377                         return result;
378                 }               
379
380                 if (tag==3) {
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 );
386                         return result;
387                 }
388
389                 if (tag==4) {
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 );
395                         return result;
396                 }
397                 
398                 if (tag==5) {
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 );
404                         return result;
405                 }
406
407                 if (tag==6) {
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 );
413                         return result;
414                 }               
415
416                 if (tag==7) {
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));
422                         }
423                         return new RecordType(refereableRecords ? random.nextBoolean() : false, components);
424                 }
425                 
426                 if (tag==8) {
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 );            
432                         return result;
433                 }               
434                 
435                 if (tag==9) {
436                         return new MapType(randomType(depth+1, maxDepth), randomType(depth+1, maxDepth));
437                 }               
438                 
439                 if (tag==10) {
440                         return new OptionalType( randomType(depth+1, maxDepth) );
441                 }               
442                 
443                 if (tag==11) {
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));
449                         }
450                         return new UnionType(components);
451                 }
452                 
453                 if (tag==12) {
454                         return new VariantType();
455                 }
456
457                 return null;
458         }
459
460         /**
461          * Answers to the question weather we should pick a cached value.
462          * There is 10% if referable records is enabled.
463          * 
464          * @return
465          */
466         boolean pickCached() {
467                 if (!refereableRecords) return false;
468                 return refereableRecords && random.nextInt(10)==0;  
469         }
470     
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(); 
477         }
478         
479         /**
480          * Create <code>count</code> unique random names. 
481          * 
482          * @param count
483          * @return
484          */
485         String[] randomUniqueNames(int count) {
486                 HashSet<String> result = new HashSet<String>(count);
487                 for (int i=0; i<count; i++) {
488                         String name = null;
489                         do {                            
490                                 name = randomName();
491                         } while (result.contains(name));
492                         result.add(name);
493                 }
494                 return result.toArray(new String[count]);
495         }
496                 
497         long nextRandom(long n) {
498                 long v = random.nextLong();
499                 v = Math.abs(v);
500                 return v % n;
501         }
502         
503 }
504