package net.rubyeye.xmemcached.transcoders;
import java.util.Date;
/**
* Transcoder that provides compatibility with Greg Whalin's memcached client.
*/
public class WhalinTranscoder extends BaseSerializingTranscoder implements
Transcoder<Object> {
public static final int SPECIAL_BYTE = 1;
public static final int SPECIAL_BOOLEAN = 8192;
public static final int SPECIAL_INT = 4;
public static final int SPECIAL_LONG = 16384;
public static final int SPECIAL_CHARACTER = 16;
public static final int SPECIAL_STRING = 32;
public static final int SPECIAL_STRINGBUFFER = 64;
public static final int SPECIAL_FLOAT = 128;
public static final int SPECIAL_SHORT = 256;
public static final int SPECIAL_DOUBLE = 512;
public static final int SPECIAL_DATE = 1024;
public static final int SPECIAL_STRINGBUILDER = 2048;
public static final int SPECIAL_BYTEARRAY = 4096;
public static final int COMPRESSED = 2;
public static final int SERIALIZED = 8;
private int maxSize;
private boolean primitiveAsString;
public void setPackZeros(boolean packZeros) {
this.tu.setPackZeros(packZeros);
}
public void setPrimitiveAsString(boolean primitiveAsString) {
this.primitiveAsString = primitiveAsString;
}
public WhalinTranscoder() {
this(CachedData.MAX_SIZE);
}
public WhalinTranscoder(int maxSize) {
super();
this.maxSize = maxSize;
}
public final int getMaxSize() {
return this.maxSize;
}
public final void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
public boolean isPackZeros() {
return this.tu.isPackZeros();
}
public boolean isPrimitiveAsString() {
return this.primitiveAsString;
}
private final TranscoderUtils tu = new TranscoderUtils(false);
/*
* (non-Javadoc)
*
* @see net.spy.memcached.Transcoder#decode(net.spy.memcached.CachedData)
*/
public Object decode(CachedData d) {
byte[] data = d.getData();
Object rv = null;
if ((d.getFlag() & COMPRESSED) != 0) {
data = decompress(d.getData());
}
if ((d.getFlag() & SERIALIZED) != 0) {
rv = deserialize(data);
} else {
int f = d.getFlag() & ~COMPRESSED;
if (this.primitiveAsString) {
if (f == SPECIAL_STRING) {
return decodeString(d.getData());
}
}
switch (f) {
case SPECIAL_BOOLEAN:
rv = Boolean.valueOf(this.decodeBoolean(data));
break;
case SPECIAL_INT:
rv = Integer.valueOf(this.tu.decodeInt(data));
break;
case SPECIAL_SHORT:
rv = Short.valueOf((short) this.tu.decodeInt(data));
break;
case SPECIAL_LONG:
rv = Long.valueOf(this.tu.decodeLong(data));
break;
case SPECIAL_DATE:
rv = new Date(this.tu.decodeLong(data));
break;
case SPECIAL_BYTE:
rv = Byte.valueOf(this.tu.decodeByte(data));
break;
case SPECIAL_FLOAT:
rv = new Float(Float.intBitsToFloat(this.tu.decodeInt(data)));
break;
case SPECIAL_DOUBLE:
rv = new Double(Double.longBitsToDouble(this.tu
.decodeLong(data)));
break;
case SPECIAL_BYTEARRAY:
rv = data;
break;
case SPECIAL_STRING:
rv = decodeString(data);
break;
case SPECIAL_STRINGBUFFER:
rv = new StringBuffer(decodeString(data));
break;
case SPECIAL_STRINGBUILDER:
rv = new StringBuilder(decodeString(data));
break;
case SPECIAL_CHARACTER:
rv = decodeCharacter(data);
break;
default:
log.warn(String.format("Cannot handle data with flags %x", f));
}
}
return rv;
}
public CachedData encode(Object o) {
byte[] b = null;
int flags = 0;
if (o instanceof String) {
b = encodeString((String) o);
flags |= SPECIAL_STRING;
} else if (o instanceof StringBuffer) {
flags |= SPECIAL_STRINGBUFFER;
b = encodeString(String.valueOf(o));
} else if (o instanceof StringBuilder) {
flags |= SPECIAL_STRINGBUILDER;
b = encodeString(String.valueOf(o));
} else if (o instanceof Long) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeLong((Long) o);
}
flags |= SPECIAL_LONG;
} else if (o instanceof Integer) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeInt((Integer) o);
}
flags |= SPECIAL_INT;
} else if (o instanceof Short) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeInt((Short) o);
}
flags |= SPECIAL_SHORT;
} else if (o instanceof Boolean) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.encodeBoolean((Boolean) o);
}
flags |= SPECIAL_BOOLEAN;
} else if (o instanceof Date) {
b = this.tu.encodeLong(((Date) o).getTime());
flags |= SPECIAL_DATE;
} else if (o instanceof Byte) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeByte((Byte) o);
}
flags |= SPECIAL_BYTE;
} else if (o instanceof Float) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeInt(Float.floatToIntBits((Float) o));
}
flags |= SPECIAL_FLOAT;
} else if (o instanceof Double) {
if (this.primitiveAsString) {
b = encodeString(o.toString());
} else {
b = this.tu.encodeLong(Double.doubleToLongBits((Double) o));
}
flags |= SPECIAL_DOUBLE;
} else if (o instanceof byte[]) {
b = (byte[]) o;
flags |= SPECIAL_BYTEARRAY;
} else if (o instanceof Character) {
b = this.tu.encodeInt((Character) o);
flags |= SPECIAL_CHARACTER;
} else {
b = serialize(o);
flags |= SERIALIZED;
}
assert b != null;
if (this.primitiveAsString) {
if ((flags & SERIALIZED) == 0) {
flags = 0;
flags |= SPECIAL_STRING;
}
}
if (b.length > this.compressionThreshold) {
byte[] compressed = compress(b);
if (compressed.length < b.length) {
if (log.isDebugEnabled()) {
log
.debug(String.format("Compressed %s from %d to %d",
o.getClass().getName(), b.length,
compressed.length));
}
b = compressed;
flags |= COMPRESSED;
} else {
if (log.isDebugEnabled()) {
log
.debug(String
.format(
"Compression increased the size of %s from %d to %d",
o.getClass().getName(), b.length,
compressed.length));
}
}
}
return new CachedData(flags, b);
}
protected Character decodeCharacter(byte[] b) {
return Character.valueOf((char) this.tu.decodeInt(b));
}
public byte[] encodeBoolean(boolean b) {
byte[] rv = new byte[1];
rv[0] = (byte) (b ? 1 : 0);
return rv;
}
public boolean decodeBoolean(byte[] in) {
assert in.length == 1 : "Wrong length for a boolean";
return in[0] == 1;
}
}