/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.olingo.common; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Array; import java.sql.Date; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.UUID; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; import org.apache.olingo.commons.api.edm.EdmParameter; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.geo.Geospatial; import org.apache.olingo.commons.core.edm.primitivetype.EdmBinary; import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.commons.core.edm.primitivetype.EdmStream; import org.apache.olingo.commons.core.edm.primitivetype.EdmString; import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; import org.apache.olingo.commons.core.edm.primitivetype.SingletonPrimitiveType; import org.teiid.GeometryInputSource; import org.teiid.api.exception.query.FunctionExecutionException; import org.teiid.core.TeiidException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.TeiidRuntimeException; import org.teiid.core.types.*; import org.teiid.query.function.GeometryUtils; import org.teiid.translator.TranslatorException; import com.vividsolutions.jts.geom.Geometry; public class ODataTypeManager { private static HashMap<String, String> odataTypes = new HashMap<String, String>(); private static HashMap<String, String> teiidTypes = new HashMap<String, String>(); static { odataTypes.put("Edm.String", DataTypeManager.DefaultDataTypes.STRING); odataTypes.put("Edm.Boolean", DataTypeManager.DefaultDataTypes.BOOLEAN); odataTypes.put("Edm.Byte", DataTypeManager.DefaultDataTypes.SHORT); odataTypes.put("Edm.SByte", DataTypeManager.DefaultDataTypes.BYTE); odataTypes.put("Edm.Int16", DataTypeManager.DefaultDataTypes.SHORT); odataTypes.put("Edm.Int32", DataTypeManager.DefaultDataTypes.INTEGER); odataTypes.put("Edm.Int64", DataTypeManager.DefaultDataTypes.LONG); odataTypes.put("Edm.Single", DataTypeManager.DefaultDataTypes.FLOAT); odataTypes.put("Edm.Double", DataTypeManager.DefaultDataTypes.DOUBLE); odataTypes.put("Edm.Decimal", DataTypeManager.DefaultDataTypes.BIG_DECIMAL); odataTypes.put("Edm.Date", DataTypeManager.DefaultDataTypes.DATE); odataTypes.put("Edm.TimeOfDay", DataTypeManager.DefaultDataTypes.TIME); odataTypes.put("Edm.DateTimeOffset", DataTypeManager.DefaultDataTypes.TIMESTAMP); odataTypes.put("Edm.Stream", DataTypeManager.DefaultDataTypes.BLOB); odataTypes.put("Edm.Guid", DataTypeManager.DefaultDataTypes.STRING); odataTypes.put("Edm.Binary", DataTypeManager.DefaultDataTypes.VARBINARY); //$NON-NLS-1$ odataTypes.put("Edm.Geometry", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryPoint", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryLineString", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryPolygon", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryMultiPoint", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryMultiLineString", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ odataTypes.put("Edm.GeometryCollection", DataTypeManager.DefaultDataTypes.GEOMETRY); //$NON-NLS-1$ teiidTypes.put(DataTypeManager.DefaultDataTypes.STRING, "Edm.String"); teiidTypes.put(DataTypeManager.DefaultDataTypes.BOOLEAN, "Edm.Boolean"); teiidTypes.put(DataTypeManager.DefaultDataTypes.SHORT, "Edm.Byte"); teiidTypes.put(DataTypeManager.DefaultDataTypes.BYTE, "Edm.SByte"); teiidTypes.put(DataTypeManager.DefaultDataTypes.SHORT, "Edm.Int16"); teiidTypes.put(DataTypeManager.DefaultDataTypes.INTEGER, "Edm.Int32"); teiidTypes.put(DataTypeManager.DefaultDataTypes.LONG, "Edm.Int64"); teiidTypes.put(DataTypeManager.DefaultDataTypes.FLOAT, "Edm.Single"); teiidTypes.put(DataTypeManager.DefaultDataTypes.DOUBLE, "Edm.Double"); teiidTypes.put(DataTypeManager.DefaultDataTypes.BIG_DECIMAL, "Edm.Decimal"); teiidTypes.put(DataTypeManager.DefaultDataTypes.DATE, "Edm.Date"); teiidTypes.put(DataTypeManager.DefaultDataTypes.TIME, "Edm.TimeOfDay"); teiidTypes.put(DataTypeManager.DefaultDataTypes.TIMESTAMP, "Edm.DateTimeOffset"); teiidTypes.put(DataTypeManager.DefaultDataTypes.BLOB, "Edm.Stream"); teiidTypes.put(DataTypeManager.DefaultDataTypes.CLOB, "Edm.Stream"); teiidTypes.put(DataTypeManager.DefaultDataTypes.XML, "Edm.Stream"); teiidTypes.put(DataTypeManager.DefaultDataTypes.VARBINARY, "Edm.Binary"); //$NON-NLS-1$ //will fail for most values teiidTypes.put(DataTypeManager.DefaultDataTypes.OBJECT, "Edm.Binary"); //$NON-NLS-1$ teiidTypes.put(DataTypeManager.DefaultDataTypes.GEOMETRY, "Edm.Stream"); //$NON-NLS-1$ } public static String teiidType(SingletonPrimitiveType odataType, boolean array) { String type = odataType.getFullQualifiedName().getFullQualifiedNameAsString(); return teiidType(type, array); } public static String teiidType(String odataType, boolean array) { String type = odataTypes.get(odataType); if (type == null) { type = DataTypeManager.DefaultDataTypes.STRING; // special case for enum type } if (array) { type +="[]"; } return type; } public static EdmPrimitiveTypeKind odataType(Class<?> teiidRuntimeTypeClass) { String dataType = DataTypeManager.getDataTypeName(teiidRuntimeTypeClass); return odataType(dataType); } public static EdmPrimitiveTypeKind odataType(String teiidRuntimeType) { if (teiidRuntimeType.endsWith("[]")) { teiidRuntimeType = teiidRuntimeType.substring(0, teiidRuntimeType.length()-2); //multi-dimensional is not supported - will be returned as string } String type = teiidTypes.get(teiidRuntimeType); if (type == null) { type = "Edm.String"; } return EdmPrimitiveTypeKind.valueOfFQN(type); } /** * * @param type * @param value * @param odataType type hint if the value could be a string containing a literal value of another type * @return * @throws TeiidException */ public static Object convertToTeiidRuntimeType(Class<?> type, Object value, String odataType) throws TeiidException { if (value == null) { return null; } if (type.isAssignableFrom(value.getClass())) { return value; } if (value instanceof UUID) { return value.toString(); } if (type.isArray() && value instanceof List<?>) { List<?> list = (List<?>)value; Class<?> expectedArrayComponentType = type.getComponentType(); Object array = Array.newInstance(type.getComponentType(), list.size()); for (int i = 0; i < list.size(); i++) { Object arrayItem = convertToTeiidRuntimeType(expectedArrayComponentType, list.get(i), null); Array.set(array, i, arrayItem); } return array; } if (odataType != null && value instanceof String) { try { value = ODataTypeManager.parseLiteral(odataType, (String)value); } catch (TeiidException e) { throw new TranslatorException(e); } } if (value instanceof Geospatial && type == DataTypeManager.DefaultDataClasses.GEOMETRY) { final Geospatial val = (Geospatial)value; //the strategy of converting to GML will only work for reading from //the odata translator - we need to actually convert to the GeometryType //for the odata service. When we undertake that, then the forked Atom serializer can be removed. //the parser will guess geography by default. we'll simply rely on the import logic to have chosen geometry only for edm geometry type values //if (val.getDimension() == Dimension.GEOMETRY) { return new GeometryInputSource() { @Override public Reader getGml() throws Exception { AtomGeoValueSerializer serializer = new AtomGeoValueSerializer(); XMLOutputFactory factory = XMLOutputFactory.newInstance(); StringWriter sw = new StringWriter(); final XMLStreamWriter writer = factory.createXMLStreamWriter(sw); serializer.serialize(writer, val); writer.close(); return new StringReader(sw.toString()); } @Override public Integer getSrid() { String srid = val.getSrid().toString(); try { return Integer.parseInt(srid); } catch (NumberFormatException e) { return null; } } }; //} } if (value instanceof Calendar) { Calendar calender = (Calendar)value; if (type.isAssignableFrom(java.sql.Time.class)) { calender.set(Calendar.YEAR, 1970); calender.set(Calendar.MONTH, Calendar.JANUARY); calender.set(Calendar.DAY_OF_MONTH, 1); calender.set(Calendar.MILLISECOND, 0); return new Time(calender.getTimeInMillis()); } else if (type.isAssignableFrom(java.sql.Date.class)) { calender.set(Calendar.HOUR_OF_DAY, 0); calender.set(Calendar.MINUTE, 0); calender.set(Calendar.SECOND, 0); calender.set(Calendar.MILLISECOND, 0); return new java.sql.Date(calender.getTimeInMillis()); } else if (type.isAssignableFrom(java.sql.Timestamp.class)) { return new Timestamp(calender.getTimeInMillis()); } } Transform transform = DataTypeManager.getTransform(value.getClass(), type); if (transform != null) { try { value = transform.transform(value, type); } catch (TransformationException e) { throw new TeiidException(e); } } return value; } public static Object convertByteArrayToTeiidRuntimeType(final Class<?> type, final byte[] contents, final String odataType) throws TeiidException { if (contents == null) { return null; } Object value = null; if (DataTypeManager.isLOB(type)) { InputStreamFactory isf = new InputStreamFactory() { @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(contents); } }; if (type.isAssignableFrom(SQLXML.class)) { value = new SQLXMLImpl(isf); } else if (type.isAssignableFrom(ClobType.class)) { value = new ClobImpl(isf, -1); } else if (type.isAssignableFrom(BlobType.class)) { value = new BlobImpl(isf); } } else if (DataTypeManager.DefaultDataClasses.VARBINARY.equals(type)) { value = contents; } else { value = convertToTeiidRuntimeType(type, new String(contents), odataType); } return value; } public static Object parseLiteral(EdmParameter edmParameter, Class<?> runtimeType, String value) throws TeiidProcessingException { EdmPrimitiveType primitiveType = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind .valueOf(edmParameter.getType() .getFullQualifiedName() .getFullQualifiedNameAsString().substring(4))); try { if (EdmString.getInstance().equals(edmParameter.getType())) { value = EdmString.getInstance().fromUriLiteral(value); } Object converted = primitiveType.valueOfString(value, edmParameter.isNullable(), edmParameter.getMaxLength(), edmParameter.getPrecision(), edmParameter.getScale(), true, runtimeType); return converted; } catch (EdmPrimitiveTypeException e) { throw new TeiidProcessingException(e); } } public static Object parseLiteral(EdmProperty edmProperty, Class<?> runtimeType, String value) throws TeiidException { EdmPrimitiveType primitiveType = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind .valueOf(edmProperty.getType() .getFullQualifiedName() .getFullQualifiedNameAsString().substring(4))); try { if (EdmString.getInstance().equals(edmProperty.getType())) { value = EdmString.getInstance().fromUriLiteral(value); } Object converted = primitiveType.valueOfString(value, edmProperty.isNullable(), edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(), true, runtimeType); return converted; } catch (EdmPrimitiveTypeException e) { throw new TeiidException(e); } } public static Object parseLiteral(String odataType, String value) throws TeiidException { EdmPrimitiveType primitiveType = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind .valueOf(odataType.substring(4))); int maxLength = DataTypeManager.MAX_STRING_LENGTH; if (primitiveType instanceof EdmBinary ||primitiveType instanceof EdmStream) { maxLength = DataTypeManager.MAX_VARBINARY_BYTES; } int precision = 4; int scale = 3; if (primitiveType instanceof EdmDecimal) { precision = 38; scale = 9; } Class<?> expectedClass = primitiveType.getDefaultType(); try { if (EdmString.getInstance().equals(primitiveType)) { value = EdmString.getInstance().fromUriLiteral(value); } Object converted = primitiveType.valueOfString(value, false, maxLength, precision, scale, true, expectedClass); if (primitiveType instanceof EdmTimeOfDay) { Calendar ts = (Calendar)converted; return new Time(ts.getTimeInMillis()); } else if (primitiveType instanceof EdmDate) { Calendar ts = (Calendar)converted; return new Date(ts.getTimeInMillis()); } return converted; } catch (EdmPrimitiveTypeException e) { throw new TeiidException(e); } } public static String convertToODataURIValue(Object val, String odataType) throws EdmPrimitiveTypeException { if (val == null) { return "null"; // is this correct? //$NON-NLS-1$ } if(odataType.startsWith("Edm.")) { //$NON-NLS-1$ odataType = odataType.substring(4); } if (odataType.startsWith("Geometry") && val instanceof GeometryType) { //$NON-NLS-1$ Geometry g; try { g = GeometryUtils.getGeometry((GeometryType)val); } catch (FunctionExecutionException e1) { throw new EdmPrimitiveTypeException(e1.getMessage(), e1); } StringWriter sw = new StringWriter(); sw.write("geometry'SRID="); //$NON-NLS-1$ sw.write(String.valueOf(g.getSRID())); sw.write(";"); //$NON-NLS-1$ ODataWKTWriter writer = new ODataWKTWriter(); try { writer.write(g, sw); } catch (IOException e) { throw new TeiidRuntimeException(e); } sw.write("'"); //$NON-NLS-1$ return sw.toString(); } EdmPrimitiveTypeKind kind = EdmPrimitiveTypeKind.valueOf(odataType); String value = EdmPrimitiveTypeFactory.getInstance(kind).valueToString( val, true, DataTypeManager.MAX_STRING_LENGTH, 0, 0, true); if (kind == EdmPrimitiveTypeKind.String) { return EdmString.getInstance().toUriLiteral(value); } return value; } }