/* * _____________________________________________________________________________ * * INAF - OATS National Institute for Astrophysics - Astronomical Observatory of * Trieste INAF - IA2 Italian Center for Astronomical Archives * _____________________________________________________________________________ * * Copyright (C) 2017 Istituto Nazionale di Astrofisica * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License Version 3 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package it.inaf.ia2.tsm.model; import it.inaf.ia2.tsm.datalayer.ADQL; import it.inaf.ia2.tsm.datalayer.DataTypeMode; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.xml.bind.JAXB; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlRootElement; /** * Utility class for converting data types having different syntaxes. *

* It parses the sql_type_mapping.xml resource file. * * @author Sonia Zorba {@literal } */ public class TypesMapping { @XmlRootElement(name = "sql_type_mapping") static class TypesWrapper { private List types; @XmlElements({ @XmlElement(name = "type") }) public List getTypes() { return types; } public void setTypes(List types) { this.types = types; } } private static final List TYPES; static { try (InputStream is = TypeMapping.class.getClassLoader().getResourceAsStream("sql_type_mapping.xml")) { TypesWrapper typesWrapper = JAXB.unmarshal(is, TypesWrapper.class); TYPES = typesWrapper.types; } catch (IOException e) { throw new ExceptionInInitializerError(e); } } private static TypeMapping getTypeMappingFromADQLType(String adqlType) { for (TypeMapping typeMapping : TYPES) { if (adqlType.equals(typeMapping.getAdqlType())) { return typeMapping; } } return null; } /** * Converts an ADQL datatype into a Java type. */ public static Class getClassFromAdqlType(String adqlType) { TypeMapping typeMapping = getTypeMappingFromADQLType(adqlType); if (typeMapping == null) { return String.class; } return typeMapping.getJavaType(); } /** * Converts an ADQL datatype to MySQL datatype. */ public static String getMySQLTypeFromADQLType(String adqlType) { TypeMapping typeMapping = getTypeMappingFromADQLType(adqlType); if (typeMapping == null) { return "VARCHAR"; } return typeMapping.getMySQLMapping().getTypes().get(0); } /** * Converts an ADQL datatype to PostgreSQL datatype. */ public static String getPostgresSQLTypeFromADQLType(String adqlType) { TypeMapping typeMapping = getTypeMappingFromADQLType(adqlType); if (typeMapping == null) { return "character varying"; } return typeMapping.getPgsqlMapping().getTypes().get(0); } /** * Returns the correct datatype according to the {@link DataTypeMode} used * by a TAP_SCHEMA. * * @param adqlType the datatype in ADQL format; this format is used in XML * configuration files contained into the {@code schema_definition} * resources folder. * @param mode the {@code DataTypeMode} used by the TAP_SCHEMA. */ public static String getDataType(String adqlType, DataTypeMode mode) { switch (mode) { case ADQL: return adqlType; case VOTABLE: return getTypeMappingFromADQLType(adqlType).getVoTableType(); default: throw new UnsupportedOperationException("DataTypeMode " + mode + " not supported yet"); } } /** * Retrieves the {@code TypeMapping} for a datatype expressed in the mode * used by a given TAP_SCHEMA. * * @param dataType the datatype, as exposed by the TAP_SCHEMA. * @param mode the {@code DataTypeMode} used by the TAP_SCHEMA. */ public static TypeMapping getTypeMapping(String dataType, DataTypeMode mode) { for (TypeMapping typeMapping : TYPES) { switch (mode) { case ADQL: if (dataType.equals(typeMapping.getAdqlType())) { return typeMapping; } break; case VOTABLE: if (dataType.equals(typeMapping.getVoTableType())) { return typeMapping; } break; default: throw new UnsupportedOperationException("DataTypeMode " + mode + " not supported yet"); } } throw new RuntimeException("Unable to get TypeMapping for datatype " + dataType + " [" + mode + "]"); } private static abstract class DataTypeFromDBTypeMapper { private final DataTypeMode mode; DataTypeFromDBTypeMapper(DataTypeMode mode) { this.mode = mode; } abstract DBTypeMapping getDBTypeMapping(TypeMapping typeMapping); private String getDataType(TypeMapping mapping) { switch (mode) { case ADQL: return mapping.getAdqlType(); case VOTABLE: return mapping.getVoTableType(); default: throw new UnsupportedOperationException("DataTypeMode " + mode + " not supported yet"); } } private String getDefaultDataType(String dbType) { switch (mode) { case ADQL: return ADQL.getDataType(dbType); case VOTABLE: return "char"; default: throw new UnsupportedOperationException("DataTypeMode " + mode + " not supported yet"); } } String getDataTypeFromDBType(String dbType) { String dbTypeWithoutSize = dbType.toUpperCase().replaceAll("\\(.+\\)", ""); for (TypeMapping typeMapping : TYPES) { DBTypeMapping dbMapping = getDBTypeMapping(typeMapping); if (dbMapping.isInverse()) { for (String type : dbMapping.getTypes()) { if (dbTypeWithoutSize.toLowerCase().startsWith(type.toLowerCase())) { return getDataType(typeMapping); } } } } return getDefaultDataType(dbTypeWithoutSize); } } /** * Retrieves a datatype having the format required by a given TAP_SCHEMA * from a MySQL datatype. * * @param mode the {@code DataTypeMode} used by the TAP_SCHEMA. */ public static String getDataTypeFromMySQLType(String mysqlType, DataTypeMode mode) { return (new DataTypeFromDBTypeMapper(mode) { @Override DBTypeMapping getDBTypeMapping(TypeMapping typeMapping) { return typeMapping.getMySQLMapping(); } }).getDataTypeFromDBType(mysqlType); } /** * Retrieves a datatype having the format required by a given TAP_SCHEMA * from a PostrgreSQL datatype. * * @param mode the {@code DataTypeMode} used by the TAP_SCHEMA. */ public static String getDataTypeFromPostgresType(String pgsqlType, DataTypeMode mode) { return (new DataTypeFromDBTypeMapper(mode) { @Override DBTypeMapping getDBTypeMapping(TypeMapping typeMapping) { return typeMapping.getPgsqlMapping(); } }).getDataTypeFromDBType(pgsqlType); } /** * Converts a MySQL datatype to an ADQL datatype. * * @param mysqlDBType the datatype, as read from MySQL information_schema. */ public static ADQL getADQLFromMySQLType(String mysqlDBType) { return ADQL.parse(getDataTypeFromMySQLType(mysqlDBType, DataTypeMode.ADQL)); } /** * Converts a PostgreSQL datatype to an ADQL datatype. * * @param postgresDBType the datatype, as read from PostgreSQL * information_schema. */ public static ADQL getADQLFromPostgresType(String postgresDBType) { return ADQL.parse(getDataTypeFromPostgresType(postgresDBType, DataTypeMode.ADQL)); } /** * Parses a string converting it in a typed object according to the datatype * specified in ADQL format. * * @param type datatype in ADQL format */ public static Object parseTypedValue(String value, String type) { switch (ADQL.valueOf(type)) { case BOOLEAN: return Boolean.parseBoolean(value); case SMALLINT: return Short.parseShort(value); case INTEGER: return Integer.parseInt(value); case BIGINT: return Long.parseLong(value); case REAL: return Float.parseFloat(value); case DOUBLE: return Double.parseDouble(value); default: return value; } } }