/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services 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 Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.messaging.jmf.codec.std.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
import org.granite.messaging.jmf.CodecRegistry;
import org.granite.messaging.jmf.DumpContext;
import org.granite.messaging.jmf.InputContext;
import org.granite.messaging.jmf.JMFEncodingException;
import org.granite.messaging.jmf.OutputContext;
import org.granite.messaging.jmf.codec.std.PrimitiveArrayCodec;
import org.granite.messaging.jmf.codec.std.impl.util.FloatUtil;
import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil;
import org.granite.messaging.jmf.codec.std.impl.util.LongUtil;
import org.granite.messaging.jmf.codec.std.impl.util.DoubleUtil;
/**
* @author Franck WOLFF
*/
public class PrimitiveArrayCodecImpl extends AbstractArrayCodec implements PrimitiveArrayCodec {
protected static final boolean[] BOOLEAN_0 = new boolean[0];
protected static final char[] CHARACTER_0 = new char[0];
protected static final byte[] BYTE_0 = new byte[0];
protected static final short[] SHORT_0 = new short[0];
protected static final int[] INTEGER_0 = new int[0];
protected static final long[] LONG_0 = new long[0];
protected static final float[] FLOAT_0 = new float[0];
protected static final double[] DOUBLE_0 = new double[0];
public int getObjectType() {
return JMF_PRIMITIVE_ARRAY;
}
public boolean canEncode(Object v) {
return v.getClass().isArray() && getComponentType(v).isPrimitive();
}
public void encode(OutputContext ctx, Object v) throws IOException {
int indexOfStoredObject = ctx.indexOfObject(v);
if (indexOfStoredObject != -1) {
int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredObject);
// Write the index to a stored array:
// [parameterized type{1}, index{1,4}]
ctx.getOutputStream().write(0x80 | (count << 4) | JMF_PRIMITIVE_ARRAY);
IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count);
}
else {
ctx.addToObjects(v);
ArrayStructure structure = new ArrayStructure(v);
int jmfComponentType = primitiveClassToJmfType(structure.componentType);
int length = Array.getLength(v);
int count = IntegerUtil.significantIntegerBytesCount0(length);
final OutputStream os = ctx.getOutputStream();
if (structure.dimensions == 0) {
// Write the length and component type of the array:
// [parameterized type{1}, length{1,4}, jmf component type{1}]
os.write((count << 4) | JMF_PRIMITIVE_ARRAY);
IntegerUtil.encodeInteger(ctx, length, count);
os.write(jmfComponentType);
writePrimitiveArrayContent0(ctx, v, jmfComponentType);
}
else {
// Write the length, component type and dimensions of the array:
// [parameterized type{1}, length{1,4}, jmf component type{1}, dimensions{1}]
os.write(0x40 | (count << 4) | JMF_PRIMITIVE_ARRAY);
IntegerUtil.encodeInteger(ctx, length, count);
os.write(jmfComponentType);
os.write(structure.dimensions);
writePrimitiveArrayContent(ctx, v, jmfComponentType, structure.dimensions);
}
}
}
protected void writePrimitiveArrayContent(OutputContext ctx, Object v, int jmfComponentType, int dimensions) throws IOException {
final int length = Array.getLength(v);
if (length == 0)
return;
dimensions--;
final boolean writePrimitiveArray0 = (dimensions == 0);
final OutputStream os = ctx.getOutputStream();
for (int index = 0; index < length; index++) {
Object component = Array.get(v, index);
if (component == null)
os.write(JMF_NULL);
else {
int indexOfStoredObject = ctx.indexOfObject(component);
if (indexOfStoredObject != -1) {
int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredObject);
// Write the index to a stored array:
// [parameterized type{1}, index{1,4}]
ctx.getOutputStream().write(0x80 | (count << 4) | JMF_PRIMITIVE_ARRAY);
IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count);
}
else {
ctx.addToObjects(component);
int componentLength = Array.getLength(component);
int count = IntegerUtil.significantIntegerBytesCount0(componentLength);
// Write the length of the array:
// [parameterized type{1}, length{1,4}]
os.write((count << 4) | JMF_PRIMITIVE_ARRAY);
IntegerUtil.encodeInteger(ctx, componentLength, count);
if (writePrimitiveArray0)
writePrimitiveArrayContent0(ctx, component, jmfComponentType);
else
writePrimitiveArrayContent(ctx, component, jmfComponentType, dimensions);
}
}
}
}
protected void writePrimitiveArrayContent0(OutputContext ctx, Object v, int jmfComponentType) throws IOException {
switch (jmfComponentType) {
case JMF_BOOLEAN:
writeBooleanArrayContent0(ctx, (boolean[])v);
break;
case JMF_CHARACTER:
writeCharacterArrayContent0(ctx, (char[])v);
break;
case JMF_BYTE:
writeByteArrayContent0(ctx, (byte[])v);
break;
case JMF_SHORT:
writeShortArrayContent0(ctx, (short[])v);
break;
case JMF_INTEGER:
writeIntegerArrayContent0(ctx, (int[])v);
break;
case JMF_LONG:
writeLongArrayContent0(ctx, (long[])v);
break;
case JMF_FLOAT:
writeFloatArrayContent0(ctx, (float[])v);
break;
case JMF_DOUBLE:
writeDoubleArrayContent0(ctx, (double[])v);
break;
default:
throw new JMFEncodingException("Unsupported primitive type: " + jmfComponentType);
}
}
protected void writeBooleanArrayContent0(OutputContext ctx, boolean[] v) throws IOException {
if (v.length == 0)
return;
final OutputStream os = ctx.getOutputStream();
byte[] bytes = new byte[lengthOfBooleanArray(v.length)];
int i = 0, j = 0;
for (boolean b : v) {
if (b)
bytes[i] |= 0x80 >>> j;
j++;
if (j >= 8) {
j = 0;
i++;
}
}
os.write(bytes);
}
protected void writeCharacterArrayContent0(OutputContext ctx, char[] v) throws IOException {
if (v.length == 0)
return;
final OutputStream os = ctx.getOutputStream();
for (char c : v) {
os.write(c >>> 8);
os.write(c);
}
}
protected void writeByteArrayContent0(OutputContext ctx, byte[] v) throws IOException {
if (v.length == 0)
return;
ctx.getOutputStream().write(v);
}
protected void writeShortArrayContent0(OutputContext ctx, short[] v) throws IOException {
if (v.length == 0)
return;
final OutputStream os = ctx.getOutputStream();
for (short s : v) {
os.write(s >>> 8);
os.write(s);
}
}
protected void writeIntegerArrayContent0(OutputContext ctx, int[] v) throws IOException {
if (v.length == 0)
return;
for (int i : v)
IntegerUtil.encodeVariableInteger(ctx, i);
}
protected void writeLongArrayContent0(OutputContext ctx, long[] v) throws IOException {
if (v.length == 0)
return;
for (long l : v)
LongUtil.encodeVariableLong(ctx, l);
}
protected void writeFloatArrayContent0(OutputContext ctx, float[] v) throws IOException {
if (v.length == 0)
return;
for (float f : v)
FloatUtil.encodeFloat(ctx, f);
}
protected void writeDoubleArrayContent0(OutputContext ctx, double[] v) throws IOException {
if (v.length == 0)
return;
for (double d : v)
DoubleUtil.encodeVariableDouble(ctx, d);
}
public Object decode(InputContext ctx, int parameterizedJmfType) throws IOException, ClassNotFoundException {
Object v = null;
// Read the index (stored array) or length of the array:
// [index or length{1,4}]
int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> 4) & 0x03);
if ((parameterizedJmfType & 0x80) != 0)
v = ctx.getObject(indexOrLength);
else {
// Read the component type and, if the 0x40 flag is set, the dimensions of the array:
// [component type{1}, dimensions{0,1}]
int jmfComponentType = ctx.safeRead();
int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead());
Class<?> componentType = jmfTypeToPrimitiveClass(jmfComponentType);
if (dimensions == 0)
v = readPrimitiveArray0(ctx, componentType, jmfComponentType, indexOrLength);
else
v = readPrimitiveArray(ctx, componentType, jmfComponentType, indexOrLength, dimensions);
}
return v;
}
protected Object readPrimitiveArray(InputContext ctx, Class<?> componentType, int jmfComponentType, int length, int dimensions) throws IOException {
Object v = newArray(componentType, length, dimensions);
ctx.addToObjects(v);
dimensions--;
final boolean readPrimitiveArray0 = (dimensions == 0);
for (int index = 0; index < length; index++) {
// Read the type of the element (must be JMF_NULL or JMF_PRIMITIVE_ARRAY):
// [array element type{1}]
int eltParameterizedJmfType = ctx.safeRead();
if (eltParameterizedJmfType == JMF_NULL)
Array.set(v, index, null);
else {
// Read the length of the element (sub array):
// [length{1, 4}]
int eltIndexOrLength = IntegerUtil.decodeInteger(ctx, (eltParameterizedJmfType >>> 4) & 0x03);
if ((eltParameterizedJmfType & 0x80) != 0)
Array.set(v, index, ctx.getObject(eltIndexOrLength));
else if (readPrimitiveArray0)
Array.set(v, index, readPrimitiveArray0(ctx, componentType, jmfComponentType, eltIndexOrLength));
else
Array.set(v, index, readPrimitiveArray(ctx, componentType, jmfComponentType, eltIndexOrLength, dimensions));
}
}
return v;
}
protected Object readPrimitiveArray0(InputContext ctx, Class<?> componentType, int jmfComponentType, int length) throws IOException {
Object v = null;
switch (jmfComponentType) {
case JMF_BOOLEAN:
v = readBooleanArray0(ctx, length);
break;
case JMF_CHARACTER:
v = readCharacterArray0(ctx, length);
break;
case JMF_BYTE:
v = readByteArray0(ctx, length);
break;
case JMF_SHORT:
v = readShortArray0(ctx, length);
break;
case JMF_INTEGER:
v = readIntegerArray0(ctx, length);
break;
case JMF_LONG:
v = readLongArray0(ctx, length);
break;
case JMF_FLOAT:
v = readFloatArray0(ctx, length);
break;
case JMF_DOUBLE:
v = readDoubleArray0(ctx, length);
break;
default:
throw new JMFEncodingException("Unsupported primitive type: " + jmfComponentType);
}
ctx.addToObjects(v);
return v;
}
protected boolean[] readBooleanArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return BOOLEAN_0;
boolean[] a = new boolean[length];
int nb = lengthOfBooleanArray(length);
for (int i = 0; i < nb; i++) {
int b = ctx.safeRead();
for (int j = 0; j < 8; j++) {
int index = (i * 8) + j;
if (index >= length)
break;
a[index] = ((b & (0x80 >>> j)) != 0);
}
}
return a;
}
protected char[] readCharacterArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return CHARACTER_0;
char[] a = new char[length];
for (int i = 0; i < length; i++)
a[i] = (char)((ctx.safeRead() << 8) | ctx.safeRead());
return a;
}
protected byte[] readByteArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return BYTE_0;
byte[] a = new byte[length];
ctx.safeReadFully(a);
return a;
}
protected short[] readShortArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return SHORT_0;
short[] a = new short[length];
for (int i = 0; i < length; i++)
a[i] = (short)((ctx.safeRead() << 8) | ctx.safeRead());
return a;
}
protected int[] readIntegerArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return INTEGER_0;
int[] a = new int[length];
for (int i = 0; i < length; i++)
a[i] = IntegerUtil.decodeVariableInteger(ctx);
return a;
}
protected long[] readLongArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return LONG_0;
long[] a = new long[length];
for (int i = 0; i < length; i++)
a[i] = LongUtil.decodeVariableLong(ctx);
return a;
}
protected float[] readFloatArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return FLOAT_0;
float[] a = new float[length];
for (int i = 0; i < length; i++)
a[i] = FloatUtil.decodeFloat(ctx);
return a;
}
protected double[] readDoubleArray0(InputContext ctx, int length) throws IOException {
if (length == 0)
return DOUBLE_0;
double[] a = new double[length];
for (int i = 0; i < length; i++)
a[i] = DoubleUtil.decodeVariableDouble(ctx);
return a;
}
public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry();
int jmfType = codecRegistry.extractJmfType(parameterizedJmfType);
if (jmfType != JMF_PRIMITIVE_ARRAY)
throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
// Read the index (stored array) or length of the array:
// [index or length{1,4}]
int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> 4) & 0x03);
if ((parameterizedJmfType & 0x80) != 0)
ctx.indentPrintLn("<" + ctx.getObject(indexOrLength) + "@" + indexOrLength + ">");
else {
// Read the component type and, if the 0x40 flag is set, the dimensions of the array:
// [component type{1}, dimensions{0,1}]
int jmfComponentType = ctx.safeRead();
int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead());
Class<?> componentType = jmfTypeToPrimitiveClass(jmfComponentType);
if (dimensions == 0)
dumpPrimitiveArray0(ctx, componentType, jmfComponentType, indexOrLength);
else
dumpPrimitiveArray(ctx, componentType, jmfComponentType, indexOrLength, dimensions);
}
}
protected void dumpPrimitiveArray(DumpContext ctx, Class<?> componentType, int jmfComponentType, int length, int dimensions) throws IOException {
final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry();
String v = newDumpPrimitiveArray(jmfComponentType, length, dimensions);
int indexOfStoredObject = ctx.addToObjects(v);
ctx.indentPrintLn(v + "@" + indexOfStoredObject + ": {");
ctx.incrIndent(1);
dimensions--;
final boolean dumpPrimitiveArray0 = (dimensions == 0);
for (int index = 0; index < length; index++) {
// Read the type of the element (must be JMF_NULL or JMF_PRIMITIVE_ARRAY):
// [array element type{1}]
int parameterizedJmfType = ctx.safeRead();
int jmfType = codecRegistry.extractJmfType(parameterizedJmfType);
if (jmfType == JMF_NULL)
ctx.indentPrintLn("null");
else if (jmfType == JMF_PRIMITIVE_ARRAY) {
// Read the length of the sub array:
// [length{1, 4}]
int subLengthOrIndex = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> 4) & 0x03);
if ((parameterizedJmfType & 0x80) != 0)
ctx.indentPrintLn("<" + ctx.getObject(subLengthOrIndex) + "@" + subLengthOrIndex + ">");
else if (dumpPrimitiveArray0)
dumpPrimitiveArray0(ctx, componentType, jmfComponentType, subLengthOrIndex);
else
dumpPrimitiveArray(ctx, componentType, jmfComponentType, subLengthOrIndex, dimensions);
}
else
newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
}
ctx.incrIndent(-1);
ctx.indentPrintLn("}");
}
protected void dumpPrimitiveArray0(DumpContext ctx, Class<?> componentType, int jmfComponentType, int length) throws IOException {
String v = newDumpPrimitiveArray(jmfComponentType, length, 0);
int indexOfStoredObject = ctx.addToObjects(v);
ctx.indentPrint(v + "@" + indexOfStoredObject + ": {");
switch (jmfComponentType) {
case JMF_BOOLEAN:
dumpBooleanArray0(ctx, length);
break;
case JMF_CHARACTER:
dumpCharacterArray0(ctx, length);
break;
case JMF_BYTE:
dumpByteArray0(ctx, length);
break;
case JMF_SHORT:
dumpShortArray0(ctx, length);
break;
case JMF_INTEGER:
dumpIntegerArray0(ctx, length);
break;
case JMF_LONG:
dumpLongArray0(ctx, length);
break;
case JMF_FLOAT:
dumpFloatArray0(ctx, length);
break;
case JMF_DOUBLE:
dumpDoubleArray0(ctx, length);
break;
default:
throw new JMFEncodingException("Unsupported primitive type: " + jmfComponentType);
}
ctx.noIndentPrintLn("}");
}
protected String newDumpPrimitiveArray(int jmfComponentType, int length, int dimensions) throws IOException {
StringBuilder sb = new StringBuilder();
switch (jmfComponentType) {
case JMF_BOOLEAN: sb.append("boolean"); break;
case JMF_CHARACTER: sb.append("char"); break;
case JMF_BYTE: sb.append("byte"); break;
case JMF_SHORT: sb.append("short"); break;
case JMF_INTEGER: sb.append("int"); break;
case JMF_LONG: sb.append("long"); break;
case JMF_FLOAT: sb.append("float"); break;
case JMF_DOUBLE: sb.append("double"); break;
default: throw new JMFEncodingException("Unsupported primitive type: " + jmfComponentType);
}
sb.append('[').append(length).append(']');
for (int i = 0; i < dimensions; i++)
sb.append("[]");
return sb.toString();
}
protected void dumpBooleanArray0(DumpContext ctx, int length) throws IOException {
if (length == 0)
return;
int nb = lengthOfBooleanArray(length);
for (int i = 0; i < nb; i++) {
int b = ctx.safeRead();
for (int j = 0; j < 8; j++) {
int index = (i * 8) + j;
if (index >= length)
break;
if (index > 0)
ctx.print(", ");
ctx.print(String.valueOf(((b & (0x80 >>> j)) != 0)));
}
}
}
protected void dumpCharacterArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf((char)((ctx.safeRead() << 8) | ctx.safeRead())));
}
}
protected void dumpByteArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf((byte)ctx.safeRead()));
}
}
protected void dumpShortArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf((short)(ctx.safeRead() << 8) | ctx.safeRead()));
}
}
protected void dumpIntegerArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf(IntegerUtil.decodeVariableInteger(ctx)));
}
}
protected void dumpLongArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf(LongUtil.decodeVariableLong(ctx)));
}
}
protected void dumpFloatArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf(FloatUtil.decodeFloat(ctx)));
}
}
protected void dumpDoubleArray0(DumpContext ctx, int length) throws IOException {
for (int i = 0; i < length; i++) {
if (i > 0)
ctx.print(", ");
ctx.print(String.valueOf(DoubleUtil.decodeVariableDouble(ctx)));
}
}
protected int lengthOfBooleanArray(int nb) {
return (nb / 8) + (nb % 8 != 0 ? 1 : 0);
}
protected int primitiveClassToJmfType(Class<?> primitiveClass) throws JMFEncodingException {
if (primitiveClass == byte.class)
return JMF_BYTE;
if (primitiveClass == int.class)
return JMF_INTEGER;
if (primitiveClass == char.class)
return JMF_CHARACTER;
if (primitiveClass == double.class)
return JMF_DOUBLE;
if (primitiveClass == long.class)
return JMF_LONG;
if (primitiveClass == boolean.class)
return JMF_BOOLEAN;
if (primitiveClass == float.class)
return JMF_FLOAT;
if (primitiveClass == short.class)
return JMF_SHORT;
throw new JMFEncodingException("Not a primitive class: " + primitiveClass);
}
protected Class<?> jmfTypeToPrimitiveClass(int jmfType) throws JMFEncodingException {
switch (jmfType) {
case JMF_BOOLEAN:
return boolean.class;
case JMF_BYTE:
return byte.class;
case JMF_CHARACTER:
return char.class;
case JMF_SHORT:
return short.class;
case JMF_INTEGER:
return int.class;
case JMF_LONG:
return long.class;
case JMF_FLOAT:
return float.class;
case JMF_DOUBLE:
return double.class;
}
throw new JMFEncodingException("Not a primitive JMF type: " + jmfType);
}
}