package tap.db; import java.util.HashMap; import java.util.Map; import tap.metadata.TAPType; import tap.metadata.TAPType.TAPDatatype; public class JDBCTAPFactory { public static enum DBMS{ POSTGRES; } public static interface DbmsTypeConverter< T, C > { public C convert(final T typeToConvert); } public static Map mapTypeAliases; public static Map> mapDbmsToTap; public static Map>> mapTapToDbms; static{ /* DECLARE DBMS TYPE ALIASES */ mapTypeAliases = new HashMap(); mapTypeAliases.put("int8", TAPDatatype.BIGINT); mapTypeAliases.put("bigserial", TAPDatatype.BIGINT); mapTypeAliases.put("bit", TAPDatatype.VARCHAR); mapTypeAliases.put("bit varying", TAPDatatype.VARCHAR); mapTypeAliases.put("varbit", TAPDatatype.VARCHAR); mapTypeAliases.put("boolean", TAPDatatype.SMALLINT); mapTypeAliases.put("bytea", TAPDatatype.VARBINARY); mapTypeAliases.put("character varying", TAPDatatype.VARCHAR); mapTypeAliases.put("character", TAPDatatype.CHAR); mapTypeAliases.put("double precision", TAPDatatype.DOUBLE); mapTypeAliases.put("float8", TAPDatatype.DOUBLE); mapTypeAliases.put("integer", TAPDatatype.INTEGER); mapTypeAliases.put("int4", TAPDatatype.INTEGER); mapTypeAliases.put("float4", TAPDatatype.REAL); mapTypeAliases.put("int2", TAPDatatype.SMALLINT); mapTypeAliases.put("serial", TAPDatatype.INTEGER); mapTypeAliases.put("serial4", TAPDatatype.INTEGER); mapTypeAliases.put("text", TAPDatatype.VARCHAR); /* DECLARE SPECIAL DBMS->TAP CONVERSIONS */ mapDbmsToTap = new HashMap>(); mapDbmsToTap.put("numeric", new DbmsTypeConverter(){ @Override public TAPType convert(String typeToConvert){ return new TAPType(TAPDatatype.DOUBLE); } }); mapDbmsToTap.put("decimal", new DbmsTypeConverter(){ @Override public TAPType convert(String typeToConvert){ return new TAPType(TAPDatatype.DOUBLE); } }); /* DECLARE SPECIAL TAP->DBMS CONVERSIONS */ mapTapToDbms = new HashMap>>(); // POSTGRES HashMap> postgresConverters = new HashMap>(); postgresConverters.put(TAPDatatype.DOUBLE, new DbmsTypeConverter(){ @Override public String convert(TAPType typeToConvert){ return "double precision"; } }); DbmsTypeConverter binaryConverter = new DbmsTypeConverter(){ @Override public String convert(TAPType typeToConvert){ return "bytea"; } }; postgresConverters.put(TAPDatatype.VARBINARY, binaryConverter); postgresConverters.put(TAPDatatype.BINARY, binaryConverter); postgresConverters.put(TAPDatatype.BLOB, binaryConverter); postgresConverters.put(TAPDatatype.CLOB, binaryConverter); mapTapToDbms.put(DBMS.POSTGRES, postgresConverters); } public JDBCTAPFactory(){ // TODO Auto-generated constructor stub } /** *

Convert the given TAP column type into a column type compatible with the specified DBMS.

* *

Note 1: if no {@link TAPType} is provided, the returned DBMS type will correspond to a * VARCHAR.

* *

Note 2: if no DBMS is specified or if the conversion has failed, the given TAP type will be * just "stringified" (by calling {@link TAPType#toString()})

* * @param tapType A TAP column type. * @param dbms DBMS target in which the given TAP column type must be converted. * * @return The corresponding DBMS column type. */ public static String toDbmsType(TAPType tapType, final DBMS dbms){ // If no TAP type is specified, consider it by default as a VARCHAR type: if (tapType == null) tapType = new TAPType(TAPDatatype.VARCHAR); // By default, just "stringify" the given TAP type: String dbmsType = tapType.toString(); // If some converters are defined for the specified DBMS... if (dbms != null && mapTapToDbms.containsKey(dbms)){ Map> dbmsMap = mapTapToDbms.get(dbms); // ...and if a converter exists for the given TAP datatype... DbmsTypeConverter converter = dbmsMap.get(tapType.type); if (converter != null){ // ...convert the given TAP type: String conversion = converter.convert(tapType); // ...and set the DBMS conversion if NOT NULL: if (conversion != null) dbmsType = conversion; } } return dbmsType; } /** *

Convert the given DBMS column type into a compatible TAP datatype.

* *

Note: If no DBMS type is specified or if the DBMS type can not be identified, * it will be converted as a VARCHAR.

* * @param dbmsType DBMS column datatype. * * @return The corresponding TAP column datatype. */ public static TAPType toTAPType(final String dbmsType){ // If no type is provided return VARCHAR: if (dbmsType == null || dbmsType.trim().length() == 0) return new TAPType(TAPDatatype.VARCHAR); // Extract the type prefix and lower-case it: int paramIndex = dbmsType.indexOf('('); String dbmsTypePrefix = (paramIndex <= 0) ? dbmsType : dbmsType.substring(0, paramIndex); dbmsTypePrefix = dbmsTypePrefix.toLowerCase(); // Use this type prefix as key to determine if it's a DBMS type alias and get its corresponding TAP datatype: TAPDatatype datatype = mapTypeAliases.get(dbmsTypePrefix); // If it's an alias, build the corresponding TAP type: if (datatype != null) return new TAPType(datatype, getLengthParam(dbmsType, paramIndex)); // If it's not an alias, use the type prefix as key to get a corresponding converter: DbmsTypeConverter converter = mapDbmsToTap.get(dbmsTypePrefix); // Try the type conversion using this converter: TAPType taptype = null; if (converter != null) taptype = converter.convert(dbmsType); /* * If no converter was found OR if the type conversion has failed, * consider the given type as equivalent to a declared TAP type. * * /!\ But if no equivalent exists, the given type will be ignore and * VARCHAR will be returned! */ if (taptype == null){ try{ // Try to find an equivalent TAPType: datatype = TAPDatatype.valueOf(dbmsTypePrefix.toUpperCase()); // If there is one return directly the TAPType: taptype = new TAPType(datatype, getLengthParam(dbmsType, paramIndex)); }catch(IllegalArgumentException iae){ // If none exists, return VARCHAR: taptype = new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH); } } return taptype; } /** *

Extract the 'length' parameter of a DBMS type string.

* *

* If the given type string does not contain any parameter * OR if the first parameter can not be casted into an integer, * {@link TAPType#NO_LENGTH} will be returned. *

* * @param dbmsType DBMS type string (containing the datatype and the 'length' parameter). * @param paramIndex Index of the open bracket. * * @return The 'length' parameter value if found, {@link TAPType#NO_LENGTH} otherwise. */ private static int getLengthParam(final String dbmsType, final int paramIndex){ // If no parameter has been previously detected, no length parameter: if (paramIndex <= 0) return TAPType.NO_LENGTH; // If there is one and that at least ONE parameter is provided.... else{ int lengthParam = TAPType.NO_LENGTH; String paramsStr = dbmsType.substring(paramIndex + 1); // ...extract the 'length' parameter: /* note: we suppose here that no other parameter is possible ; * but if there are, they are ignored and we try to consider the first parameter * as the length */ int paramEndIndex = paramsStr.indexOf(','); if (paramEndIndex <= 0) paramEndIndex = paramsStr.indexOf(')'); // ...cast it into an integer: try{ lengthParam = Integer.parseInt(paramsStr.substring(0, paramEndIndex)); }catch(Exception ex){} // ...and finally return it: return lengthParam; } } }