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.parser.repository;
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;
20 import java.util.Map.Entry;
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;
75 * Data value repository is a collection of name data values.
76 * Each value is associated with type.
79 * It can also translate data lines and value texts to objects and
84 * @author Hannu Niemistö
86 public class DataValueRepository {
88 // Type respository to convert
89 DataTypeRepository typeRepository = Datatypes.datatypeRepository;
91 // Scheme to convert values
92 BindingScheme bindingScheme = Bindings.mutableBindingFactory;
95 Map<String, MutableVariant> values = new HashMap<String, MutableVariant>();
96 Map<Object, String> nameMap = new IdentityHashMap<Object, String>();
98 public MutableVariant get(String name) {
99 return values.get(name);
102 public String getName(Object value) {
103 return nameMap.get(value);
106 public void put(String name, Binding binding, Object value) {
107 put(name, new MutableVariant(binding, value));
110 public void put(String name, MutableVariant value) {
111 values.put(name, value);
112 nameMap.put(value.getValue(), name);
115 public MutableVariant remove(String name) {
116 MutableVariant value = values.remove(name);
117 if (value==null) return null;
118 nameMap.remove(value.getValue());
122 public void clear() {
128 * Get a view of the value names in this repository
132 public Set<String> getValueNames() {
133 return values.keySet();
137 * Translates a data value from an abstract syntax tree to an object by the binding.
142 * @throws DataTypeSyntaxError
144 public Object translate(AstValue value, Binding binding) throws DataTypeSyntaxError {
146 if(value instanceof AstReference) {
147 String name = ((AstReference)value).name;
148 MutableVariant v = get(name);
150 if(binding instanceof UnionBinding) {
151 UnionBinding b = (UnionBinding)binding;
152 UnionType type = b.type();
153 Integer index = type.getComponentIndex(name);
156 return b.create(index,
157 b.getComponentBinding(index).createDefault());
158 } catch(BindingException e) {
159 throw new DataTypeSyntaxError(e);
162 throw new DataTypeSyntaxError("Undefined reference to " + name + ".");
164 return Bindings.adaptUnchecked(v.getValue(), v.getBinding(), binding);
166 return binding.accept(new ValueTranslator(value));
167 } catch(ValueTranslationRuntimeException e) {
168 throw new DataTypeSyntaxError(e);
173 * Translates a data value from a string to an object by the binding.
177 * @throws DataTypeSyntaxError
179 public Object translate(String value, Binding binding) throws DataTypeSyntaxError {
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);
190 * Adds a value definition to the repository
192 * @throws DataTypeSyntaxError
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);
203 * Adds a value definition to the repository
206 * @throws DataTypeSyntaxError
208 public String addValueDefinition(String def) throws DataTypeSyntaxError {
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);
223 * Adds multiple value definitions to the repository
226 * @throws DataTypeSyntaxError
228 public void addValueDefinitions(Collection<AstValueDefinition> defs) throws DataTypeSyntaxError {
229 // TODO recursive definitions
230 for(AstValueDefinition def : defs)
231 addValueDefinition(def);
235 * Adds multiple value definitions to the repository
237 * @throws DataTypeSyntaxError
239 public void addValueDefinitions(String def) throws DataTypeSyntaxError {
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);
249 public DataTypeRepository getTypeRepository() {
250 return typeRepository;
253 public void setTypeRepository(DataTypeRepository typeRepository) {
254 this.typeRepository = typeRepository;
257 public BindingScheme getBindingScheme() {
258 return bindingScheme;
261 public void setBindingScheme(BindingScheme bindingScheme) {
262 this.bindingScheme = bindingScheme;
266 * Print the content part of a data value. This excludes the name and type of the value.
269 * @return value or <code>null</code> if value doesn't exist
270 * @throws BindingException
271 * @throws IOException
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);
279 return sb.toString();
283 * Print the whole value repository
286 * @throws IOException
287 * @throws BindingException
289 public void print(StringBuilder sb)
290 throws IOException, BindingException
292 DataValuePrinter vp = new DataValuePrinter(sb, this);
293 vp.setFormat( PrintFormat.SINGLE_LINE );
294 DataTypePrinter tp = new DataTypePrinter( sb );
295 tp.setLinefeed( false );
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+" : " );
310 * Print the whole data value repository as a single multiline string
312 * @throws RuntimeBindingException
313 * @throws {@link RuntimeIOException}
316 public String toString() {
318 StringBuilder sb = new StringBuilder();
320 return sb.toString();
321 } catch (BindingException e) {
322 throw new RuntimeBindingException(e);
323 } catch (IOException e) {
324 throw new RuntimeIOException(e);
329 * Gives a data type to a value heuristically.
331 public Datatype guessDataType(AstValue value) throws DataTypeSyntaxError {
332 return value.accept(guessDataType);
336 * Gives a data type to a value heuristically.
338 public Datatype guessDataType(String value) throws DataTypeSyntaxError {
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);
348 class ValueTranslator implements Binding.Visitor<Object> {
352 public ValueTranslator(AstValue value) {
356 private ValueTranslationRuntimeException typeError(Binding expectedType, AstValue actualValue) {
357 throw new ValueTranslationRuntimeException("Expected " + expectedType.type().toSingleLineString() +
358 " but got " + actualValue.getClass().getSimpleName() + ".");
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();
368 for(AstValue component : array.elements) {
370 components[i++] = componentBinding.accept(this);
372 return b.createUnchecked(components);
374 throw typeError(b, value);
378 public Object visit(BooleanBinding b) {
379 if(value instanceof AstBoolean) {
380 return b.createUnchecked(((AstBoolean)value).value);
382 throw typeError(b, value);
386 public Object visit(DoubleBinding b) {
387 if(value instanceof AstFloat) {
388 return b.createUnchecked(((AstFloat)value).value);
390 if(value instanceof AstInteger) {
391 return b.createUnchecked(((AstInteger)value).value);
393 throw typeError(b, value);
397 public Object visit(FloatBinding b) {
398 if(value instanceof AstFloat) {
399 return b.createUnchecked(((AstFloat)value).value);
401 if(value instanceof AstInteger) {
402 return b.createUnchecked(((AstInteger)value).value);
404 throw typeError(b, value);
408 public Object visit(IntegerBinding b) {
409 if(value instanceof AstInteger) {
410 return b.createUnchecked(((AstInteger)value).value);
412 throw typeError(b, value);
416 public Object visit(ByteBinding b) {
417 if(value instanceof AstInteger) {
418 return b.createUnchecked(((AstInteger)value).value);
420 throw typeError(b, value);
424 public Object visit(LongBinding b) {
425 if(value instanceof AstInteger) {
426 return b.createUnchecked(((AstInteger)value).value);
428 throw typeError(b, value);
432 public Object visit(OptionalBinding b) {
433 if(value == AstNull.NULL)
434 return b.createNoValueUnchecked();
436 return b.createValueUnchecked(b.getComponentBinding().accept(this));
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);
449 throw new ValueTranslationRuntimeException("Invalid record component " + assignment.component + ".");
450 components[index] = b.getComponentBinding(index).accept(this);
451 assigned[index] = true;
453 for(int i=0;i<assigned.length;++i)
455 Binding binding = b.getComponentBinding(i);
456 if(binding instanceof OptionalBinding)
457 components[i] = ((OptionalBinding)binding).createNoValueUnchecked();
459 throw new ValueTranslationRuntimeException("Non-optional field " +
460 b.type().getComponent(i).name + " is not defined.");
462 return b.createUnchecked(components);
464 if(value instanceof AstTuple) {
465 AstTuple tuple = (AstTuple)value;
466 Object[] components = new Object[b.getComponentCount()];
468 for(AstValue element : tuple.elements) {
470 components[i] = b.getComponentBinding(i).accept(this);
473 return b.createUnchecked(components);
475 throw typeError(b, value);
479 public Object visit(StringBinding b) {
480 if(value instanceof AstString) {
481 return b.createUnchecked(((AstString)value).value);
483 throw typeError(b, value);
487 public Object visit(UnionBinding b) {
488 if(value instanceof AstTaggedValue) {
489 AstTaggedValue taggedValue = (AstTaggedValue)value;
490 Integer tagIndex = b.type().getComponentIndex(taggedValue.tag);
492 throw new ValueTranslationRuntimeException("Invalid union tag " + taggedValue.tag + ".");
493 value = taggedValue.value;
494 return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).accept(this));
496 else if(value instanceof AstReference) {
497 AstReference ref = (AstReference)value;
498 Integer tagIndex = b.type().getComponentIndex(ref.name);
500 throw new ValueTranslationRuntimeException("Invalid union tag " + ref.name + ".");
502 return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).createDefault());
503 } catch(BindingException e) {
506 throw typeError(b, value);
510 public Object visit(VariantBinding b) {
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));
520 Datatype dataType = guessDataType(value);
521 Binding binding = bindingScheme.getBinding(dataType);
522 return b.createUnchecked(binding, binding.accept(this));
524 } catch(DataTypeSyntaxError e) {
525 throw new ValueTranslationRuntimeException(e);
526 } catch (BindingConstructionException e) {
527 throw new ValueTranslationRuntimeException(e);
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();
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);
547 return b.createUnchecked(keys, values);
549 throw typeError(b, value);
554 AstValueVisitor<Datatype> guessDataType = new AstValueVisitor<Datatype>() {
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);
564 public Datatype visit(AstBoolean astBoolean) {
565 return Datatypes.BOOLEAN;
569 public Datatype visit(AstFloat astFloat) {
570 return Datatypes.DOUBLE;
574 public Datatype visit(AstInteger astInteger) {
575 return Datatypes.INTEGER;
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));
587 public Datatype visit(AstNull astNull) {
588 throw new ValueTranslationRuntimeException("Cannot guess the data type");
592 public Datatype visit(AstRecord astRecord) {
593 Component[] components = new Component[astRecord.components.size()];
595 for(AstComponentAssignment assignment : astRecord.components) {
596 components[i++] = new Component(
597 assignment.component,
598 assignment.value.accept(this)
601 return new RecordType(false, components);
605 public Datatype visit(AstReference astReference) {
606 MutableVariant v = get(astReference.name);
608 throw new ValueTranslationRuntimeException("Undefined reference to " + astReference.name + ".");
613 public Datatype visit(AstString astString) {
614 return Datatypes.STRING;
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");
624 public Datatype visit(AstTuple astTuple) {
625 Component[] components = new Component[astTuple.elements.size()];
627 for(AstValue value : astTuple.elements) {
628 components[i] = new Component(
634 return new RecordType(false, components);
638 public Datatype visit(AstVariant astVariant) {
639 return Datatypes.VARIANT;