--- /dev/null
+/*******************************************************************************\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;
+
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.StringReader;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.parser.DataParser;\r
+import org.simantics.databoard.parser.ParseException;\r
+import org.simantics.databoard.parser.ast.type.AstArrayType;\r
+import org.simantics.databoard.parser.ast.type.AstAttribute;\r
+import org.simantics.databoard.parser.ast.type.AstComponent;\r
+import org.simantics.databoard.parser.ast.type.AstRecordType;\r
+import org.simantics.databoard.parser.ast.type.AstTupleType;\r
+import org.simantics.databoard.parser.ast.type.AstType;\r
+import org.simantics.databoard.parser.ast.type.AstTypeDefinition;\r
+import org.simantics.databoard.parser.ast.type.AstTypeReference;\r
+import org.simantics.databoard.parser.ast.type.AstUnionType;\r
+import org.simantics.databoard.parser.unparsing.DataTypePrinter;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.Component;\r
+import org.simantics.databoard.type.DataTypeDefinition;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.DoubleType;\r
+import org.simantics.databoard.type.FloatType;\r
+import org.simantics.databoard.type.IntegerType;\r
+import org.simantics.databoard.type.LongType;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.util.Limit;\r
+import org.simantics.databoard.util.Range;\r
+
+/**
+ * Type repository maintains a mapping from strings to
+ * data types. It can also convert abstract syntax trees
+ * to data types.
+ *
+ * @author Hannu Niemistö
+ */
+public class DataTypeRepository {
+
+ Map<String, Datatype> dataTypes = new TreeMap<String, Datatype>();
+ Map<String, Datatype> dataTypesConstruction = new HashMap<String, Datatype>();
+ Map<String, AstType> untranslatedTypes = new TreeMap<String, AstType>();
+ Map<Datatype, String> typeNames = new HashMap<Datatype, String>();
+
+ /**
+ * Adds a type to the repository.
+ *
+ * @param name Name of the type
+ * @param type Type to be added
+ */
+ public void add(String name, Datatype type) {\r
+ //System.out.println("add(" + name + ", " + type.toSingleLineString() + ")");
+ //Datatype oldType = dataTypes.get(name);
+ //String oldName = typeNames.get(type);
+ /*if (oldType!=null && !oldType.equals(type)) throw new RuntimeException("name = "+type+" is already mapped in the repository to "+oldType);
+ if (oldName!=null && !oldName.equals(name)) throw new RuntimeException("name = "+type+" is already mapped in the repository to "+oldName);
+ */dataTypes.put(name, type);
+ typeNames.put(type, name);
+ }
+
+ void addTemp(String name, Datatype type) {
+ Datatype oldType = dataTypesConstruction.get(name);
+ if (oldType!=null && oldType!=type) throw new RuntimeException("name = "+type+" is already mapped in the repository to "+oldType);
+ dataTypesConstruction.put(name, type);
+ }
+
+ void finishType(String name) {
+ Datatype type = dataTypesConstruction.remove(name);
+ if (type==null) throw new RuntimeException("X");
+ dataTypes.put(name, type);
+ typeNames.put(type, name);
+ }
+
+
+ /**
+ * Gets a data type in the repository.
+ *
+ * @param name
+ * @return the data type
+ */
+ public Datatype get(String name) {
+ Datatype res1 = dataTypesConstruction.get(name);
+ if (res1!=null) return res1;
+ Datatype res2 = dataTypes.get(name);
+ return res2;
+ }
+
+ public String get(Datatype type) {
+ return typeNames.get(type);
+ }
+
+ public boolean contains(String name) {
+ return dataTypes.containsKey(name);
+ }
+
+ public boolean contains(Datatype type) {
+ return typeNames.containsKey(type);
+ }
+
+
+ /**
+ * @return a view of all data types defined in this repository.
+ */
+ public Set<String> getTypeNames() {
+ return dataTypes.keySet();
+ }
+
+ /**
+ * Add a type definitions to the repository.
+ *
+ * @param defs type definitions
+ */
+ public void addDefinitions(DataTypeDefinition...defs) {
+ for (DataTypeDefinition def : defs) {
+ add(def.getName(), def.getDataType());
+ }
+ }
+
+ /**
+ * Add a type definition to the repository.
+ *
+ * @param def type definition
+ */
+ public void addDefinition(DataTypeDefinition def) {
+ add(def.getName(), def.getDataType());
+ }
+
+
+ /**
+ * Adds a type to the repository.
+ *
+ * @param name Name of the type
+ * @param ast Abstract syntax tree of the type to be added
+ * @return Translated data type
+ * @throws DataTypeSyntaxError
+ */
+ public Datatype add(String name, AstType ast) throws DataTypeSyntaxError {
+ if (name!=null) {
+ Datatype t = get(name);
+ if (t!=null) return t;
+ }
+
+ if(ast instanceof AstTypeReference) {
+ AstTypeReference named = (AstTypeReference)ast;
+ Datatype type = null;
+ if (dataTypesConstruction.containsKey(named.name))
+ return dataTypesConstruction.get(named.name);
+ if (dataTypes.containsKey(named.name))
+ return dataTypes.get(named.name);
+
+ if(untranslatedTypes.containsKey(named.name))
+ type = add(named.name, untranslatedTypes.remove(named.name)); //?
+ else
+ type = translateBuiltin(named);
+ if(name != null)
+ add(name, type);
+ return type;
+ }
+ else if(ast instanceof AstArrayType) {
+ ArrayType type = new ArrayType();
+ if(name != null) addTemp(name, type);
+ translate((AstArrayType)ast, type);
+ if(name != null) finishType(name);
+ return type;
+ }
+ else if(ast instanceof AstRecordType) {
+ RecordType type = new RecordType();
+ if(name != null) addTemp(name, type);
+ translate((AstRecordType)ast, type);
+ if(name != null) finishType(name);
+ return type;
+ }
+ else if(ast instanceof AstTupleType) {
+ RecordType type = new RecordType();
+ if(name != null) addTemp(name, type);
+ translate((AstTupleType)ast, type);
+ if(name != null) finishType(name);
+ return type;
+ }
+ else if(ast instanceof AstUnionType) {
+ UnionType type = new UnionType();
+ if(name != null) addTemp(name, type);
+ translate((AstUnionType)ast, type);
+ if(name != null) finishType(name);
+ return type;
+ }
+ else
+ throw new AssertionError("Not all syntax tree nodes covered");
+ }
+
+ private Datatype translateBuiltin(AstTypeReference named)
+ throws DataTypeSyntaxError {
+ try {
+ if(named.name.equals("Boolean")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(!named.attributes.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not have attributes.");
+ return Datatypes.BOOLEAN;
+ }
+ else if(named.name.equals("Byte")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(!named.attributes.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not have attributes.");
+ return Datatypes.BYTE;
+ }
+ else if(named.name.equals("Integer")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(named.attributes.isEmpty())
+ return Datatypes.INTEGER;
+ else {
+ IntegerType type = new IntegerType();
+ for(AstAttribute attribute : named.attributes) {
+ String key = attribute.key;
+ if(key.equals("range"))
+ type.setRange( attribute.value );
+ else if(key.equals("unit"))
+ type.setUnit( attribute.value );
+ else
+ throw new DataTypeSyntaxError(named.name + " does not have attribute " + attribute.key + ".");
+ }
+ return type;
+ }
+ }
+ else if(named.name.equals("Long")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(named.attributes.isEmpty())
+ return Datatypes.LONG;
+ else {
+ LongType type = new LongType();
+ for(AstAttribute attribute : named.attributes) {
+ String key = attribute.key;
+ if(key.equals("range"))
+ type.setRange( attribute.value );
+ else if(key.equals("unit"))
+ type.setUnit( attribute.value );
+ else
+ throw new DataTypeSyntaxError(named.name + " does not have attribute " + attribute.key + ".");
+ }
+ return type;
+ }
+ }
+ else if(named.name.equals("Float")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(named.attributes.isEmpty())
+ return Datatypes.FLOAT;
+ else {
+ FloatType type = new FloatType();
+ for(AstAttribute attribute : named.attributes) {
+ String key = attribute.key;
+ if(key.equals("range"))
+ type.setRange( attribute.value );
+ else if(key.equals("unit"))
+ type.setUnit( attribute.value );
+ else
+ throw new DataTypeSyntaxError(named.name + " does not have attribute " + attribute.key + ".");
+ }
+ return type;
+ }
+ }
+ else if(named.name.equals("Double")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(named.attributes.isEmpty())
+ return Datatypes.DOUBLE;
+ else {
+ DoubleType type = new DoubleType();
+ for(AstAttribute attribute : named.attributes) {
+ String key = attribute.key;
+ if(key.equals("range"))
+ type.setRange( attribute.value );
+ else if(key.equals("unit"))
+ type.setUnit( attribute.value );
+ else
+ throw new DataTypeSyntaxError(named.name + " does not have attribute " + attribute.key + ".");
+ }
+ return type;
+ }
+ }
+ else if(named.name.equals("String")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ if(named.attributes.isEmpty())
+ return Datatypes.STRING;
+ else {
+ StringType type = new StringType();
+ for(AstAttribute attribute : named.attributes) {
+ String key = attribute.key;
+ if(key.equals("mimeType"))
+ type.setMimeType( attribute.value );
+ else if(key.equals("pattern"))
+ type.setPattern( attribute.value );
+ else if(key.equals("length"))
+ type.setLength( attribute.value );
+ else
+ throw new DataTypeSyntaxError(named.name + " does not have attribute " + attribute.key + ".");
+ }
+ return type;
+ }
+ }
+ else if(named.name.equals("Optional")) {
+ if(named.parameters.size() != 1)
+ throw new DataTypeSyntaxError(
+ "Optional takes one type parameter not " + named.parameters.size() + ".");
+ if(!named.attributes.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not have attributes.");
+ OptionalType type = new OptionalType();
+ type.componentType = translate(named.parameters.get(0));
+ return type;
+ }
+ else if(named.name.equals("Variant")) {
+ if(!named.parameters.isEmpty())
+ throw new DataTypeSyntaxError(named.name + " does not take type parameters.");
+ return Datatypes.VARIANT;
+ }
+ else if(named.name.equals("Map")) {
+ if(named.parameters.size() != 2)
+ throw new DataTypeSyntaxError(
+ "Map takes two type parameters not " + named.parameters.size() + ".");
+ return new MapType(
+ translate(named.parameters.get(0)),
+ translate(named.parameters.get(1))
+ );
+ }
+ else
+ throw new DataTypeSyntaxError("Undefined type " + named.name);
+ }
+ catch(IllegalArgumentException e) {
+ throw new DataTypeSyntaxError(e);
+ }
+ }
+
+ private void translate(AstArrayType ast, ArrayType type) throws DataTypeSyntaxError {
+ type.componentType = translate(ast.componentType);
+ if(ast.minLength == null) {
+ if(ast.maxLength == null) {
+ type.setLength( (String) null );
+ } else {
+ type.setLength( new Range(
+ Limit.nolimit(),
+ Limit.inclusive(ast.maxLength)
+ ));
+ }
+ } else {
+ if(ast.maxLength == null) {
+ type.setLength( new Range(
+ Limit.inclusive(ast.minLength),
+ Limit.nolimit()
+ ));
+ } else {
+ type.setLength( new Range(
+ Limit.inclusive(ast.minLength),
+ Limit.inclusive(ast.maxLength)
+ ));
+ }
+ }
+ }
+
+ private void translate(AstRecordType ast, RecordType type) throws DataTypeSyntaxError {
+ Component[] components = new Component[ast.components.size()];
+ for(int i=0;i<ast.components.size();++i) {
+ AstComponent astComponent = ast.components.get(i);
+ components[i] = new Component(
+ astComponent.name,
+ translate(astComponent.type)
+ );
+ } \r
+ type.setReferable( ast.referable );
+ type.setComponents( components );
+ }
+
+ private void translate(AstTupleType ast, RecordType type) throws DataTypeSyntaxError {
+ Component[] components = new Component[ast.components.size()];
+ components = new Component[ast.components.size()];
+ for(int i=0;i<ast.components.size();++i) {
+ components[i] = new Component(
+ Integer.toString(i),
+ translate(ast.components.get(i))
+ );
+ }
+ type.setComponents( components );
+ }
+
+ private void translate(AstUnionType ast, UnionType type) throws DataTypeSyntaxError {
+ type.components = new Component[ast.components.size()];
+ for(int i=0;i<ast.components.size();++i) {
+ AstComponent astComponent = ast.components.get(i);
+ type.components[i] = new Component(
+ astComponent.name,
+ add(astComponent.name, astComponent.type) //translate(astComponent.type)
+ );
+ }
+ }
+
+ /**
+ * Adds all type definitions to the repository.
+ * @param definitions Abstract syntax trees of the definitions
+ * @throws DataTypeSyntaxError
+ */
+ public void add(List<AstTypeDefinition> definitions) throws DataTypeSyntaxError {
+ for(AstTypeDefinition def : definitions)
+ untranslatedTypes.put(def.name, def.type);
+ for(AstTypeDefinition def : definitions)
+ if(untranslatedTypes.containsKey(def.name))
+ add(def.name, untranslatedTypes.remove(def.name));
+ }
+
+ /**
+ * Parses and adds type definitions to the repository.
+ *
+ * @param definitions Definitions in textual format.
+ */
+ public void addDefinitions(String definitions) throws DataTypeSyntaxError {
+ try {
+ List<AstTypeDefinition> typeDefinitions =
+ new DataParser(new StringReader(definitions)).typeDefinitions();
+ add(typeDefinitions);
+ } catch (ParseException e) {
+ throw new DataTypeSyntaxError(e);
+ }
+ }
+
+ public void addDefinitions(InputStream definitions) throws IOException, DataTypeSyntaxError {
+ try {
+ List<AstTypeDefinition> typeDefinitions =
+ new DataParser(definitions).typeDefinitions();
+ add(typeDefinitions);
+ } catch (ParseException e) {
+ throw new DataTypeSyntaxError(e);
+ }
+ }
+
+ /**
+ * Translates an unnamed data type.
+ * @param ast Abstract syntax tree of the type to be translated
+ * @return Translated data type
+ * @throws DataTypeSyntaxError
+ */
+ public Datatype translate(AstType ast) throws DataTypeSyntaxError {
+ return add(null, ast);
+ }
+
+ /**
+ * Parses an unnamed data type.
+ *
+ * @param typeString The textual representation of the type to be translated
+ * @return Translated data type
+ * @throws DataTypeSyntaxError
+ */
+ public Datatype translate(String typeString) throws DataTypeSyntaxError {
+ try {
+ AstType type =
+ new DataParser(new StringReader(typeString)).type();
+ return add(null, type);
+ } catch (ParseException e) {
+ throw new DataTypeSyntaxError(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ DataTypePrinter printer = new DataTypePrinter( sb );
+ printer.setLinefeed( true );
+
+ for (Entry<String, Datatype> e : dataTypes.entrySet()) {
+ String name = e.getKey();
+ Datatype type = e.getValue();
+ sb.append("type ");
+ sb.append( name );
+ sb.append(" = ");
+ printer.print( type );
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+
+}