package org.yajul.serialization;
import org.yajul.collections.CollectionUtil;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectInput;
import java.io.Externalizable;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import static org.yajul.juli.LogHelper.unexpected;
/**
* Helper methods for Externalizable.
* <ul>
* <li>Nullable boxed types - readObject() / writeObject() are not very efficient.</li>
* </ul>
* <br>
* User: josh
* Date: Sep 9, 2009
* Time: 12:52:38 PM
*/
public class ExternalizableHelper {
private final static Logger log = Logger.getLogger(ExternalizableHelper.class.getName());
private static final int NULL_VALUE = -1;
private static final int NOT_NULL_VALUE = 1;
public static void writeNullableString(ObjectOutput out, String value) throws IOException {
if (value == null) {
out.writeInt(-1);
} else {
out.writeInt(value.length());
out.writeBytes(value);
}
}
public static String readNullableString(ObjectInput in) throws IOException {
int len = in.readInt();
if (len < 0)
return null;
else {
byte[] bytes = new byte[len];
if (len > 0) {
int r = in.read(bytes);
if (r != len)
throw new IOException("Unexpected end of stream! Expected " + len + " bytes, read " + r);
}
return new String(bytes);
}
}
public static void writeNullableLong(ObjectOutput out, Long aLong) throws IOException {
if (aLong == null)
writeNull(out);
else {
writeNotNull(out);
long v = aLong;
out.writeLong(v);
}
}
public static Long readNullableLong(ObjectInput in) throws IOException {
boolean isNull = readIsNull(in);
if (isNull)
return null;
else {
long v = in.readLong();
return v;
}
}
public static void writeNullableInteger(ObjectOutput out, Integer anInteger) throws IOException {
if (anInteger == null)
writeNull(out);
else {
writeNotNull(out);
out.writeInt(anInteger);
}
}
public static Integer readNullableInteger(ObjectInput in) throws IOException {
boolean isNull = readIsNull(in);
if (isNull)
return null;
else
return in.readInt();
}
public static void writeNullableEnum(ObjectOutput out, Enum<?> value) throws IOException {
if (value == null)
out.writeInt(NULL_VALUE);
else
out.writeInt(value.ordinal());
}
public static <T> T readNullableEnum(ObjectInput in, T[] values) throws IOException {
int ord = in.readInt();
if (ord == NULL_VALUE)
return null;
else
return values[ord];
}
public static void writeNullableEnumByte(ObjectOutput out, Enum<?> value) throws IOException {
if (value == null)
out.writeByte(NULL_VALUE);
else
writeEnumByte(out, value);
}
public static void writeEnumByte(ObjectOutput out, Enum<?> value) throws IOException {
assert value != null;
out.writeByte(value.ordinal());
}
public static <T> T readNullableEnumByte(ObjectInput in, T[] values) throws IOException {
return readEnumByte(in, values);
}
public static <T> T readEnumByte(ObjectInput in, T[] values) throws IOException {
int ord = in.readByte();
if (ord == NULL_VALUE)
return null;
else
return values[ord];
}
/**
* Writes the optional externalizable object directly. NOTE: Use ObjectOutput.writeObject() if
* you expect the object to have multiple references!
*
* @param out the output stream
* @param ex the object to write
* @throws IOException if something goes wrong
*/
public static void writeNullable(ObjectOutput out, Externalizable ex) throws IOException {
if (ex == null)
writeNull(out);
else {
writeNotNull(out);
ex.writeExternal(out);
}
}
/**
* Reads in an optional 'child' externalizable. NOTE: Use ObjectInput.readObject() if you
* expect the object to have multiple references!
*
* @param in the input stream
* @param exClass the object class
* @param <T> parameterized object class
* @return the object, or null
* @throws IOException if something goes wrong
* @throws ClassNotFoundException if something goes wrong
*/
public static <T extends Externalizable> T readNullable(ObjectInput in, Class<T> exClass) throws IOException, ClassNotFoundException {
boolean isNull = readIsNull(in);
if (isNull)
return null;
else {
T ex;
try {
ex = exClass.newInstance();
} catch (InstantiationException e) {
unexpected(log, e);
throw new IOException("Unable to instantiate " + exClass.getName() + " due to : " + e.getMessage());
} catch (IllegalAccessException e) {
unexpected(log, e);
throw new IOException("Unable to instantiate " + exClass.getName() + " due to : " + e.getMessage());
}
ex.readExternal(in);
return ex;
}
}
private static void writeNotNull(ObjectOutput out) throws IOException {
out.writeByte(NOT_NULL_VALUE);
}
private static void writeNull(ObjectOutput out) throws IOException {
out.writeByte(NULL_VALUE);
}
private static boolean readIsNull(ObjectInput in) throws IOException {
return in.readByte() == NULL_VALUE;
}
public static <T> T readObject(ObjectInput in, Class<T> clazz) throws IOException, ClassNotFoundException {
Object o = in.readObject();
return o == null ? null : clazz.cast(o);
}
public static void writeList(ObjectOutput out, Collection<? extends Object> collection) throws IOException {
if (collection == null) {
out.writeInt(NULL_VALUE);
return;
} else {
out.writeInt(collection.size());
for (Object obj : collection) {
out.writeObject(obj);
}
}
}
public static <T> List<T> readArrayList(ObjectInput in, Class<T> clazz) throws IOException, ClassNotFoundException {
int size = in.readInt();
if (size == NULL_VALUE)
return null;
else {
List<T> list = CollectionUtil.newArrayList(size);
for (int i = 0; i < size; i++) {
T e = readObject(in, clazz);
list.add(e);
}
return list;
}
}
/**
* Returns an int representing the state of each object: 0 for null, 1 for not null.
*
* @param objects objects that may or may not be null
* @return an int contining 'null bits'
*/
public static int getNullBits(Object... objects) {
if (objects == null || objects.length == 0)
return 0;
if (objects.length > 30)
throw new IllegalArgumentException("Too many nullable objects.");
int bits = 0;
int bitflag = 1;
for (Object object : objects) {
if (object != null)
bits |= bitflag;
bitflag <<= 1;
}
return bits;
}
/**
* Returns true if the object at the given position was NOT NULL, false if it IS NULL.
*
* @param bits the bit flags, returned from getNullBits()
* @param pos the position (starting with zero)
* @return true if the object at the given position was NOT NULL, false if it IS NULL.
*/
public static boolean isNotNullBit(int bits, int pos) {
int bitflag = 1 << pos;
return (bitflag & bits) != 0;
}
}