]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
index 0bba95ada2cfde5f657933b07d6e6532fbb9c9cf..9912e9ac6529fb5f8eb33913420f2a27207d4262 100644 (file)
-/*******************************************************************************\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.parser.repository;\r
-\r
-import java.io.IOException;\r
-import java.io.StringReader;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.IdentityHashMap;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.Set;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Datatypes;\r
-import org.simantics.databoard.binding.ArrayBinding;\r
-import org.simantics.databoard.binding.Binding;\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.RuntimeBindingException;\r
-import org.simantics.databoard.binding.factory.BindingScheme;\r
-import org.simantics.databoard.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.file.RuntimeIOException;\r
-import org.simantics.databoard.parser.DataParser;\r
-import org.simantics.databoard.parser.DataValuePrinter;\r
-import org.simantics.databoard.parser.ParseException;\r
-import org.simantics.databoard.parser.PrintFormat;\r
-import org.simantics.databoard.parser.TokenMgrError;\r
-import org.simantics.databoard.parser.ast.value.AstArray;\r
-import org.simantics.databoard.parser.ast.value.AstBoolean;\r
-import org.simantics.databoard.parser.ast.value.AstComponentAssignment;\r
-import org.simantics.databoard.parser.ast.value.AstFloat;\r
-import org.simantics.databoard.parser.ast.value.AstInteger;\r
-import org.simantics.databoard.parser.ast.value.AstMap;\r
-import org.simantics.databoard.parser.ast.value.AstMapAssignment;\r
-import org.simantics.databoard.parser.ast.value.AstNull;\r
-import org.simantics.databoard.parser.ast.value.AstRecord;\r
-import org.simantics.databoard.parser.ast.value.AstReference;\r
-import org.simantics.databoard.parser.ast.value.AstString;\r
-import org.simantics.databoard.parser.ast.value.AstTaggedValue;\r
-import org.simantics.databoard.parser.ast.value.AstTuple;\r
-import org.simantics.databoard.parser.ast.value.AstValue;\r
-import org.simantics.databoard.parser.ast.value.AstValueDefinition;\r
-import org.simantics.databoard.parser.ast.value.AstVariant;\r
-import org.simantics.databoard.parser.ast.value.visitor.AstValueVisitor;\r
-import org.simantics.databoard.parser.unparsing.DataTypePrinter;\r
-import org.simantics.databoard.type.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.MapType;\r
-import org.simantics.databoard.type.RecordType;\r
-import org.simantics.databoard.type.UnionType;\r
-\r
-/**\r
- * Data value repository is a collection of name data values.\r
- * Each value is associated with type. \r
- * \r
- * <p>\r
- * It can also translate data lines and value texts to objects and \r
- * print values as \r
- * \r
- * \r
- * \r
- * @author Hannu Niemist&ouml;\r
- */\r
-public class DataValueRepository {\r
-       \r
-       // Type respository to convert \r
-       DataTypeRepository typeRepository = Datatypes.datatypeRepository;\r
-       \r
-       // Scheme to convert values \r
-       BindingScheme bindingScheme = Bindings.mutableBindingFactory;\r
-       \r
-       /** Stored values */\r
-       Map<String, MutableVariant> values = new HashMap<String, MutableVariant>();\r
-       Map<Object, String> nameMap = new IdentityHashMap<Object, String>();\r
-       \r
-       public MutableVariant get(String name) {\r
-               return values.get(name);\r
-       }\r
-       \r
-       public String getName(Object value) {\r
-               return nameMap.get(value);\r
-       }\r
-\r
-       public void put(String name, Binding binding, Object value) {\r
-               put(name, new MutableVariant(binding, value));\r
-       }\r
-       \r
-       public void put(String name, MutableVariant value) {\r
-               values.put(name, value);\r
-               nameMap.put(value.getValue(), name);\r
-       }\r
-       \r
-       public MutableVariant remove(String name) {\r
-               MutableVariant value = values.remove(name);\r
-               if (value==null) return null;\r
-               nameMap.remove(value.getValue());\r
-               return value;\r
-       }\r
-       \r
-       public void clear() {\r
-               values.clear();\r
-               nameMap.clear();\r
-       }\r
-       \r
-       /**\r
-        * Get a view of the value names in this repository\r
-        * \r
-        * @return names\r
-        */\r
-       public Set<String> getValueNames() {\r
-               return values.keySet();\r
-       }\r
-       \r
-       /**\r
-        * Translates a data value from an abstract syntax tree to an object by the binding.\r
-        * \r
-        * @param value\r
-        * @param binding\r
-        * @return value\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public Object translate(AstValue value, Binding binding) throws DataTypeSyntaxError {           \r
-               try {\r
-                       if(value instanceof AstReference) {\r
-                               String name = ((AstReference)value).name;\r
-                               MutableVariant v = get(name);\r
-                               if(v == null) {\r
-                                       if(binding instanceof UnionBinding) {\r
-                                               UnionBinding b = (UnionBinding)binding;\r
-                                               UnionType type = b.type();\r
-                                               Integer index = type.getComponentIndex(name);\r
-                                               if(index != null)\r
-                                                       try {\r
-                                                               return b.create(index, \r
-                                                                               b.getComponentBinding(index).createDefault());\r
-                                                       } catch(BindingException e) {\r
-                                                               throw new DataTypeSyntaxError(e);\r
-                                                       }\r
-                                       }\r
-                                       throw new DataTypeSyntaxError("Undefined reference to " + name + ".");\r
-                               }\r
-                               return Bindings.adaptUnchecked(v.getValue(), v.getBinding(), binding);\r
-                       }\r
-                       return binding.accept(new ValueTranslator(value));\r
-               } catch(ValueTranslationRuntimeException e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Translates a data value from a string to an object by the binding.\r
-        * @param value\r
-        * @param binding\r
-        * @return value\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public Object translate(String value, Binding binding) throws DataTypeSyntaxError {\r
-               try {\r
-                       return translate(new DataParser(new StringReader(value)).value(), binding);\r
-               } catch (TokenMgrError e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               } catch (ParseException e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Adds a value definition to the repository\r
-        * @param def\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public void addValueDefinition(AstValueDefinition def) throws DataTypeSyntaxError {\r
-               Datatype type = typeRepository.translate(def.type);\r
-               Binding binding = Bindings.getMutableBinding(type);\r
-               MutableVariant variant = new MutableVariant(binding, translate(def.value, binding));\r
-               values.put(def.name, variant);\r
-               nameMap.put(variant.getValue(), def.name);\r
-       }\r
-       \r
-       /**\r
-        * Adds a value definition to the repository\r
-        * @param def\r
-        * @return name\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public String addValueDefinition(String def) throws DataTypeSyntaxError {\r
-               try {\r
-                       StringReader reader = new StringReader(def);\r
-                       DataParser parser = new DataParser( reader );\r
-                       AstValueDefinition valueAstDef = parser.valueDefinition(); \r
-                       addValueDefinition( valueAstDef );\r
-                       return valueAstDef.name;\r
-               } catch (TokenMgrError e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               } catch (ParseException e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Adds multiple value definitions to the repository\r
-        * \r
-        * @param defs\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public void addValueDefinitions(Collection<AstValueDefinition> defs) throws DataTypeSyntaxError {\r
-               // TODO recursive definitions\r
-               for(AstValueDefinition def : defs)\r
-                       addValueDefinition(def);\r
-       }\r
-       \r
-       /**\r
-        * Adds multiple value definitions to the repository\r
-        * @param def\r
-        * @throws DataTypeSyntaxError\r
-        */\r
-       public void addValueDefinitions(String def) throws DataTypeSyntaxError {\r
-               try {\r
-                       addValueDefinitions(new DataParser(new StringReader(def)).valueDefinitions());\r
-               } catch (TokenMgrError e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               } catch (ParseException e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               }\r
-       }\r
-       \r
-       public DataTypeRepository getTypeRepository() {\r
-               return typeRepository;\r
-       }\r
-\r
-       public void setTypeRepository(DataTypeRepository typeRepository) {\r
-               this.typeRepository = typeRepository;\r
-       }\r
-\r
-       public BindingScheme getBindingScheme() {\r
-               return bindingScheme;\r
-       }\r
-\r
-       public void setBindingScheme(BindingScheme bindingScheme) {\r
-               this.bindingScheme = bindingScheme;\r
-       }\r
-\r
-       /**\r
-        * Print the content part of a data value. This excludes the name and type of the value.\r
-        * \r
-        * @param valueName\r
-        * @return value or <code>null</code> if value doesn't exist\r
-        * @throws BindingException \r
-        * @throws IOException \r
-        */\r
-       public String printValue(String valueName) throws IOException, BindingException {\r
-               MutableVariant value = get(valueName);\r
-               if (value==null) return null;\r
-               StringBuilder sb = new StringBuilder();\r
-               DataValuePrinter vp = new DataValuePrinter(sb, this);\r
-               vp.print(value);\r
-               return sb.toString();\r
-       }\r
-       \r
-       /**\r
-        * Print the whole value repository\r
-        * \r
-        * @param sb\r
-        * @throws IOException\r
-        * @throws BindingException\r
-        */\r
-       public void print(StringBuilder sb)\r
-       throws IOException, BindingException\r
-       {\r
-               DataValuePrinter vp = new DataValuePrinter(sb, this);\r
-               vp.setFormat( PrintFormat.SINGLE_LINE );\r
-               DataTypePrinter tp = new DataTypePrinter( sb );\r
-               tp.setLinefeed( false );\r
-                       \r
-               for (Entry<String, MutableVariant> e : values.entrySet()) {\r
-                       String name = e.getKey();\r
-                       MutableVariant value = e.getValue();\r
-                       Datatype type = value.type();\r
-                       sb.append( name+" : " );\r
-                       tp.print(type);\r
-                       sb.append( " = " );\r
-                       vp.print(value);                        \r
-                       sb.append("\n");\r
-               }\r
-       }       \r
-\r
-       /**\r
-        * Print the whole data value repository as a single multiline string\r
-        * \r
-        * @throws RuntimeBindingException\r
-        * @throws {@link RuntimeIOException}\r
-        */\r
-       @Override\r
-       public String toString() {\r
-               try {\r
-                       StringBuilder sb = new StringBuilder();\r
-                       print(sb);\r
-                       return sb.toString();\r
-               } catch (BindingException e) {\r
-                       throw new RuntimeBindingException(e);\r
-               } catch (IOException e) {\r
-                       throw new RuntimeIOException(e);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Gives a data type to a value heuristically.\r
-        */\r
-       public Datatype guessDataType(AstValue value) throws DataTypeSyntaxError {\r
-               return value.accept(guessDataType);\r
-       }\r
-       \r
-       /**\r
-        * Gives a data type to a value heuristically.\r
-        */\r
-       public Datatype guessDataType(String value) throws DataTypeSyntaxError {\r
-               try {\r
-                       return guessDataType(new DataParser(new StringReader(value)).value());\r
-               } catch (TokenMgrError e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               } catch (ParseException e) {\r
-                       throw new DataTypeSyntaxError(e);\r
-               }\r
-       }\r
-\r
-       class ValueTranslator implements Binding.Visitor<Object> {\r
-\r
-               AstValue value;\r
-               \r
-               public ValueTranslator(AstValue value) {\r
-                       this.value = value;\r
-               }\r
-               \r
-               private ValueTranslationRuntimeException typeError(Binding expectedType, AstValue actualValue) {\r
-                       throw new ValueTranslationRuntimeException("Expected " + expectedType.type().toSingleLineString() + \r
-                                       " but got " + actualValue.getClass().getSimpleName() + ".");\r
-               }\r
-\r
-               @Override\r
-               public Object visit(ArrayBinding b) {\r
-                       if(value instanceof AstArray) {\r
-                               AstArray array = (AstArray)value;\r
-                               Object[] components = new Object[array.elements.size()];\r
-                               Binding componentBinding = b.getComponentBinding();\r
-                               int i=0;\r
-                               for(AstValue component : array.elements) {\r
-                                       value = component;\r
-                                       components[i++] = componentBinding.accept(this);\r
-                               }\r
-                               return b.createUnchecked(components);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(BooleanBinding b) {\r
-                       if(value instanceof AstBoolean) {                               \r
-                               return b.createUnchecked(((AstBoolean)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(DoubleBinding b) {\r
-                       if(value instanceof AstFloat) {                         \r
-                               return b.createUnchecked(((AstFloat)value).value);\r
-                       }\r
-                       if(value instanceof AstInteger) {                               \r
-                               return b.createUnchecked(((AstInteger)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(FloatBinding b) {\r
-                       if(value instanceof AstFloat) {                         \r
-                               return b.createUnchecked(((AstFloat)value).value);\r
-                       }\r
-                       if(value instanceof AstInteger) {                               \r
-                               return b.createUnchecked(((AstInteger)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(IntegerBinding b) {\r
-                       if(value instanceof AstInteger) {                               \r
-                               return b.createUnchecked(((AstInteger)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(ByteBinding b) {\r
-                       if(value instanceof AstInteger) {                               \r
-                               return b.createUnchecked(((AstInteger)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(LongBinding b) {\r
-                       if(value instanceof AstInteger) {                               \r
-                               return b.createUnchecked(((AstInteger)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(OptionalBinding b) {\r
-                       if(value == AstNull.NULL)\r
-                               return b.createNoValueUnchecked();\r
-                       else\r
-                               return b.createValueUnchecked(b.getComponentBinding().accept(this));                            \r
-               }\r
-\r
-               @Override\r
-               public Object visit(RecordBinding b) {\r
-                       if(value instanceof AstRecord) {\r
-                               AstRecord record = (AstRecord)value;\r
-                               Object[] components = new Object[b.getComponentCount()];\r
-                               boolean[] assigned = new boolean[b.getComponentCount()];\r
-                               for(AstComponentAssignment assignment : record.components) {\r
-                                       value = assignment.value;\r
-                                       Integer index = b.type().getComponentIndex(assignment.component);\r
-                                       if(index == null)\r
-                                               throw new ValueTranslationRuntimeException("Invalid record component " + assignment.component + ".");\r
-                                       components[index] = b.getComponentBinding(index).accept(this);\r
-                                       assigned[index] = true;\r
-                               }\r
-                               for(int i=0;i<assigned.length;++i)\r
-                                       if(!assigned[i]) {\r
-                                               Binding binding = b.getComponentBinding(i);\r
-                                               if(binding instanceof OptionalBinding)\r
-                                                       components[i] = ((OptionalBinding)binding).createNoValueUnchecked();\r
-                                               else\r
-                                                       throw new ValueTranslationRuntimeException("Non-optional field " + \r
-                                                                       b.type().getComponent(i).name + " is not defined.");\r
-                                       }\r
-                               return b.createUnchecked(components);                           \r
-                       }\r
-                       if(value instanceof AstTuple) {\r
-                               AstTuple tuple = (AstTuple)value;\r
-                               Object[] components = new Object[b.getComponentCount()];\r
-                               int i=0;\r
-                               for(AstValue element : tuple.elements) {\r
-                                       value = element;\r
-                                       components[i] = b.getComponentBinding(i).accept(this);\r
-                                       ++i;\r
-                               }\r
-                               return b.createUnchecked(components);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(StringBinding b) {\r
-                       if(value instanceof AstString) {                                \r
-                               return b.createUnchecked(((AstString)value).value);\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(UnionBinding b) {\r
-                       if(value instanceof AstTaggedValue) {\r
-                               AstTaggedValue taggedValue = (AstTaggedValue)value;\r
-                               Integer tagIndex = b.type().getComponentIndex(taggedValue.tag);\r
-                               if(tagIndex == null)\r
-                                       throw new ValueTranslationRuntimeException("Invalid union tag " + taggedValue.tag + ".");\r
-                               value = taggedValue.value;\r
-                               return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).accept(this));\r
-                       }\r
-                       else if(value instanceof AstReference) {\r
-                           AstReference ref = (AstReference)value;\r
-                           Integer tagIndex = b.type().getComponentIndex(ref.name);\r
-                           if(tagIndex == null)\r
-                    throw new ValueTranslationRuntimeException("Invalid union tag " + ref.name + ".");\r
-                           try {\r
-                               return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).createDefault());\r
-                           } catch(BindingException e) {                               \r
-                           }\r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-\r
-               @Override\r
-               public Object visit(VariantBinding b) {\r
-                       try {\r
-                               if(value instanceof AstVariant) {\r
-                                       AstVariant variant = (AstVariant)value;\r
-                                       Datatype dataType = typeRepository.translate(variant.type);\r
-                                       Binding binding = bindingScheme.getBinding(dataType);\r
-                                       value = variant.value;\r
-                                       return b.createUnchecked(binding, binding.accept(this));\r
-                               }\r
-                               else {\r
-                                       Datatype dataType = guessDataType(value);\r
-                                       Binding binding = bindingScheme.getBinding(dataType);\r
-                                       return b.createUnchecked(binding, binding.accept(this));\r
-                               }\r
-                       } catch(DataTypeSyntaxError e) {\r
-                               throw new ValueTranslationRuntimeException(e);\r
-                       } catch (BindingConstructionException e) {\r
-                               throw new ValueTranslationRuntimeException(e);\r
-                       }\r
-               }\r
-\r
-               @Override\r
-               public Object visit(MapBinding b) {\r
-                       if(value instanceof AstMap) {\r
-                               AstMap map = (AstMap)value;\r
-                               Object[] keys = new Object[map.components.size()];\r
-                               Object[] values = new Object[map.components.size()];\r
-                               Binding keyBinding = b.getKeyBinding();\r
-                               Binding valueBinding = b.getValueBinding();\r
-                               int i = 0;\r
-                               for(AstMapAssignment assignment : map.components) {\r
-                                       value = assignment.key;\r
-                                       keys[i] = keyBinding.accept(this);                                      \r
-                                       value = assignment.value;\r
-                                       values[i] = valueBinding.accept(this);                                  \r
-                                       ++i;\r
-                               }\r
-                               return b.createUnchecked(keys, values);                         \r
-                       }\r
-                       throw typeError(b, value);\r
-               }\r
-               \r
-       }\r
-       \r
-       AstValueVisitor<Datatype> guessDataType = new AstValueVisitor<Datatype>() {\r
-\r
-               @Override\r
-               public Datatype visit(AstArray astArray) {\r
-                       if(astArray.elements.isEmpty())\r
-                               throw new ValueTranslationRuntimeException("Cannot guess the data type of empty array.");\r
-                       return astArray.elements.get(0).accept(this);\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstBoolean astBoolean) {\r
-                       return Datatypes.BOOLEAN;\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstFloat astFloat) {\r
-                       return Datatypes.DOUBLE;\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstInteger astInteger) {\r
-                       return Datatypes.INTEGER;\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstMap astMap) {\r
-                       if(astMap.components.isEmpty())\r
-                               throw new ValueTranslationRuntimeException("Cannot guess the data type of empty map.");\r
-                       AstMapAssignment assignment = astMap.components.get(0);\r
-                       return new MapType(assignment.key.accept(this), assignment.value.accept(this));\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstNull astNull) {\r
-                       throw new ValueTranslationRuntimeException("Cannot guess the data type");\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstRecord astRecord) {\r
-                       Component[] components = new Component[astRecord.components.size()];\r
-                       int i = 0;\r
-                       for(AstComponentAssignment assignment : astRecord.components) {\r
-                               components[i++] = new Component(\r
-                                               assignment.component,\r
-                                               assignment.value.accept(this)\r
-                                               );\r
-                       }\r
-                       return new RecordType(false, components);\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstReference astReference) {\r
-                       MutableVariant v = get(astReference.name);\r
-                       if(v == null)\r
-                               throw new ValueTranslationRuntimeException("Undefined reference to " + astReference.name + ".");\r
-                       return v.type();\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstString astString) {\r
-                       return Datatypes.STRING;\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstTaggedValue astTaggedValue) {\r
-                       // Guessed datatype would be a union with just one component. Not very useful.\r
-                       throw new ValueTranslationRuntimeException("Cannot guess the data type of tagged value");\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstTuple astTuple) {\r
-                       Component[] components = new Component[astTuple.elements.size()];\r
-                       int i = 0;\r
-                       for(AstValue value : astTuple.elements) {\r
-                               components[i] = new Component(\r
-                                               Integer.toString(i),\r
-                                               value.accept(this)\r
-                                               );\r
-                               ++i;\r
-                       }\r
-                       return new RecordType(false, components);\r
-               }\r
-\r
-               @Override\r
-               public Datatype visit(AstVariant astVariant) {\r
-                       return Datatypes.VARIANT;\r
-               }\r
-       };\r
-       \r
-       \r
-}\r
+/*******************************************************************************
+ *  Copyright (c) 2010 Association for Decentralized Information Management in
+ *  Industry THTH ry.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *      VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.databoard.parser.repository;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Datatypes;
+import org.simantics.databoard.binding.ArrayBinding;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.BooleanBinding;
+import org.simantics.databoard.binding.ByteBinding;
+import org.simantics.databoard.binding.DoubleBinding;
+import org.simantics.databoard.binding.FloatBinding;
+import org.simantics.databoard.binding.IntegerBinding;
+import org.simantics.databoard.binding.LongBinding;
+import org.simantics.databoard.binding.MapBinding;
+import org.simantics.databoard.binding.OptionalBinding;
+import org.simantics.databoard.binding.RecordBinding;
+import org.simantics.databoard.binding.StringBinding;
+import org.simantics.databoard.binding.UnionBinding;
+import org.simantics.databoard.binding.VariantBinding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingException;
+import org.simantics.databoard.binding.factory.BindingScheme;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.file.RuntimeIOException;
+import org.simantics.databoard.parser.DataParser;
+import org.simantics.databoard.parser.DataValuePrinter;
+import org.simantics.databoard.parser.ParseException;
+import org.simantics.databoard.parser.PrintFormat;
+import org.simantics.databoard.parser.TokenMgrError;
+import org.simantics.databoard.parser.ast.value.AstArray;
+import org.simantics.databoard.parser.ast.value.AstBoolean;
+import org.simantics.databoard.parser.ast.value.AstComponentAssignment;
+import org.simantics.databoard.parser.ast.value.AstFloat;
+import org.simantics.databoard.parser.ast.value.AstInteger;
+import org.simantics.databoard.parser.ast.value.AstMap;
+import org.simantics.databoard.parser.ast.value.AstMapAssignment;
+import org.simantics.databoard.parser.ast.value.AstNull;
+import org.simantics.databoard.parser.ast.value.AstRecord;
+import org.simantics.databoard.parser.ast.value.AstReference;
+import org.simantics.databoard.parser.ast.value.AstString;
+import org.simantics.databoard.parser.ast.value.AstTaggedValue;
+import org.simantics.databoard.parser.ast.value.AstTuple;
+import org.simantics.databoard.parser.ast.value.AstValue;
+import org.simantics.databoard.parser.ast.value.AstValueDefinition;
+import org.simantics.databoard.parser.ast.value.AstVariant;
+import org.simantics.databoard.parser.ast.value.visitor.AstValueVisitor;
+import org.simantics.databoard.parser.unparsing.DataTypePrinter;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.MapType;
+import org.simantics.databoard.type.RecordType;
+import org.simantics.databoard.type.UnionType;
+
+/**
+ * Data value repository is a collection of name data values.
+ * Each value is associated with type. 
+ * 
+ * <p>
+ * It can also translate data lines and value texts to objects and 
+ * print values as 
+ * 
+ * 
+ * 
+ * @author Hannu Niemist&ouml;
+ */
+public class DataValueRepository {
+       
+       // Type respository to convert 
+       DataTypeRepository typeRepository = Datatypes.datatypeRepository;
+       
+       // Scheme to convert values 
+       BindingScheme bindingScheme = Bindings.mutableBindingFactory;
+       
+       /** Stored values */
+       Map<String, MutableVariant> values = new HashMap<String, MutableVariant>();
+       Map<Object, String> nameMap = new IdentityHashMap<Object, String>();
+       
+       public MutableVariant get(String name) {
+               return values.get(name);
+       }
+       
+       public String getName(Object value) {
+               return nameMap.get(value);
+       }
+
+       public void put(String name, Binding binding, Object value) {
+               put(name, new MutableVariant(binding, value));
+       }
+       
+       public void put(String name, MutableVariant value) {
+               values.put(name, value);
+               nameMap.put(value.getValue(), name);
+       }
+       
+       public MutableVariant remove(String name) {
+               MutableVariant value = values.remove(name);
+               if (value==null) return null;
+               nameMap.remove(value.getValue());
+               return value;
+       }
+       
+       public void clear() {
+               values.clear();
+               nameMap.clear();
+       }
+       
+       /**
+        * Get a view of the value names in this repository
+        * 
+        * @return names
+        */
+       public Set<String> getValueNames() {
+               return values.keySet();
+       }
+       
+       /**
+        * Translates a data value from an abstract syntax tree to an object by the binding.
+        * 
+        * @param value
+        * @param binding
+        * @return value
+        * @throws DataTypeSyntaxError
+        */
+       public Object translate(AstValue value, Binding binding) throws DataTypeSyntaxError {           
+               try {
+                       if(value instanceof AstReference) {
+                               String name = ((AstReference)value).name;
+                               MutableVariant v = get(name);
+                               if(v == null) {
+                                       if(binding instanceof UnionBinding) {
+                                               UnionBinding b = (UnionBinding)binding;
+                                               UnionType type = b.type();
+                                               Integer index = type.getComponentIndex(name);
+                                               if(index != null)
+                                                       try {
+                                                               return b.create(index, 
+                                                                               b.getComponentBinding(index).createDefault());
+                                                       } catch(BindingException e) {
+                                                               throw new DataTypeSyntaxError(e);
+                                                       }
+                                       }
+                                       throw new DataTypeSyntaxError("Undefined reference to " + name + ".");
+                               }
+                               return Bindings.adaptUnchecked(v.getValue(), v.getBinding(), binding);
+                       }
+                       return binding.accept(new ValueTranslator(value));
+               } catch(ValueTranslationRuntimeException e) {
+                       throw new DataTypeSyntaxError(e);
+               }
+       }
+       
+       /**
+        * Translates a data value from a string to an object by the binding.
+        * @param value
+        * @param binding
+        * @return value
+        * @throws DataTypeSyntaxError
+        */
+       public Object translate(String value, Binding binding) throws DataTypeSyntaxError {
+               try {
+                       return translate(new DataParser(new StringReader(value)).value(), binding);
+               } catch (TokenMgrError e) {
+                       throw new DataTypeSyntaxError(e);
+               } catch (ParseException e) {
+                       throw new DataTypeSyntaxError(e);
+               }
+       }
+       
+       /**
+        * Adds a value definition to the repository
+        * @param def
+        * @throws DataTypeSyntaxError
+        */
+       public void addValueDefinition(AstValueDefinition def) throws DataTypeSyntaxError {
+               Datatype type = typeRepository.translate(def.type);
+               Binding binding = Bindings.getMutableBinding(type);
+               MutableVariant variant = new MutableVariant(binding, translate(def.value, binding));
+               values.put(def.name, variant);
+               nameMap.put(variant.getValue(), def.name);
+       }
+       
+       /**
+        * Adds a value definition to the repository
+        * @param def
+        * @return name
+        * @throws DataTypeSyntaxError
+        */
+       public String addValueDefinition(String def) throws DataTypeSyntaxError {
+               try {
+                       StringReader reader = new StringReader(def);
+                       DataParser parser = new DataParser( reader );
+                       AstValueDefinition valueAstDef = parser.valueDefinition(); 
+                       addValueDefinition( valueAstDef );
+                       return valueAstDef.name;
+               } catch (TokenMgrError e) {
+                       throw new DataTypeSyntaxError(e);
+               } catch (ParseException e) {
+                       throw new DataTypeSyntaxError(e);
+               }
+       }
+       
+       /**
+        * Adds multiple value definitions to the repository
+        * 
+        * @param defs
+        * @throws DataTypeSyntaxError
+        */
+       public void addValueDefinitions(Collection<AstValueDefinition> defs) throws DataTypeSyntaxError {
+               // TODO recursive definitions
+               for(AstValueDefinition def : defs)
+                       addValueDefinition(def);
+       }
+       
+       /**
+        * Adds multiple value definitions to the repository
+        * @param def
+        * @throws DataTypeSyntaxError
+        */
+       public void addValueDefinitions(String def) throws DataTypeSyntaxError {
+               try {
+                       addValueDefinitions(new DataParser(new StringReader(def)).valueDefinitions());
+               } catch (TokenMgrError e) {
+                       throw new DataTypeSyntaxError(e);
+               } catch (ParseException e) {
+                       throw new DataTypeSyntaxError(e);
+               }
+       }
+       
+       public DataTypeRepository getTypeRepository() {
+               return typeRepository;
+       }
+
+       public void setTypeRepository(DataTypeRepository typeRepository) {
+               this.typeRepository = typeRepository;
+       }
+
+       public BindingScheme getBindingScheme() {
+               return bindingScheme;
+       }
+
+       public void setBindingScheme(BindingScheme bindingScheme) {
+               this.bindingScheme = bindingScheme;
+       }
+
+       /**
+        * Print the content part of a data value. This excludes the name and type of the value.
+        * 
+        * @param valueName
+        * @return value or <code>null</code> if value doesn't exist
+        * @throws BindingException 
+        * @throws IOException 
+        */
+       public String printValue(String valueName) throws IOException, BindingException {
+               MutableVariant value = get(valueName);
+               if (value==null) return null;
+               StringBuilder sb = new StringBuilder();
+               DataValuePrinter vp = new DataValuePrinter(sb, this);
+               vp.print(value);
+               return sb.toString();
+       }
+       
+       /**
+        * Print the whole value repository
+        * 
+        * @param sb
+        * @throws IOException
+        * @throws BindingException
+        */
+       public void print(StringBuilder sb)
+       throws IOException, BindingException
+       {
+               DataValuePrinter vp = new DataValuePrinter(sb, this);
+               vp.setFormat( PrintFormat.SINGLE_LINE );
+               DataTypePrinter tp = new DataTypePrinter( sb );
+               tp.setLinefeed( false );
+                       
+               for (Entry<String, MutableVariant> e : values.entrySet()) {
+                       String name = e.getKey();
+                       MutableVariant value = e.getValue();
+                       Datatype type = value.type();
+                       sb.append( name+" : " );
+                       tp.print(type);
+                       sb.append( " = " );
+                       vp.print(value);                        
+                       sb.append("\n");
+               }
+       }       
+
+       /**
+        * Print the whole data value repository as a single multiline string
+        * 
+        * @throws RuntimeBindingException
+        * @throws {@link RuntimeIOException}
+        */
+       @Override
+       public String toString() {
+               try {
+                       StringBuilder sb = new StringBuilder();
+                       print(sb);
+                       return sb.toString();
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       }
+       
+       /**
+        * Gives a data type to a value heuristically.
+        */
+       public Datatype guessDataType(AstValue value) throws DataTypeSyntaxError {
+               return value.accept(guessDataType);
+       }
+       
+       /**
+        * Gives a data type to a value heuristically.
+        */
+       public Datatype guessDataType(String value) throws DataTypeSyntaxError {
+               try {
+                       return guessDataType(new DataParser(new StringReader(value)).value());
+               } catch (TokenMgrError e) {
+                       throw new DataTypeSyntaxError(e);
+               } catch (ParseException e) {
+                       throw new DataTypeSyntaxError(e);
+               }
+       }
+
+       class ValueTranslator implements Binding.Visitor<Object> {
+
+               AstValue value;
+               
+               public ValueTranslator(AstValue value) {
+                       this.value = value;
+               }
+               
+               private ValueTranslationRuntimeException typeError(Binding expectedType, AstValue actualValue) {
+                       throw new ValueTranslationRuntimeException("Expected " + expectedType.type().toSingleLineString() + 
+                                       " but got " + actualValue.getClass().getSimpleName() + ".");
+               }
+
+               @Override
+               public Object visit(ArrayBinding b) {
+                       if(value instanceof AstArray) {
+                               AstArray array = (AstArray)value;
+                               Object[] components = new Object[array.elements.size()];
+                               Binding componentBinding = b.getComponentBinding();
+                               int i=0;
+                               for(AstValue component : array.elements) {
+                                       value = component;
+                                       components[i++] = componentBinding.accept(this);
+                               }
+                               return b.createUnchecked(components);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(BooleanBinding b) {
+                       if(value instanceof AstBoolean) {                               
+                               return b.createUnchecked(((AstBoolean)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(DoubleBinding b) {
+                       if(value instanceof AstFloat) {                         
+                               return b.createUnchecked(((AstFloat)value).value);
+                       }
+                       if(value instanceof AstInteger) {                               
+                               return b.createUnchecked(((AstInteger)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(FloatBinding b) {
+                       if(value instanceof AstFloat) {                         
+                               return b.createUnchecked(((AstFloat)value).value);
+                       }
+                       if(value instanceof AstInteger) {                               
+                               return b.createUnchecked(((AstInteger)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(IntegerBinding b) {
+                       if(value instanceof AstInteger) {                               
+                               return b.createUnchecked(((AstInteger)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(ByteBinding b) {
+                       if(value instanceof AstInteger) {                               
+                               return b.createUnchecked(((AstInteger)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(LongBinding b) {
+                       if(value instanceof AstInteger) {                               
+                               return b.createUnchecked(((AstInteger)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(OptionalBinding b) {
+                       if(value == AstNull.NULL)
+                               return b.createNoValueUnchecked();
+                       else
+                               return b.createValueUnchecked(b.getComponentBinding().accept(this));                            
+               }
+
+               @Override
+               public Object visit(RecordBinding b) {
+                       if(value instanceof AstRecord) {
+                               AstRecord record = (AstRecord)value;
+                               Object[] components = new Object[b.getComponentCount()];
+                               boolean[] assigned = new boolean[b.getComponentCount()];
+                               for(AstComponentAssignment assignment : record.components) {
+                                       value = assignment.value;
+                                       Integer index = b.type().getComponentIndex(assignment.component);
+                                       if(index == null)
+                                               throw new ValueTranslationRuntimeException("Invalid record component " + assignment.component + ".");
+                                       components[index] = b.getComponentBinding(index).accept(this);
+                                       assigned[index] = true;
+                               }
+                               for(int i=0;i<assigned.length;++i)
+                                       if(!assigned[i]) {
+                                               Binding binding = b.getComponentBinding(i);
+                                               if(binding instanceof OptionalBinding)
+                                                       components[i] = ((OptionalBinding)binding).createNoValueUnchecked();
+                                               else
+                                                       throw new ValueTranslationRuntimeException("Non-optional field " + 
+                                                                       b.type().getComponent(i).name + " is not defined.");
+                                       }
+                               return b.createUnchecked(components);                           
+                       }
+                       if(value instanceof AstTuple) {
+                               AstTuple tuple = (AstTuple)value;
+                               Object[] components = new Object[b.getComponentCount()];
+                               int i=0;
+                               for(AstValue element : tuple.elements) {
+                                       value = element;
+                                       components[i] = b.getComponentBinding(i).accept(this);
+                                       ++i;
+                               }
+                               return b.createUnchecked(components);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(StringBinding b) {
+                       if(value instanceof AstString) {                                
+                               return b.createUnchecked(((AstString)value).value);
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(UnionBinding b) {
+                       if(value instanceof AstTaggedValue) {
+                               AstTaggedValue taggedValue = (AstTaggedValue)value;
+                               Integer tagIndex = b.type().getComponentIndex(taggedValue.tag);
+                               if(tagIndex == null)
+                                       throw new ValueTranslationRuntimeException("Invalid union tag " + taggedValue.tag + ".");
+                               value = taggedValue.value;
+                               return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).accept(this));
+                       }
+                       else if(value instanceof AstReference) {
+                           AstReference ref = (AstReference)value;
+                           Integer tagIndex = b.type().getComponentIndex(ref.name);
+                           if(tagIndex == null)
+                    throw new ValueTranslationRuntimeException("Invalid union tag " + ref.name + ".");
+                           try {
+                               return b.createUnchecked(tagIndex, b.getComponentBinding(tagIndex).createDefault());
+                           } catch(BindingException e) {                               
+                           }
+                       }
+                       throw typeError(b, value);
+               }
+
+               @Override
+               public Object visit(VariantBinding b) {
+                       try {
+                               if(value instanceof AstVariant) {
+                                       AstVariant variant = (AstVariant)value;
+                                       Datatype dataType = typeRepository.translate(variant.type);
+                                       Binding binding = bindingScheme.getBinding(dataType);
+                                       value = variant.value;
+                                       return b.createUnchecked(binding, binding.accept(this));
+                               }
+                               else {
+                                       Datatype dataType = guessDataType(value);
+                                       Binding binding = bindingScheme.getBinding(dataType);
+                                       return b.createUnchecked(binding, binding.accept(this));
+                               }
+                       } catch(DataTypeSyntaxError e) {
+                               throw new ValueTranslationRuntimeException(e);
+                       } catch (BindingConstructionException e) {
+                               throw new ValueTranslationRuntimeException(e);
+                       }
+               }
+
+               @Override
+               public Object visit(MapBinding b) {
+                       if(value instanceof AstMap) {
+                               AstMap map = (AstMap)value;
+                               Object[] keys = new Object[map.components.size()];
+                               Object[] values = new Object[map.components.size()];
+                               Binding keyBinding = b.getKeyBinding();
+                               Binding valueBinding = b.getValueBinding();
+                               int i = 0;
+                               for(AstMapAssignment assignment : map.components) {
+                                       value = assignment.key;
+                                       keys[i] = keyBinding.accept(this);                                      
+                                       value = assignment.value;
+                                       values[i] = valueBinding.accept(this);                                  
+                                       ++i;
+                               }
+                               return b.createUnchecked(keys, values);                         
+                       }
+                       throw typeError(b, value);
+               }
+               
+       }
+       
+       AstValueVisitor<Datatype> guessDataType = new AstValueVisitor<Datatype>() {
+
+               @Override
+               public Datatype visit(AstArray astArray) {
+                       if(astArray.elements.isEmpty())
+                               throw new ValueTranslationRuntimeException("Cannot guess the data type of empty array.");
+                       return astArray.elements.get(0).accept(this);
+               }
+
+               @Override
+               public Datatype visit(AstBoolean astBoolean) {
+                       return Datatypes.BOOLEAN;
+               }
+
+               @Override
+               public Datatype visit(AstFloat astFloat) {
+                       return Datatypes.DOUBLE;
+               }
+
+               @Override
+               public Datatype visit(AstInteger astInteger) {
+                       return Datatypes.INTEGER;
+               }
+
+               @Override
+               public Datatype visit(AstMap astMap) {
+                       if(astMap.components.isEmpty())
+                               throw new ValueTranslationRuntimeException("Cannot guess the data type of empty map.");
+                       AstMapAssignment assignment = astMap.components.get(0);
+                       return new MapType(assignment.key.accept(this), assignment.value.accept(this));
+               }
+
+               @Override
+               public Datatype visit(AstNull astNull) {
+                       throw new ValueTranslationRuntimeException("Cannot guess the data type");
+               }
+
+               @Override
+               public Datatype visit(AstRecord astRecord) {
+                       Component[] components = new Component[astRecord.components.size()];
+                       int i = 0;
+                       for(AstComponentAssignment assignment : astRecord.components) {
+                               components[i++] = new Component(
+                                               assignment.component,
+                                               assignment.value.accept(this)
+                                               );
+                       }
+                       return new RecordType(false, components);
+               }
+
+               @Override
+               public Datatype visit(AstReference astReference) {
+                       MutableVariant v = get(astReference.name);
+                       if(v == null)
+                               throw new ValueTranslationRuntimeException("Undefined reference to " + astReference.name + ".");
+                       return v.type();
+               }
+
+               @Override
+               public Datatype visit(AstString astString) {
+                       return Datatypes.STRING;
+               }
+
+               @Override
+               public Datatype visit(AstTaggedValue astTaggedValue) {
+                       // Guessed datatype would be a union with just one component. Not very useful.
+                       throw new ValueTranslationRuntimeException("Cannot guess the data type of tagged value");
+               }
+
+               @Override
+               public Datatype visit(AstTuple astTuple) {
+                       Component[] components = new Component[astTuple.elements.size()];
+                       int i = 0;
+                       for(AstValue value : astTuple.elements) {
+                               components[i] = new Component(
+                                               Integer.toString(i),
+                                               value.accept(this)
+                                               );
+                               ++i;
+                       }
+                       return new RecordType(false, components);
+               }
+
+               @Override
+               public Datatype visit(AstVariant astVariant) {
+                       return Datatypes.VARIANT;
+               }
+       };
+       
+       
+}