/**
* Copyright (c) 2011-2015 Exxeleron GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.exxeleron.qjava;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
/**
* Default {@link QWriter} implementation.
*/
public class DefaultQWriter extends QWriter {
/**
* @see com.exxeleron.qjava.QWriter#writeObject(java.lang.Object)
*/
@Override
protected void writeObject( final Object obj ) throws IOException, QException {
if ( obj instanceof Collection<?> ) {
writeCollection((Collection<?>) obj);
} else if ( obj instanceof Map<?, ?> ) {
writeMap((Map<?, ?>) obj);
} else {
final QType qtype = getQType(obj);
checkProtocolVersionCompatibility(qtype);
if ( qtype == QType.STRING ) {
writeString((char[]) obj);
} else if ( qtype == QType.GENERAL_LIST ) {
writeGeneralList((Object[]) obj);
} else if ( qtype == QType.NULL_ITEM ) {
writeNullItem();
} else if ( qtype == QType.ERROR ) {
writeError((Exception) obj);
} else if ( qtype == QType.DICTIONARY ) {
writeDictionary((QDictionary) obj);
} else if ( qtype == QType.TABLE ) {
writeTable((QTable) obj);
} else if ( qtype == QType.KEYED_TABLE ) {
writeKeyedTable((QKeyedTable) obj);
} else if ( qtype.getTypeCode() < 0 ) {
writeAtom(obj, qtype);
} else if ( qtype.getTypeCode() >= QType.BOOL_LIST.getTypeCode() && qtype.getTypeCode() <= QType.TIME_LIST.getTypeCode() ) {
writeList(obj, qtype);
} else if ( qtype == QType.LAMBDA ) {
writeLambda((QLambda) obj);
} else if ( qtype == QType.PROJECTION ) {
writeProjection((QProjection) obj);
} else {
throw new QWriterException("Unable to serialize q type: " + qtype);
}
}
}
@SuppressWarnings("incomplete-switch")
protected void writeAtom( final Object obj, final QType qtype ) throws IOException {
writer.writeByte(qtype.getTypeCode());
switch ( qtype ) {
case BOOL:
writer.writeByte((byte) ((Boolean) obj ? 1 : 0));
break;
case GUID:
writeGuid((UUID) obj);
break;
case BYTE:
writer.writeByte((Byte) obj);
break;
case SHORT:
writer.writeShort((Short) obj);
break;
case INT:
writer.writeInt((Integer) obj);
break;
case LONG:
writer.writeLong((Long) obj);
break;
case FLOAT:
writer.writeFloat((Float) obj);
break;
case DOUBLE:
writer.writeDouble((Double) obj);
break;
case CHAR:
writer.writeByte((byte) (char) (Character) obj);
break;
case SYMBOL:
writeSymbol((String) obj);
break;
case TIMESTAMP:
writer.writeLong(((QTimestamp) obj).getValue());
break;
case MONTH:
writer.writeInt(((QMonth) obj).getValue());
break;
case DATE:
writer.writeInt(((QDate) obj).getValue());
break;
case DATETIME:
writer.writeDouble(((QDateTime) obj).getValue());
break;
case TIMESPAN:
writer.writeLong(((QTimespan) obj).getValue());
break;
case MINUTE:
writer.writeInt(((QMinute) obj).getValue());
break;
case SECOND:
writer.writeInt(((QSecond) obj).getValue());
break;
case TIME:
writer.writeInt(((QTime) obj).getValue());
break;
}
}
@SuppressWarnings("incomplete-switch")
protected void writeList( final Object obj, final QType qtype ) throws IOException {
writer.writeByte(qtype.getTypeCode());
writer.writeByte((byte) 0); // attributes
switch ( qtype ) {
case BOOL_LIST: {
if ( obj instanceof boolean[] ) {
final boolean[] list = (boolean[]) obj;
writer.writeInt(list.length);
for ( final boolean e : list ) {
writer.writeByte((byte) (e ? 1 : 0));
}
} else if ( obj instanceof Boolean[] ) {
final Boolean[] list = (Boolean[]) obj;
writer.writeInt(list.length);
for ( final Boolean e : list ) {
writer.writeByte((byte) (e ? 1 : 0));
}
}
break;
}
case GUID_LIST: {
final UUID[] list = (UUID[]) obj;
writer.writeInt(list.length);
for ( final UUID e : list ) {
writeGuid(e);
}
break;
}
case BYTE_LIST: {
if ( obj instanceof byte[] ) {
final byte[] list = (byte[]) obj;
writer.writeInt(list.length);
for ( final byte e : list ) {
writer.writeByte(e);
}
} else if ( obj instanceof Byte[] ) {
final Byte[] list = (Byte[]) obj;
writer.writeInt(list.length);
for ( final Byte e : list ) {
writer.writeByte(e);
}
}
break;
}
case SHORT_LIST: {
if ( obj instanceof short[] ) {
final short[] list = (short[]) obj;
writer.writeInt(list.length);
for ( final short e : list ) {
writer.writeShort(e);
}
} else if ( obj instanceof Short[] ) {
final Short[] list = (Short[]) obj;
writer.writeInt(list.length);
for ( final Short e : list ) {
writer.writeShort(e);
}
}
break;
}
case INT_LIST: {
if ( obj instanceof int[] ) {
final int[] list = (int[]) obj;
writer.writeInt(list.length);
for ( final int e : list ) {
writer.writeInt(e);
}
} else if ( obj instanceof Integer[] ) {
final Integer[] list = (Integer[]) obj;
writer.writeInt(list.length);
for ( final Integer e : list ) {
writer.writeInt(e);
}
}
break;
}
case LONG_LIST: {
if ( obj instanceof long[] ) {
final long[] list = (long[]) obj;
writer.writeInt(list.length);
for ( final long e : list ) {
writer.writeLong(e);
}
} else if ( obj instanceof Long[] ) {
final Long[] list = (Long[]) obj;
writer.writeInt(list.length);
for ( final Long e : list ) {
writer.writeLong(e);
}
}
break;
}
case FLOAT_LIST: {
if ( obj instanceof float[] ) {
final float[] list = (float[]) obj;
writer.writeInt(list.length);
for ( final float e : list ) {
writer.writeFloat(e);
}
} else if ( obj instanceof Float[] ) {
final Float[] list = (Float[]) obj;
writer.writeInt(list.length);
for ( final Float e : list ) {
writer.writeFloat(e);
}
}
break;
}
case DOUBLE_LIST: {
if ( obj instanceof double[] ) {
final double[] list = (double[]) obj;
writer.writeInt(list.length);
for ( final double e : list ) {
writer.writeDouble(e);
}
} else if ( obj instanceof Double[] ) {
final Double[] list = (Double[]) obj;
writer.writeInt(list.length);
for ( final Double e : list ) {
writer.writeDouble(e);
}
}
break;
}
case SYMBOL_LIST: {
final String[] list = (String[]) obj;
writer.writeInt(list.length);
for ( final String e : list ) {
writeSymbol(e);
}
break;
}
case TIMESTAMP_LIST: {
final QTimestamp[] list = (QTimestamp[]) obj;
writer.writeInt(list.length);
for ( final QTimestamp e : list ) {
writer.writeLong(e.getValue());
}
break;
}
case MONTH_LIST: {
final QMonth[] list = (QMonth[]) obj;
writer.writeInt(list.length);
for ( final QMonth e : list ) {
writer.writeInt(e.getValue());
}
break;
}
case DATE_LIST: {
final QDate[] list = (QDate[]) obj;
writer.writeInt(list.length);
for ( final QDate e : list ) {
writer.writeInt(e.getValue());
}
break;
}
case DATETIME_LIST: {
final QDateTime[] list = (QDateTime[]) obj;
writer.writeInt(list.length);
for ( final QDateTime e : list ) {
writer.writeDouble(e.getValue());
}
break;
}
case TIMESPAN_LIST: {
final QTimespan[] list = (QTimespan[]) obj;
writer.writeInt(list.length);
for ( final QTimespan e : list ) {
writer.writeLong(e.getValue());
}
break;
}
case MINUTE_LIST: {
final QMinute[] list = (QMinute[]) obj;
writer.writeInt(list.length);
for ( final QMinute e : list ) {
writer.writeInt(e.getValue());
}
break;
}
case SECOND_LIST: {
final QSecond[] list = (QSecond[]) obj;
writer.writeInt(list.length);
for ( final QSecond e : list ) {
writer.writeInt(e.getValue());
}
break;
}
case TIME_LIST: {
final QTime[] list = (QTime[]) obj;
writer.writeInt(list.length);
for ( final QTime e : list ) {
writer.writeInt(e.getValue());
}
break;
}
}
}
@SuppressWarnings("incomplete-switch")
protected void writeCollection( final Collection<?> collection ) throws IOException, QException {
final Iterator<?> it = collection.iterator();
QType qtype = QType.GENERAL_LIST;
if ( it.hasNext() ) {
final Object firstElem = it.next();
qtype = firstElem != null && toQ.containsKey(firstElem.getClass()) ? toQ.get(firstElem.getClass()) : QType.GENERAL_LIST;
if ( qtype != QType.STRING ) {
qtype = QType.valueOf((byte) (-1 * qtype.getTypeCode()));
} else {
qtype = QType.GENERAL_LIST;
}
}
checkProtocolVersionCompatibility(qtype);
writer.writeByte(qtype.getTypeCode());
writer.writeByte((byte) 0); // attributes
writer.writeInt(collection.size());
switch ( qtype ) {
case BOOL_LIST: {
for ( final Object e : collection ) {
writer.writeByte((byte) ((Boolean) e ? 1 : 0));
}
break;
}
case GUID_LIST: {
for ( final Object e : collection ) {
writeGuid((UUID) e);
}
break;
}
case BYTE_LIST: {
for ( final Object e : collection ) {
writer.writeByte((Byte) e);
}
break;
}
case SHORT_LIST: {
for ( final Object e : collection ) {
writer.writeShort((Short) e);
}
break;
}
case INT_LIST: {
for ( final Object e : collection ) {
writer.writeInt((Integer) e);
}
break;
}
case LONG_LIST: {
for ( final Object e : collection ) {
writer.writeLong((Long) e);
}
break;
}
case FLOAT_LIST: {
for ( final Object e : collection ) {
writer.writeFloat((Float) e);
}
break;
}
case DOUBLE_LIST: {
for ( final Object e : collection ) {
writer.writeDouble((Double) e);
}
break;
}
case SYMBOL_LIST: {
for ( final Object e : collection ) {
writeSymbol((String) e);
}
break;
}
case TIMESTAMP_LIST: {
for ( final Object e : collection ) {
writer.writeLong(((QTimestamp) e).getValue());
}
break;
}
case MONTH_LIST:
for ( final Object e : collection ) {
writer.writeInt(((QMonth) e).getValue());
}
break;
case DATE_LIST: {
for ( final Object e : collection ) {
writer.writeInt(((QDate) e).getValue());
}
break;
}
case DATETIME_LIST: {
for ( final Object e : collection ) {
writer.writeDouble(((QDateTime) e).getValue());
}
break;
}
case TIMESPAN_LIST: {
for ( final Object e : collection ) {
writer.writeLong(((QTimespan) e).getValue());
}
break;
}
case MINUTE_LIST: {
for ( final Object e : collection ) {
writer.writeInt(((QMinute) e).getValue());
}
break;
}
case SECOND_LIST: {
for ( final Object e : collection ) {
writer.writeInt(((QSecond) e).getValue());
}
break;
}
case TIME_LIST: {
for ( final Object e : collection ) {
writer.writeInt(((QTime) e).getValue());
}
break;
}
case GENERAL_LIST: {
for ( final Object e : collection ) {
writeObject(e);
}
break;
}
}
}
protected void writeGeneralList( final Object[] list ) throws IOException, QException {
writer.writeByte(QType.GENERAL_LIST.getTypeCode());
writer.writeByte((byte) 0); // attributes
writer.writeInt(list.length);
for ( final Object obj : list ) {
writeObject(obj);
}
}
protected void writeSymbol( final String s ) throws IOException {
writer.write(s.getBytes(getEncoding()));
writer.writeByte((byte) 0);
}
protected void writeGuid( final UUID obj ) {
writer.writeLongBigEndian(obj.getMostSignificantBits());
writer.writeLongBigEndian(obj.getLeastSignificantBits());
}
protected void writeString( final char[] s ) throws IOException {
writer.writeByte(QType.STRING.getTypeCode());
writer.writeByte((byte) 0); // attributes
final byte[] encoded = String.valueOf(s).getBytes(getEncoding());
writer.writeInt(encoded.length);
writer.write(encoded);
}
protected void writeNullItem() {
writer.writeByte(QType.NULL_ITEM.getTypeCode());
writer.writeByte((byte) 0);
}
protected void writeError( final Exception e ) throws IOException {
writer.writeByte(QType.ERROR.getTypeCode());
writeSymbol(e.getMessage());
}
protected void writeDictionary( final QDictionary d ) throws IOException, QException {
writer.writeByte(QType.DICTIONARY.getTypeCode());
writeObject(d.getKeys());
writeObject(d.getValues());
}
protected void writeMap( final Map<?, ?> m ) throws IOException, QException {
writer.writeByte(QType.DICTIONARY.getTypeCode());
writer.writeByte(QType.GENERAL_LIST.getTypeCode());
writer.writeByte((byte) 0); // attributes
writer.writeInt(m.size());
for ( final Object key : m.keySet() ) {
writeObject(key);
}
writer.writeByte(QType.GENERAL_LIST.getTypeCode());
writer.writeByte((byte) 0); // attributes
writer.writeInt(m.size());
for ( final Object key : m.keySet() ) {
writeObject(m.get(key));
}
}
protected void writeTable( final QTable t ) throws IOException, QException {
writer.writeByte(QType.TABLE.getTypeCode());
writer.writeByte((byte) 0); // attributes
writer.writeByte(QType.DICTIONARY.getTypeCode());
writeObject(t.getColumns());
writeObject(t.getData());
}
protected void writeKeyedTable( final QKeyedTable t ) throws IOException, QException {
writer.writeByte(QType.KEYED_TABLE.getTypeCode());
writeObject(t.getKeys());
writeObject(t.getValues());
}
protected void writeLambda( final QLambda l ) throws IOException {
writer.writeByte(QType.LAMBDA.getTypeCode());
writer.writeByte((byte) 0);
writeString(l.getExpression().toCharArray());
}
protected void writeProjection( final QProjection p ) throws IOException, QException {
writer.writeByte(QType.PROJECTION.getTypeCode());
final int length = p.getParameters().length;
writer.writeInt(length);
for ( int i = 0; i < length; i++ ) {
writeObject(p.getParameters()[i]);
}
}
@SuppressWarnings("rawtypes")
private static final Map<Class, QType> toQ = Collections.unmodifiableMap(new HashMap<Class, QType>() {
private static final long serialVersionUID = 7199217298785029447L;
{
put(Object[].class, QType.GENERAL_LIST);
put(Boolean.class, QType.BOOL);
put(boolean[].class, QType.BOOL_LIST);
put(Boolean[].class, QType.BOOL_LIST);
put(Byte.class, QType.BYTE);
put(byte[].class, QType.BYTE_LIST);
put(Byte[].class, QType.BYTE_LIST);
put(UUID.class, QType.GUID);
put(UUID[].class, QType.GUID_LIST);
put(Short.class, QType.SHORT);
put(short[].class, QType.SHORT_LIST);
put(Short[].class, QType.SHORT_LIST);
put(Integer.class, QType.INT);
put(int[].class, QType.INT_LIST);
put(Integer[].class, QType.INT_LIST);
put(Long.class, QType.LONG);
put(long[].class, QType.LONG_LIST);
put(Long[].class, QType.LONG_LIST);
put(Float.class, QType.FLOAT);
put(float[].class, QType.FLOAT_LIST);
put(Float[].class, QType.FLOAT_LIST);
put(Double.class, QType.DOUBLE);
put(double[].class, QType.DOUBLE_LIST);
put(Double[].class, QType.DOUBLE_LIST);
put(Character.class, QType.CHAR);
put(char[].class, QType.STRING);
put(char[][].class, QType.GENERAL_LIST);
put(String.class, QType.SYMBOL);
put(String[].class, QType.SYMBOL_LIST);
put(QTimestamp.class, QType.TIMESTAMP);
put(QTimestamp[].class, QType.TIMESTAMP_LIST);
put(QMonth.class, QType.MONTH);
put(QMonth[].class, QType.MONTH_LIST);
put(QDate.class, QType.DATE);
put(QDate[].class, QType.DATE_LIST);
put(QDateTime.class, QType.DATETIME);
put(QDateTime[].class, QType.DATETIME_LIST);
put(QTimespan.class, QType.TIMESPAN);
put(QTimespan[].class, QType.TIMESPAN_LIST);
put(QMinute.class, QType.MINUTE);
put(QMinute[].class, QType.MINUTE_LIST);
put(QSecond.class, QType.SECOND);
put(QSecond[].class, QType.SECOND_LIST);
put(QTime.class, QType.TIME);
put(QTime[].class, QType.TIME_LIST);
put(QException.class, QType.ERROR);
put(QDictionary.class, QType.DICTIONARY);
put(QTable.class, QType.TABLE);
put(QKeyedTable.class, QType.KEYED_TABLE);
put(QLambda.class, QType.LAMBDA);
put(QProjection.class, QType.PROJECTION);
}
});
/**
* Returns default mapping for particular java object to representative q type.
*
* @param obj
* Requested object
* @return {@link QType} enum being a result of q serialization
* @throws QWriterException
*/
public static QType getQType( final Object obj ) throws QWriterException {
if ( obj == null ) {
return QType.NULL_ITEM;
} else if ( toQ.containsKey(obj.getClass()) ) {
return toQ.get(obj.getClass());
} else {
throw new QWriterException("Cannot serialize object of type: " + obj.getClass().getCanonicalName());
}
}
}