AbstractFixedLengthData.java
// <editor-fold defaultstate="collapsed" desc="license">
/*
* Copyright (c) 2009, Karl H. Beckers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
**/
// </editor-fold>
package net.jarre_de_the.griffin.types.data;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import net.jarre_de_the.griffin.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The parent for all data types that have a defined, fixed length in bytes,
* irrespective of their content.
*
* @author charly4711
* @param <T>
* @param <P>
*/
public abstract class AbstractFixedLengthData<T extends Number, P extends AbstractData> extends AbstractData<P> {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFixedLengthData.class);
// I want this, but I can't have it in Java:
// abstract static public P read(RandomAccessFile in)
// throws IOException;
/**
* Returns the value of the instance in a format fit for physical storage in
* a GFF file.
*
* @return
*/
abstract public byte[] getValueAsByteArray();
/**
* Returns the value of the instance as a Number with enough bits to store
* the value. The numeric representation in Java may be off, though, due to
* differences regarding signed-ness.
*
* @return
*/
abstract public T getValue();
/**
* A way to ask an instance about the number of bytes it needs. This way
* code that can work with various subclasses of AbstractFixedLengthData
* doesn't need to use ugly reflection hacks to find that out.
*
* @return
*/
abstract public int length();
/**
* Reads a given Number from a file at the current location of the file
* pointer.
*
* @param in
* @param clazz Long.class, Double.class, Float.class, Integer.class,
* Short.class, or (the default) Byte.class
* @return
* @throws IOException
*/
protected static Number read(RandomAccessFile in, Class clazz) throws IOException {
int length = Util.getByteLength(clazz);
LOGGER.debug("Reading " + length + " bytes from file.");
byte[] buf = new byte[length];
in.readFully(buf);
LOGGER.trace("Read this buffer: " + new String(buf, Util.CHARSET_US_ASCII));
// GFF data types are always stored as little endian, no matter what
// the native byte order, so we need to make sure we interpret the
// data read in that way
ByteBuffer bb = ByteBuffer.wrap(buf);
bb.order(ByteOrder.LITTLE_ENDIAN);
Number value;
if (Long.class.isAssignableFrom(clazz)) {
value = bb.getLong(0);
} else if (Double.class.isAssignableFrom(clazz)) {
value = bb.getDouble(0);
} else if (Float.class.isAssignableFrom(clazz)) {
value = bb.getFloat(0);
} else if (Integer.class.isAssignableFrom(clazz)) {
value = bb.getInt(0);
} else if (Short.class.isAssignableFrom(clazz)) {
value = bb.getShort(0);
} else {
// Byte
value = bb.get(0);
}
LOGGER.trace("Endianness corrected to yield: " + value);
return value;
}
/**
* @see #getValueAsByteArray()
*
* @param value
* @return
*/
protected byte[] getValueAsByteArray(T value) {
if (null != value) {
Class<T> clazz = (Class<T>) value.getClass();
int length = Util.getByteLength(clazz);
byte[] buf = new byte[length];
// GFF data types are always little endian, no matter what the
// native byte order, so we need to make sure we write our data in that
// way
ByteBuffer bb = ByteBuffer.wrap(buf);
bb.order(ByteOrder.LITTLE_ENDIAN);
if (Long.class.isAssignableFrom(clazz)) {
bb.putLong(value.longValue());
} else if (Double.class.isAssignableFrom(clazz)) {
bb.putDouble(value.doubleValue());
} else if (Float.class.isAssignableFrom(clazz)) {
bb.putFloat(value.floatValue());
} else if (Integer.class.isAssignableFrom(clazz)) {
bb.putInt(value.intValue());
} else if (Short.class.isAssignableFrom(clazz)) {
bb.putShort(value.shortValue());
} else {
// Byte
bb.put(value.byteValue());
}
LOGGER.debug("Retrieving byte array to: " + new String(buf, Util.CHARSET_US_ASCII));
return buf;
} else {
return null;
}
}
}