]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/parser/repository/DataValueRepository.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / parser / repository / DataValueRepository.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.parser.repository;
13
14 import java.io.IOException;
15 import java.io.StringReader;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.IdentityHashMap;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22
23 import org.simantics.databoard.Bindings;
24 import org.simantics.databoard.Datatypes;
25 import org.simantics.databoard.binding.ArrayBinding;
26 import org.simantics.databoard.binding.Binding;
27 import org.simantics.databoard.binding.BooleanBinding;
28 import org.simantics.databoard.binding.ByteBinding;
29 import org.simantics.databoard.binding.DoubleBinding;
30 import org.simantics.databoard.binding.FloatBinding;
31 import org.simantics.databoard.binding.IntegerBinding;
32 import org.simantics.databoard.binding.LongBinding;
33 import org.simantics.databoard.binding.MapBinding;
34 import org.simantics.databoard.binding.OptionalBinding;
35 import org.simantics.databoard.binding.RecordBinding;
36 import org.simantics.databoard.binding.StringBinding;
37 import org.simantics.databoard.binding.UnionBinding;
38 import org.simantics.databoard.binding.VariantBinding;
39 import org.simantics.databoard.binding.error.BindingConstructionException;
40 import org.simantics.databoard.binding.error.BindingException;
41 import org.simantics.databoard.binding.error.RuntimeBindingException;
42 import org.simantics.databoard.binding.factory.BindingScheme;
43 import org.simantics.databoard.binding.mutable.MutableVariant;
44 import org.simantics.databoard.file.RuntimeIOException;
45 import org.simantics.databoard.parser.DataParser;
46 import org.simantics.databoard.parser.DataValuePrinter;
47 import org.simantics.databoard.parser.ParseException;
48 import org.simantics.databoard.parser.PrintFormat;
49 import org.simantics.databoard.parser.TokenMgrError;
50 import org.simantics.databoard.parser.ast.value.AstArray;
51 import org.simantics.databoard.parser.ast.value.AstBoolean;
52 import org.simantics.databoard.parser.ast.value.AstComponentAssignment;
53 import org.simantics.databoard.parser.ast.value.AstFloat;
54 import org.simantics.databoard.parser.ast.value.AstInteger;
55 import org.simantics.databoard.parser.ast.value.AstMap;
56 import org.simantics.databoard.parser.ast.value.AstMapAssignment;
57 import org.simantics.databoard.parser.ast.value.AstNull;
58 import org.simantics.databoard.parser.ast.value.AstRecord;
59 import org.simantics.databoard.parser.ast.value.AstReference;
60 import org.simantics.databoard.parser.ast.value.AstString;
61 import org.simantics.databoard.parser.ast.value.AstTaggedValue;
62 import org.simantics.databoard.parser.ast.value.AstTuple;
63 import org.simantics.databoard.parser.ast.value.AstValue;
64 import org.simantics.databoard.parser.ast.value.AstValueDefinition;
65 import org.simantics.databoard.parser.ast.value.AstVariant;
66 import org.simantics.databoard.parser.ast.value.visitor.AstValueVisitor;
67 import org.simantics.databoard.parser.unparsing.DataTypePrinter;
68 import org.simantics.databoard.type.Component;
69 import org.simantics.databoard.type.Datatype;
70 import org.simantics.databoard.type.MapType;
71 import org.simantics.databoard.type.RecordType;
72 import org.simantics.databoard.type.UnionType;
73
74 /**
75  * Data value repository is a collection of name data values.
76  * Each value is associated with type. 
77  * 
78  * <p>
79  * It can also translate data lines and value texts to objects and 
80  * print values as 
81  * 
82  * 
83  * 
84  * @author Hannu Niemist&ouml;
85  */
86 public class DataValueRepository {
87         
88         // Type respository to convert 
89         DataTypeRepository typeRepository = Datatypes.datatypeRepository;
90         
91         // Scheme to convert values 
92         BindingScheme bindingScheme = Bindings.mutableBindingFactory;
93         
94         /** Stored values */
95         Map<String, MutableVariant> values = new HashMap<String, MutableVariant>();
96         Map<Object, String> nameMap = new IdentityHashMap<Object, String>();
97         
98         public MutableVariant get(String name) {
99                 return values.get(name);
100         }
101         
102         public String getName(Object value) {
103                 return nameMap.get(value);
104         }
105
106         public void put(String name, Binding binding, Object value) {
107                 put(name, new MutableVariant(binding, value));
108         }
109         
110         public void put(String name, MutableVariant value) {
111                 values.put(name, value);
112                 nameMap.put(value.getValue(), name);
113         }
114         
115         public MutableVariant remove(String name) {
116                 MutableVariant value = values.remove(name);
117                 if (value==null) return null;
118                 nameMap.remove(value.getValue());
119                 return value;
120         }
121         
122         public void clear() {
123                 values.clear();
124                 nameMap.clear();
125         }
126         
127         /**
128          * Get a view of the value names in this repository
129          * 
130          * @return names
131          */
132         public Set<String> getValueNames() {
133                 return values.keySet();
134         }
135         
136         /**
137          * Translates a data value from an abstract syntax tree to an object by the binding.
138          * 
139          * @param value
140          * @param binding
141          * @return value
142          * @throws DataTypeSyntaxError
143          */
144         public Object translate(AstValue value, Binding binding) throws DataTypeSyntaxError {           
145                 try {
146                         if(value instanceof AstReference) {
147                                 String name = ((AstReference)value).name;
148                                 MutableVariant v = get(name);
149                                 if(v == null) {
150                                         if(binding instanceof UnionBinding) {
151                                                 UnionBinding b = (UnionBinding)binding;
152                                                 UnionType type = b.type();
153                                                 Integer index = type.getComponentIndex(name);
154                                                 if(index != null)
155                                                         try {
156                                                                 return b.create(index, 
157                                                                                 b.getComponentBinding(index).createDefault());
158                                                         } catch(BindingException e) {
159                                                                 throw new DataTypeSyntaxError(e);
160                                                         }
161                                         }
162                                         throw new DataTypeSyntaxError("Undefined reference to " + name + ".");
163                                 }
164                                 return Bindings.adaptUnchecked(v.getValue(), v.getBinding(), binding);
165                         }
166                         return binding.accept(new ValueTranslator(value));
167                 } catch(ValueTranslationRuntimeException e) {
168                         throw new DataTypeSyntaxError(e);
169                 }
170         }
171         
172         /**
173          * Translates a data value from a string to an object by the binding.
174          * @param value
175          * @param binding
176          * @return value
177          * @throws DataTypeSyntaxError
178          */
179         public Object translate(String value, Binding binding) throws DataTypeSyntaxError {
180                 try {
181                         return translate(new DataParser(new StringReader(value)).value(), binding);
182                 } catch (TokenMgrError e) {
183                         throw new DataTypeSyntaxError(e);
184                 } catch (ParseException e) {
185                         throw new DataTypeSyntaxError(e);
186                 }
187         }
188         
189         /**
190          * Adds a value definition to the repository
191          * @param def
192          * @throws DataTypeSyntaxError
193          */
194         public void addValueDefinition(AstValueDefinition def) throws DataTypeSyntaxError {
195                 Datatype type = typeRepository.translate(def.type);
196                 Binding binding = Bindings.getMutableBinding(type);
197                 MutableVariant variant = new MutableVariant(binding, translate(def.value, binding));
198                 values.put(def.name, variant);
199                 nameMap.put(variant.getValue(), def.name);
200         }
201         
202         /**
203          * Adds a value definition to the repository
204          * @param def
205          * @return name
206          * @throws DataTypeSyntaxError
207          */
208         public String addValueDefinition(String def) throws DataTypeSyntaxError {
209                 try {
210                         StringReader reader = new StringReader(def);
211                         DataParser parser = new DataParser( reader );
212                         AstValueDefinition valueAstDef = parser.valueDefinition(); 
213                         addValueDefinition( valueAstDef );
214                         return valueAstDef.name;
215                 } catch (TokenMgrError e) {
216                         throw new DataTypeSyntaxError(e);
217                 } catch (ParseException e) {
218                         throw new DataTypeSyntaxError(e);
219                 }
220         }
221         
222         /**
223          * Adds multiple value definitions to the repository
224          * 
225          * @param defs
226          * @throws DataTypeSyntaxError
227          */
228         public void addValueDefinitions(Collection<AstValueDefinition> defs) throws DataTypeSyntaxError {
229                 // TODO recursive definitions
230                 for(AstValueDefinition def : defs)
231                         addValueDefinition(def);
232         }
233         
234         /**
235          * Adds multiple value definitions to the repository
236          * @param def
237          * @throws DataTypeSyntaxError
238          */
239         public void addValueDefinitions(String def) throws DataTypeSyntaxError {
240                 try {
241                         addValueDefinitions(new DataParser(new StringReader(def)).valueDefinitions());
242                 } catch (TokenMgrError e) {
243                         throw new DataTypeSyntaxError(e);
244                 } catch (ParseException e) {
245                         throw new DataTypeSyntaxError(e);
246                 }
247         }
248         
249         public DataTypeRepository getTypeRepository() {
250                 return typeRepository;
251         }
252
253         public void setTypeRepository(DataTypeRepository typeRepository) {
254                 this.typeRepository = typeRepository;
255         }
256
257         public BindingScheme getBindingScheme() {
258                 return bindingScheme;
259         }
260
261         public void setBindingScheme(BindingScheme bindingScheme) {
262                 this.bindingScheme = bindingScheme;
263         }
264
265         /**
266          * Print the content part of a data value. This excludes the name and type of the value.
267          * 
268          * @param valueName
269          * @return value or <code>null</code> if value doesn't exist
270          * @throws BindingException 
271          * @throws IOException 
272          */
273         public String printValue(String valueName) throws IOException, BindingException {
274                 MutableVariant value = get(valueName);
275                 if (value==null) return null;
276                 StringBuilder sb = new StringBuilder();
277                 DataValuePrinter vp = new DataValuePrinter(sb, this);
278                 vp.print(value);
279                 return sb.toString();
280         }
281         
282         /**
283          * Print the whole value repository
284          * 
285          * @param sb
286          * @throws IOException
287          * @throws BindingException
288          */
289         public void print(StringBuilder sb)
290         throws IOException, BindingException
291         {
292                 DataValuePrinter vp = new DataValuePrinter(sb, this);
293                 vp.setFormat( PrintFormat.SINGLE_LINE );
294                 DataTypePrinter tp = new DataTypePrinter( sb );
295                 tp.setLinefeed( false );
296                         
297                 for (Entry<String, MutableVariant> e : values.entrySet()) {
298                         String name = e.getKey();
299                         MutableVariant value = e.getValue();
300                         Datatype type = value.type();
301                         sb.append( name+" : " );
302                         tp.print(type);
303                         sb.append( " = " );
304                         vp.print(value);                        
305                         sb.append("\n");
306                 }
307         }       
308
309         /**
310          * Print the whole data value repository as a single multiline string
311          * 
312          * @throws RuntimeBindingException
313          * @throws {@link RuntimeIOException}
314          */
315         @Override
316         public String toString() {
317                 try {
318                         StringBuilder sb = new StringBuilder();
319                         print(sb);
320                         return sb.toString();
321                 } catch (BindingException e) {
322                         throw new RuntimeBindingException(e);
323                 } catch (IOException e) {
324                         throw new RuntimeIOException(e);
325                 }
326         }
327         
328         /**
329          * Gives a data type to a value heuristically.
330          */
331         public Datatype guessDataType(AstValue value) throws DataTypeSyntaxError {
332                 return value.accept(guessDataType);
333         }
334         
335         /**
336          * Gives a data type to a value heuristically.
337          */
338         public Datatype guessDataType(String value) throws DataTypeSyntaxError {
339                 try {
340                         return guessDataType(new DataParser(new StringReader(value)).value());
341                 } catch (TokenMgrError e) {
342                         throw new DataTypeSyntaxError(e);
343                 } catch (ParseException e) {
344                         throw new DataTypeSyntaxError(e);
345                 }
346         }
347
348         class ValueTranslator implements Binding.Visitor<Object> {
349
350                 AstValue value;
351                 
352                 public ValueTranslator(AstValue value) {
353                         this.value = value;
354                 }
355                 
356                 private ValueTranslationRuntimeException typeError(Binding expectedType, AstValue actualValue) {
357                         throw new ValueTranslationRuntimeException("Expected " + expectedType.type().toSingleLineString() + 
358                                         " but got " + actualValue.getClass().getSimpleName() + ".");
359                 }
360
361                 @Override
362                 public Object visit(ArrayBinding b) {
363                         if(value instanceof AstArray) {
364                                 AstArray array = (AstArray)value;
365                                 Object[] components = new Object[array.elements.size()];
366                                 Binding componentBinding = b.getComponentBinding();
367                                 int i=0;
368                                 for(AstValue component : array.elements) {
369                                         value = component;
370                                         components[i++] = componentBinding.accept(this);
371                                 }
372                                 return b.createUnchecked(components);
373                         }
374                         throw typeError(b, value);
375                 }
376
377                 @Override
378                 public Object visit(BooleanBinding b) {
379                         if(value instanceof AstBoolean) {                               
380                                 return b.createUnchecked(((AstBoolean)value).value);
381                         }
382                         throw typeError(b, value);
383                 }
384
385                 @Override
386                 public Object visit(DoubleBinding b) {
387                         if(value instanceof AstFloat) {                         
388                                 return b.createUnchecked(((AstFloat)value).value);
389                         }
390                         if(value instanceof AstInteger) {                               
391                                 return b.createUnchecked(((AstInteger)value).value);
392                         }
393                         throw typeError(b, value);
394                 }
395
396                 @Override
397                 public Object visit(FloatBinding b) {
398                         if(value instanceof AstFloat) {                         
399                                 return b.createUnchecked(((AstFloat)value).value);
400                         }
401                         if(value instanceof AstInteger) {                               
402                                 return b.createUnchecked(((AstInteger)value).value);
403                         }
404                         throw typeError(b, value);
405                 }
406
407                 @Override
408                 public Object visit(IntegerBinding b) {
409                         if(value instanceof AstInteger) {                               
410                                 return b.createUnchecked(((AstInteger)value).value);
411                         }
412                         throw typeError(b, value);
413                 }
414
415                 @Override
416                 public Object visit(ByteBinding b) {
417                         if(value instanceof AstInteger) {                               
418                                 return b.createUnchecked(((AstInteger)value).value);
419                         }
420                         throw typeError(b, value);
421                 }
422
423                 @Override
424                 public Object visit(LongBinding b) {
425                         if(value instanceof AstInteger) {                               
426                                 return b.createUnchecked(((AstInteger)value).value);
427                         }
428                         throw typeError(b, value);
429                 }
430
431                 @Override
432                 public Object visit(OptionalBinding b) {
433                         if(value == AstNull.NULL)
434                                 return b.createNoValueUnchecked();
435                         else
436                                 return b.createValueUnchecked(b.getComponentBinding().accept(this));                            
437                 }
438
439                 @Override
440                 public Object visit(RecordBinding b) {
441                         if(value instanceof AstRecord) {
442                                 AstRecord record = (AstRecord)value;
443                                 Object[] components = new Object[b.getComponentCount()];
444                                 boolean[] assigned = new boolean[b.getComponentCount()];
445                                 for(AstComponentAssignment assignment : record.components) {
446                                         value = assignment.value;
447                                         Integer index = b.type().getComponentIndex(assignment.component);
448                                         if(index == null)
449                                                 throw new ValueTranslationRuntimeException("Invalid record component " + assignment.component + ".");
450                                         components[index] = b.getComponentBinding(index).accept(this);
451                                         assigned[index] = true;
452                                 }
453                                 for(int i=0;i<assigned.length;++i)
454                                         if(!assigned[i]) {
455                                                 Binding binding = b.getComponentBinding(i);
456                                                 if(binding instanceof OptionalBinding)
457                                                         components[i] = ((OptionalBinding)binding).createNoValueUnchecked();
458                                                 else
459                                                         throw new ValueTranslationRuntimeException("Non-optional field " + 
460                                                                         b.type().getComponent(i).name + " is not defined.");
461                                         }
462                                 return b.createUnchecked(components);                           
463                         }
464                         if(value instanceof AstTuple) {
465                                 AstTuple tuple = (AstTuple)value;
466                                 Object[] components = new Object[b.getComponentCount()];
467                                 int i=0;
468                                 for(AstValue element : tuple.elements) {
469                                         value = element;
470                                         components[i] = b.getComponentBinding(i).accept(this);
471                                         ++i;
472                                 }
473                                 return b.createUnchecked(components);
474                         }
475                         throw typeError(b, value);
476                 }
477
478                 @Override
479                 public Object visit(StringBinding b) {
480                         if(value instanceof AstString) {                                
481                                 return b.createUnchecked(((AstString)value).value);
482                         }
483                         throw typeError(b, value);
484                 }
485
486                 @Override
487                 public Object visit(UnionBinding b) {
488                         if(value instanceof AstTaggedValue) {
489                                 AstTaggedValue taggedValue = (AstTaggedValue)value;
490                                 Integer tagIndex = b.type().getComponentIndex(taggedValue.tag);
491                                 if(tagIndex == null)
492                                         throw new ValueTranslationRuntimeException("Invalid union tag " + taggedValue.tag + ".");
493                                 value = taggedValue.value;
494                                 return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).accept(this));
495                         }
496                         else if(value instanceof AstReference) {
497                             AstReference ref = (AstReference)value;
498                             Integer tagIndex = b.type().getComponentIndex(ref.name);
499                             if(tagIndex == null)
500                     throw new ValueTranslationRuntimeException("Invalid union tag " + ref.name + ".");
501                             try {
502                                 return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).createDefault());
503                             } catch(BindingException e) {                               
504                             }
505                         }
506                         throw typeError(b, value);
507                 }
508
509                 @Override
510                 public Object visit(VariantBinding b) {
511                         try {
512                                 if(value instanceof AstVariant) {
513                                         AstVariant variant = (AstVariant)value;
514                                         Datatype dataType = typeRepository.translate(variant.type);
515                                         Binding binding = bindingScheme.getBinding(dataType);
516                                         value = variant.value;
517                                         return b.createUnchecked(binding, binding.accept(this));
518                                 }
519                                 else {
520                                         Datatype dataType = guessDataType(value);
521                                         Binding binding = bindingScheme.getBinding(dataType);
522                                         return b.createUnchecked(binding, binding.accept(this));
523                                 }
524                         } catch(DataTypeSyntaxError e) {
525                                 throw new ValueTranslationRuntimeException(e);
526                         } catch (BindingConstructionException e) {
527                                 throw new ValueTranslationRuntimeException(e);
528                         }
529                 }
530
531                 @Override
532                 public Object visit(MapBinding b) {
533                         if(value instanceof AstMap) {
534                                 AstMap map = (AstMap)value;
535                                 Object[] keys = new Object[map.components.size()];
536                                 Object[] values = new Object[map.components.size()];
537                                 Binding keyBinding = b.getKeyBinding();
538                                 Binding valueBinding = b.getValueBinding();
539                                 int i = 0;
540                                 for(AstMapAssignment assignment : map.components) {
541                                         value = assignment.key;
542                                         keys[i] = keyBinding.accept(this);                                      
543                                         value = assignment.value;
544                                         values[i] = valueBinding.accept(this);                                  
545                                         ++i;
546                                 }
547                                 return b.createUnchecked(keys, values);                         
548                         }
549                         throw typeError(b, value);
550                 }
551                 
552         }
553         
554         AstValueVisitor<Datatype> guessDataType = new AstValueVisitor<Datatype>() {
555
556                 @Override
557                 public Datatype visit(AstArray astArray) {
558                         if(astArray.elements.isEmpty())
559                                 throw new ValueTranslationRuntimeException("Cannot guess the data type of empty array.");
560                         return astArray.elements.get(0).accept(this);
561                 }
562
563                 @Override
564                 public Datatype visit(AstBoolean astBoolean) {
565                         return Datatypes.BOOLEAN;
566                 }
567
568                 @Override
569                 public Datatype visit(AstFloat astFloat) {
570                         return Datatypes.DOUBLE;
571                 }
572
573                 @Override
574                 public Datatype visit(AstInteger astInteger) {
575                         return Datatypes.INTEGER;
576                 }
577
578                 @Override
579                 public Datatype visit(AstMap astMap) {
580                         if(astMap.components.isEmpty())
581                                 throw new ValueTranslationRuntimeException("Cannot guess the data type of empty map.");
582                         AstMapAssignment assignment = astMap.components.get(0);
583                         return new MapType(assignment.key.accept(this), assignment.value.accept(this));
584                 }
585
586                 @Override
587                 public Datatype visit(AstNull astNull) {
588                         throw new ValueTranslationRuntimeException("Cannot guess the data type");
589                 }
590
591                 @Override
592                 public Datatype visit(AstRecord astRecord) {
593                         Component[] components = new Component[astRecord.components.size()];
594                         int i = 0;
595                         for(AstComponentAssignment assignment : astRecord.components) {
596                                 components[i++] = new Component(
597                                                 assignment.component,
598                                                 assignment.value.accept(this)
599                                                 );
600                         }
601                         return new RecordType(false, components);
602                 }
603
604                 @Override
605                 public Datatype visit(AstReference astReference) {
606                         MutableVariant v = get(astReference.name);
607                         if(v == null)
608                                 throw new ValueTranslationRuntimeException("Undefined reference to " + astReference.name + ".");
609                         return v.type();
610                 }
611
612                 @Override
613                 public Datatype visit(AstString astString) {
614                         return Datatypes.STRING;
615                 }
616
617                 @Override
618                 public Datatype visit(AstTaggedValue astTaggedValue) {
619                         // Guessed datatype would be a union with just one component. Not very useful.
620                         throw new ValueTranslationRuntimeException("Cannot guess the data type of tagged value");
621                 }
622
623                 @Override
624                 public Datatype visit(AstTuple astTuple) {
625                         Component[] components = new Component[astTuple.elements.size()];
626                         int i = 0;
627                         for(AstValue value : astTuple.elements) {
628                                 components[i] = new Component(
629                                                 Integer.toString(i),
630                                                 value.accept(this)
631                                                 );
632                                 ++i;
633                         }
634                         return new RecordType(false, components);
635                 }
636
637                 @Override
638                 public Datatype visit(AstVariant astVariant) {
639                         return Datatypes.VARIANT;
640                 }
641         };
642         
643         
644 }