/******************************************************************************* * 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.InputStream; import java.io.StringReader; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.simantics.databoard.Datatypes; import org.simantics.databoard.parser.DataParser; import org.simantics.databoard.parser.ParseException; import org.simantics.databoard.parser.ast.type.AstArrayType; import org.simantics.databoard.parser.ast.type.AstAttribute; import org.simantics.databoard.parser.ast.type.AstComponent; import org.simantics.databoard.parser.ast.type.AstRecordType; import org.simantics.databoard.parser.ast.type.AstTupleType; import org.simantics.databoard.parser.ast.type.AstType; import org.simantics.databoard.parser.ast.type.AstTypeDefinition; import org.simantics.databoard.parser.ast.type.AstTypeReference; import org.simantics.databoard.parser.ast.type.AstUnionType; import org.simantics.databoard.parser.unparsing.DataTypePrinter; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.Component; import org.simantics.databoard.type.DataTypeDefinition; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.DoubleType; import org.simantics.databoard.type.FloatType; import org.simantics.databoard.type.IntegerType; import org.simantics.databoard.type.LongType; import org.simantics.databoard.type.MapType; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.StringType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.Limit; import org.simantics.databoard.util.Range; /** * 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 dataTypes = new TreeMap(); Map dataTypesConstruction = new HashMap(); Map untranslatedTypes = new TreeMap(); Map typeNames = new HashMap(); /** * 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) { //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 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 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 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 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 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(); } }