/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.sparse;
import java.io.Externalizable;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import com.bigdata.io.DataInputBuffer;
import com.bigdata.io.DataOutputBuffer;
import com.bigdata.io.SerializerUtil;
/**
* A type safe enumeration of value types. This class also supports encoding
* and decoding of instances of the enumerated value types and is used for
* encoding and decoding of column values.
*/
public enum ValueType {
/**
* A 32-bit integer.
*/
Integer(1),
/**
* A 64-bit integer.
*/
Long(2),
/**
* A single precision floating point value.
*/
Float(3),
/**
* A double precision floating point value.
*/
Double(4),
/**
* A Unicode string.
*/
Unicode(5),
/**
* A date is serialized as a long integer.
*/
Date(6),
/**
* An uninterpreted byte array.
*/
ByteArray(7),
/**
* A 32-bit auto-incremental counter.
*
* @see AutoIncIntegerCounter
*/
AutoIncInteger(8),
/**
* A 64-bit auto-incremental counter.
*
* @see AutoIncLongCounter
*/
AutoIncLong(9),
/**
* A Java object that will be serialized using its {@link Serializable} or
* {@link Externalizable} interface.
*/
Serializable(10)
;
private final int code;
ValueType(int code) {
this.code = code;
}
/**
* An integer that identifies the type safe enumeration value.
*
* @return
*/
public int intValue() {
return code;
}
public static ValueType valueOf(int code) {
switch(code) {
case 1: return Integer;
case 2: return Long;
case 3: return Float;
case 4: return Double;
case 5: return Unicode;
case 6: return Date;
case 7: return ByteArray;
case 8: return AutoIncInteger;
case 9: return AutoIncLong;
case 10: return Serializable;
default:
throw new IllegalArgumentException("Unknown code: "+code);
}
}
/**
* The encoding used to serialize Unicode data.
*/
private static final String UTF8 = "UTF-8";
/**
* Thread local buffer.
*/
private static ThreadLocal buf = new ThreadLocal() {
protected synchronized Object initialValue() {
return new DataOutputBuffer();
}
};
/**
* Return a {@link ThreadLocal} buffer that is used to serialize values.
*/
public static DataOutputBuffer getBuffer() {
return (DataOutputBuffer)buf.get();
}
/**
* Encode an object that is an instance of a supported class.
*
* @param v
* The value.
*
* @return The serialized byte[] encoding that value -or- null iff the
* value is null.
*
* @exception UnsupportedOperationException
* if the value is not an instance of a supported class.
*/
public static byte[] encode(Object v) {
try {
if (v == null) {
/*
* A null will be interpreted as a deletion request for a
* column value on insert / update.
*/
return null;
}
DataOutputBuffer buf = getBuffer();
buf.reset();
if (v instanceof byte[]) {
buf.writeByte(ValueType.ByteArray.intValue());
// @todo constrain max byte[] length?
byte[] bytes = (byte[])v;
buf.packLong(bytes.length);
buf.write(bytes);
} else if (v instanceof Number) {
if (v instanceof Integer) {
buf.writeByte(ValueType.Integer.intValue());
buf.writeInt(((Number) v).intValue());
} else if (v instanceof Long) {
buf.writeByte(ValueType.Long.intValue());
buf.writeLong(((Number) v).longValue());
} else if (v instanceof Float) {
buf.writeByte(ValueType.Float.intValue());
buf.writeFloat(((Number) v).floatValue());
} else if (v instanceof Double) {
buf.writeByte(ValueType.Double.intValue());
buf.writeDouble(((Number) v).doubleValue());
} else {
throw new UnsupportedOperationException();
}
} else if (v instanceof Date) {
buf.writeByte(ValueType.Date.intValue());
buf.writeLong(((Date)v).getTime());
} else if (v instanceof String) {
buf.writeByte(ValueType.Unicode.intValue());
// @todo constrain max byte[] length?
byte[] bytes = ((String)v).getBytes(UTF8);
buf.packLong( bytes.length );
buf.write( bytes );
} else if( v instanceof AutoIncIntegerCounter ){
buf.writeByte(ValueType.AutoIncInteger.intValue());
} else if( v instanceof AutoIncLongCounter ){
buf.writeByte(ValueType.AutoIncLong.intValue());
} else if( v instanceof Serializable) {
buf.writeByte(ValueType.Serializable.intValue());
final byte[] bytes = SerializerUtil.serialize(v);
buf.packLong( bytes.length );
buf.write( bytes );
} else {
throw new UnsupportedOperationException();
}
return buf.toByteArray();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
/**
*
* @param v
* @return
*/
public static Object decode(final byte[] v) {
if (v == null)
return null;
if (v.length == 0)
throw new IllegalArgumentException("Zero length byte[]");
final DataInputBuffer buf = new DataInputBuffer(v, 0, v.length);
try {
final ValueType type = ValueType.valueOf((int) buf.readByte());
switch (type) {
case Integer:
return buf.readInt();
case Long:
return buf.readLong();
case Float:
return buf.readFloat();
case Double:
return buf.readDouble();
case Unicode: {
final int len = (int)buf.unpackLong();
final byte[] bytes = new byte[len];
buf.readFully(bytes);
String s = new String(bytes,UTF8);
return s;
}
case Date:
return new java.util.Date(buf.readLong());
case ByteArray: {
final int len = (int)buf.unpackLong();
final byte[] bytes = new byte[len];
buf.readFully(bytes);
return bytes;
}
case AutoIncInteger: {
return AutoIncIntegerCounter.INSTANCE;
}
case AutoIncLong: {
return AutoIncLongCounter.INSTANCE;
}
case Serializable: {
final int len = (int)buf.unpackLong();
final byte[] bytes = new byte[len];
buf.readFully(bytes);
return SerializerUtil.deserialize(bytes);
}
default:
throw new AssertionError("type="+type);
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}