/*
* 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;
}
}