/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.client;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.List;
import java.util.Map;
import org.teiid.client.batch.Batch0Serializer;
import org.teiid.client.batch.Batch1Serializer;
import org.teiid.client.batch.Batch2Serializer;
import org.teiid.client.batch.Batch3Serializer;
import org.teiid.client.batch.Batch4Serializer;
import org.teiid.core.types.DataTypeManagerService;
import org.teiid.designer.runtime.version.spi.ITeiidServerVersion;
import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version;
/**
* @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
* <li>version 3: starts with 8.6 and adds better repeated string performance
* <li>version 4: starts with 8.10 and adds the geometry type
* </ul>
*/
public abstract class BatchSerializer {
protected final byte currentVersion;
private final ITeiidServerVersion teiidVersion;
protected ColumnSerializer defaultSerializer = new ColumnSerializer();
/**
* Base Column Serializer
*/
public class ColumnSerializer {
/**
* @param out
* @param col
* @param batch
* @param cache
* @param version
* @throws IOException
*/
public void writeColumn(ObjectOutput out, int col, List<? extends List<?>> batch, Map<Object, Integer> cache, byte version) throws IOException {
writeIsNullData(out, col, batch);
Object obj = null;
for (int i = 0; i < batch.size(); i++) {
obj = batch.get(i).get(col);
if (obj != null) {
writeObject(out, obj, cache, version);
}
}
}
/**
* @param in
* @param col
* @param batch
* @param isNull
* @param cache
* @param version
* @throws IOException
* @throws ClassNotFoundException
*/
public void readColumn(ObjectInput in, int col, List<List<Object>> batch, byte[] isNull, List<Object> cache, byte version) throws IOException, ClassNotFoundException {
readIsNullData(in, isNull);
for (int i = 0; i < batch.size(); i++) {
if (!isNullObject(isNull, i)) {
batch.get(i).set(col, readObject(in, cache, version));
}
}
}
/**
* @param out
* @param obj
* @param cache
* @param version
* @throws IOException
*/
public void writeObject(ObjectOutput out, Object obj, Map<Object, Integer> cache, byte version) throws IOException {
out.writeObject(obj);
}
/**
* @param in
* @param cache
* @param version
* @return read object
* @throws IOException
* @throws ClassNotFoundException
*/
public Object readObject(ObjectInput in, List<Object> cache, byte version) throws IOException, ClassNotFoundException {
return in.readObject();
}
/**
* @param version
* @return true if serializer should cache, false by default
*/
public boolean usesCache(byte version) {
return false;
}
}
/**
* @param teiidVersion
*/
protected BatchSerializer(ITeiidServerVersion teiidVersion, byte version) {
this.teiidVersion = teiidVersion;
this.currentVersion = version;
}
protected ITeiidServerVersion getTeiidVersion() {
return this.teiidVersion;
}
/**
* @return the currentVersion
*/
protected byte getCurrentVersion() {
return this.currentVersion;
}
protected DataTypeManagerService getDataTypeManager() {
return DataTypeManagerService.getInstance(getTeiidVersion());
}
/**
* @param teiidVersion
* @return correct version of batch serializer according to teiid version
*/
public static BatchSerializer getInstance(ITeiidServerVersion teiidVersion) {
if (teiidVersion.isLessThan(Version.TEIID_8_0))
return new Batch0Serializer(teiidVersion);
else if (teiidVersion.isLessThan(Version.TEIID_8_2))
return new Batch1Serializer(teiidVersion);
else if (teiidVersion.isLessThan(Version.TEIID_8_6))
return new Batch2Serializer(teiidVersion);
else if (teiidVersion.isLessThan(Version.TEIID_8_10))
return new Batch3Serializer(teiidVersion);
else
return new Batch4Serializer(teiidVersion);
}
protected void writeIsNullData(ObjectOutput out, int offset, Object[] batch) throws IOException {
int currentByte = 0;
for (int mask = 0x80; offset < batch.length; offset++, mask >>= 1) {
if (batch[offset] == null) {
currentByte |= mask;
}
}
out.write(currentByte);
}
/**
* Packs the (boolean) information about whether data values in the column are null
* into bytes so that we send ~n/8 instead of n bytes.
* @param out
* @param col
* @param batch
* @throws IOException
* @since 4.2
*/
protected void writeIsNullData(ObjectOutput out, int col, List<? extends List<?>> batch) throws IOException {
int numBytes = batch.size() / 8, row = 0, currentByte = 0;
for (int byteNum = 0; byteNum < numBytes; byteNum++, row += 8) {
currentByte = (batch.get(row).get(col) == null) ? 0x80 : 0;
if (batch.get(row + 1).get(col) == null) {
currentByte |= 0x40;
}
if (batch.get(row + 2).get(col) == null) {
currentByte |= 0x20;
}
if (batch.get(row + 3).get(col) == null) {
currentByte |= 0x10;
}
if (batch.get(row + 4).get(col) == null) {
currentByte |= 0x08;
}
if (batch.get(row + 5).get(col) == null) {
currentByte |= 0x04;
}
if (batch.get(row + 6).get(col) == null) {
currentByte |= 0x02;
}
if (batch.get(row + 7).get(col) == null) {
currentByte |= 0x01;
}
out.write(currentByte);
}
if (batch.size() % 8 > 0) {
currentByte = 0;
for (int mask = 0x80; row < batch.size(); row++, mask >>= 1) {
if (batch.get(row).get(col) == null) {
currentByte |= mask;
}
}
out.write(currentByte);
}
}
/**
* Reads the isNull data into a byte array
* @param in
* @param isNullBytes
* @throws IOException
* @since 4.2
*/
protected void readIsNullData(ObjectInput in, byte[] isNullBytes) throws IOException {
for (int i = 0; i < isNullBytes.length; i++) {
isNullBytes[i] = in.readByte();
}
}
/**
* Gets whether a data value is null based on a packed byte array containing boolean data
* @param isNull
* @param row
* @return
* @since 4.2
*/
protected boolean isNullObject(byte[] isNull, int row) {
// byte number mask bits to shift mask
return (isNull[row / 8] & (0x01 << (7 - (row % 8)))) != 0;
}
protected boolean isNullObject(int row, byte b) {
return (b & (0x01 << (7 - (row % 8)))) != 0;
}
/**
* @param in
* @param types
* @return batch of results
* @throws IOException
* @throws ClassNotFoundException
*/
public abstract List<List<Object>> readBatch(ObjectInput in, String[] types) throws IOException, ClassNotFoundException;
/**
* @param out
* @param types
* @param batch
* @throws IOException
*/
public abstract void writeBatch(ObjectOutput out, String[] types, List<? extends List<?>> batch) throws IOException;
/**
* @param out
* @param types
* @param batch
* @param version
* @throws IOException
*/
public abstract void writeBatch(ObjectOutput out, String[] types, List<? extends List<?>> batch, byte version) throws IOException;
}