/*
* 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.client.batch;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.sql.Array;
import java.sql.SQLException;
import java.sql.Time;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.teiid.core.types.ArrayImpl;
import org.teiid.core.types.DataTypeManagerService;
import org.teiid.core.types.DataTypeManagerService.DefaultDataTypes;
import org.teiid.designer.runtime.version.spi.ITeiidServerVersion;
import org.teiid.runtime.client.Messages;
/**
* @since 4.2
*
* <ul>
* <li>version 0: starts with 7.1 and uses simple serialization too broadly
* <li>version 1: starts with 8.0 uses better string, blob, clob, xml, etc.
* add varbinary support.
* however was possibly silently truncating date/time values that were
* outside of jdbc allowed values
* <li>version 2: starts with 8.2 and adds better array serialization and
* uses a safer date/time serialization
* </ul>
*/
public class Batch2Serializer extends Batch1Serializer {
protected Batch2Serializer(ITeiidServerVersion teiidVersion, byte version) {
super(teiidVersion, version);
serializers.put(DataTypeManagerService.DefaultDataTypes.BIG_DECIMAL.getId(), new ColumnSerializer[] {new BigDecimalColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.BIG_INTEGER.getId(), new ColumnSerializer[] {new BigIntegerColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.BOOLEAN.getId(), new ColumnSerializer[] {new BooleanColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.BYTE.getId(), new ColumnSerializer[] {new ByteColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.CHAR.getId(), new ColumnSerializer[] {new CharColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.DATE.getId(), new ColumnSerializer[] {new DateColumnSerializer(), new DateColumnSerializer1_B2(), new DateColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.DOUBLE.getId(), new ColumnSerializer[] {new DoubleColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.FLOAT.getId(), new ColumnSerializer[] {new FloatColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.INTEGER.getId(), new ColumnSerializer[] {new IntColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.LONG.getId(), new ColumnSerializer[] {new LongColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.SHORT.getId(), new ColumnSerializer[] {new ShortColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.TIME.getId(), new ColumnSerializer[] {new TimeColumnSerializer(), new TimeColumnSerializer1_B2(), new TimeColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.TIMESTAMP.getId(), new ColumnSerializer[] {new TimestampColumnSerializer()});
serializers.put(DataTypeManagerService.DefaultDataTypes.STRING.getId(), new ColumnSerializer[] {defaultSerializer, new StringColumnSerializer1()});
serializers.put(DataTypeManagerService.DefaultDataTypes.CLOB.getId(), new ColumnSerializer[] {defaultSerializer, new ClobColumnSerializer1()});
serializers.put(DataTypeManagerService.DefaultDataTypes.BLOB.getId(), new ColumnSerializer[] {defaultSerializer, new BlobColumnSerializer1()});
serializers.put(DataTypeManagerService.DefaultDataTypes.XML.getId(), new ColumnSerializer[] {defaultSerializer, new XmlColumnSerializer1()});
serializers.put(DataTypeManagerService.DefaultDataTypes.NULL.getId(), new ColumnSerializer[] {defaultSerializer, new NullColumnSerializer1()});
serializers.put(DataTypeManagerService.DefaultDataTypes.OBJECT.getId(), new ColumnSerializer[] {defaultSerializer, new ObjectColumnSerializer1_B1(DataTypeManagerService.DefaultDataTypes.VARBINARY.ordinal())});
serializers.put(DataTypeManagerService.DefaultDataTypes.VARBINARY.getId(), new ColumnSerializer[] {new BinaryColumnSerializer(), new BinaryColumnSerializer1()});
}
/**
* @param teiidVersion
*/
public Batch2Serializer(ITeiidServerVersion teiidVersion) {
this(teiidVersion, (byte) 2);
}
protected ColumnSerializer arrayColumnSerializer = new ColumnSerializer() {
@Override
public void writeObject(ObjectOutput out, Object obj, Map<Object, Integer> cache, byte version)
throws IOException {
try {
super.writeObject(out, ((java.sql.Array)obj).getArray(), cache, version);
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
public Object readObject(ObjectInput in, List<Object> cache, byte version) throws IOException,
ClassNotFoundException {
return new ArrayImpl(getTeiidVersion(), (Object[]) in.readObject());
}
};
protected ColumnSerializer getArrayColumnSerializer2() {
return new ArrayColumnSerializer2(new ObjectColumnSerializer1_B1(DataTypeManagerService.DefaultDataTypes.VARBINARY.ordinal()));
}
protected class ArrayColumnSerializer2 extends ColumnSerializer {
ObjectColumnSerializer1_B1 ser;
public ArrayColumnSerializer2(ObjectColumnSerializer1_B1 ser) {
this.ser = ser;
}
@Override
public void writeObject(ObjectOutput out, Object obj, Map<Object, Integer> cache, byte version)
throws IOException {
Object[] values = null;
try {
values = (Object[]) ((Array)obj).getArray();
} catch (SQLException e) {
out.writeInt(-1);
return;
}
out.writeInt(values.length);
DefaultDataTypes dataType = getDataTypeManager().getDataType(values.getClass().getComponentType());
int code = dataType.ordinal();
out.writeByte((byte)code);
for (int i = 0; i < values.length;) {
writeIsNullData(out, i, values);
int end = Math.min(values.length, i+8);
for (; i < end; i++) {
if (values[i] != null) {
ser.writeObject(out, values[i], code, cache, version);
}
}
}
out.writeBoolean((obj instanceof ArrayImpl && ((ArrayImpl)obj).isZeroBased()));
}
@Override
public Object readObject(ObjectInput in, List<Object> cache, byte version) throws IOException,
ClassNotFoundException {
int length = in.readInt();
if (length == -1) {
return new ArrayImpl(getTeiidVersion(), (Object[]) null);
}
int code = in.readByte();
DefaultDataTypes dataType = DataTypeManagerService.DefaultDataTypes.valueOf(getTeiidVersion(), code);
Object[] vals = (Object[])java.lang.reflect.Array.newInstance(dataType.getTypeClass(), length);
for (int i = 0; i < length;) {
byte b = in.readByte();
int end = Math.min(length, i+8);
for (; i < end; i++) {
if (!isNullObject(i, b)) {
vals[i] = ser.readObject(in, code, cache, version);
}
}
}
ArrayImpl result = new ArrayImpl(getTeiidVersion(), vals);
result.setZeroBased(in.readBoolean());
return result;
}
@Override
public boolean usesCache(byte version) {
return version >= 3;
}
}
private static long MIN_DATE_32;
private static long MAX_DATE_32;
private static long MIN_TIME_32;
private static long MAX_TIME_32;
@Override
protected void initDateNormalizer() {
if (dateNormalizer == -1) {
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$
c.set(1900, 0, 1, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
MIN_DATE_32 = c.getTimeInMillis();
MAX_DATE_32 = MIN_DATE_32 + ((1l<<32)-1)*60000;
dateNormalizer = -(int)(MIN_DATE_32/60000); //support a 32 bit range starting at this value
MAX_TIME_32 = Integer.MAX_VALUE*1000l;
MIN_TIME_32 = Integer.MIN_VALUE*1000l;
}
}
private class DateColumnSerializer1_B2 extends DateColumnSerializer1 {
@Override
public void writeObject(ObjectOutput out, Object obj, Map<Object, Integer> cache, byte version) throws IOException {
long time = ((java.sql.Date)obj).getTime();
if (time < MIN_DATE_32 || time > MAX_DATE_32) {
throw new IOException(Messages.gs(Messages.TEIID.TEIID20029, obj.getClass().getName()));
}
out.writeInt((int)(time/60000) + dateNormalizer);
}
}
protected class TimeColumnSerializer1_B2 extends TimeColumnSerializer1 {
@Override
public void writeObject(ObjectOutput out, Object obj, Map<Object, Integer> cache, byte version) throws IOException {
long time = ((Time)obj).getTime();
if (time < MIN_TIME_32 || time > MAX_TIME_32) {
throw new IOException(Messages.gs(Messages.TEIID.TEIID20029, obj.getClass().getName()));
}
out.writeInt((int)(time/1000));
}
}
@Override
protected ColumnSerializer getSerializer(String type, byte version) {
ColumnSerializer[] sers = serializers.get(type);
if (sers == null) {
if (DataTypeManagerService.isArrayType(type)) {
if (version < 2) {
return arrayColumnSerializer;
}
return getArrayColumnSerializer2();
}
return defaultSerializer;
}
return sers[Math.min(version, sers.length - 1)];
}
}