/**
* 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.StandardCodec;
import org.granite.messaging.jmf.codec.std.ObjectArrayCodec;
import org.granite.messaging.jmf.codec.std.impl.util.ClassNameUtil;
import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil;
/**
* @author Franck WOLFF
*/
public class ObjectArrayCodecImpl extends AbstractArrayCodec implements ObjectArrayCodec {
public int getObjectType() {
return JMF_OBJECT_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_OBJECT_ARRAY);
IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count);
}
else {
ctx.addToObjects(v);
ArrayStructure structure = new ArrayStructure(v);
String className = ctx.getAlias(structure.componentType.getName());
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}, component type name{1+}]
os.write((count << 4) | JMF_OBJECT_ARRAY);
IntegerUtil.encodeInteger(ctx, length, count);
ClassNameUtil.encodeClassName(ctx, className);
writeObjectArrayContent0(ctx, v);
}
else {
// Write the length, component type and dimensions of the array:
// [parameterized type{1}, length{1,4}, component type name{2+}, dimensions{1}]
os.write(0x40 | (count << 4) | JMF_OBJECT_ARRAY);
IntegerUtil.encodeInteger(ctx, length, count);
ClassNameUtil.encodeClassName(ctx, className);
os.write(structure.dimensions);
writeObjectArrayContent(ctx, v, structure.componentType, structure.dimensions);
}
}
}
protected void writeObjectArrayContent(OutputContext ctx, Object v, Class<?> componentType, int dimensions) throws IOException {
final int length = Array.getLength(v);
if (length == 0)
return;
dimensions--;
final boolean writeObjectArrayContent0 = (dimensions == 0);
final OutputStream os = ctx.getOutputStream();
for (int index = 0; index < length; index++) {
Object item = Array.get(v, index);
if (item == null)
os.write(JMF_NULL);
else {
int indexOfStoredObject = ctx.indexOfObject(item);
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_OBJECT_ARRAY);
IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count);
}
else {
ctx.addToObjects(item);
int itemLength = Array.getLength(item);
int count = IntegerUtil.significantIntegerBytesCount0(itemLength);
// Write the length of the array:
// [parameterized type{1}, item length{1,4}]
os.write((count << 4) | JMF_OBJECT_ARRAY);
IntegerUtil.encodeInteger(ctx, itemLength, count);
// Write item class name:
// [item type name{2+}]
Class<?> itemType = getComponentType(item);
String className = ctx.getAlias(itemType.getName());
ClassNameUtil.encodeClassName(ctx, className);
if (writeObjectArrayContent0)
writeObjectArrayContent0(ctx, item);
else
writeObjectArrayContent(ctx, item, itemType, dimensions);
}
}
}
}
protected void writeObjectArrayContent0(OutputContext ctx, Object v) throws IOException {
final int length = Array.getLength(v);
if (length == 0)
return;
for (int index = 0; index < length; index++)
ctx.writeObject(Array.get(v, index));
}
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 element class name and, if the 0x40 flag is set, the dimensions of the array:
// [element class name{2+}, dimensions{0,1}]
String componentTypeName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx));
int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead());
Class<?> componentType = ctx.getSharedContext().getReflection().loadClass(componentTypeName);
if (dimensions == 0)
v = readObjectArray0(ctx, componentType, indexOrLength);
else
v = readObjectArray(ctx, componentType, indexOrLength, dimensions);
}
return v;
}
protected Object readObjectArray(InputContext ctx, Class<?> componentType, int length, int dimensions) throws IOException, ClassNotFoundException {
Object v = newArray(componentType, length, dimensions);
ctx.addToObjects(v);
dimensions--;
final boolean readObjectArray0 = (dimensions == 0);
for (int index = 0; index < length; index++) {
// Read the type of the element (must be JMF_NULL or JMF_OBJECT_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 {
// Read element class name:
// [element class name{2+}]
String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx));
Class<?> eltComponentType = ctx.getReflection().loadClass(eltClassName);
if (readObjectArray0)
Array.set(v, index, readObjectArray0(ctx, eltComponentType, eltIndexOrLength));
else
Array.set(v, index, readObjectArray(ctx, eltComponentType, eltIndexOrLength, dimensions));
}
}
}
return v;
}
protected Object readObjectArray0(InputContext ctx, Class<?> componentType, int length) throws IOException, ClassNotFoundException {
Object v = Array.newInstance(componentType, length);
ctx.addToObjects(v);
for (int index = 0; index < length; index++)
Array.set(v, index, ctx.readObject());
return v;
}
public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry();
int jmfType = codecRegistry.extractJmfType(parameterizedJmfType);
if (jmfType != JMF_OBJECT_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 element class name and, if the 0x40 flag is set, the dimensions of the array:
// [element class name{2+}, dimensions{0,1}]
String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx));
int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead());
if (dimensions == 0)
dumpObjectArray0(ctx, eltClassName, indexOrLength);
else
dumpObjectArray(ctx, eltClassName, indexOrLength, dimensions);
}
}
protected void dumpObjectArray(DumpContext ctx, String componentType, int length, int dimensions) throws IOException {
String v = newDumpObjectArray(componentType, length, 0);
int indexOfStoredObject = ctx.addToObjects(v);
ctx.indentPrintLn(v + "@" + indexOfStoredObject + ": {");
ctx.incrIndent(1);
dimensions--;
final boolean dumpObjectArray0 = (dimensions == 0);
for (int index = 0; index < length; index++) {
// Read the type of the element (must be JMF_NULL or JMF_OBJECT_ARRAY):
// [array element type{1}]
int eltParameterizedJmfType = ctx.safeRead();
int eltJmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(eltParameterizedJmfType);
if (eltJmfType == JMF_NULL)
ctx.indentPrintLn("null");
else if (eltJmfType == JMF_OBJECT_ARRAY) {
// Read the length of the element (sub array):
// [length{1, 4}]
int eltIndexOrLength = IntegerUtil.decodeInteger(ctx, (eltParameterizedJmfType >>> 4) & 0x03);
if ((eltParameterizedJmfType & 0x80) != 0)
ctx.indentPrintLn("<" + ctx.getObject(eltIndexOrLength) + "@" + eltIndexOrLength + ">");
else {
// Read element class name:
// [element class name{2+}]
String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx));
if (dumpObjectArray0)
dumpObjectArray0(ctx, eltClassName, eltIndexOrLength);
else
dumpObjectArray(ctx, eltClassName, eltIndexOrLength, dimensions);
}
}
else
newBadTypeJMFEncodingException(eltJmfType, eltParameterizedJmfType);
}
ctx.incrIndent(-1);
ctx.indentPrintLn("}");
}
protected void dumpObjectArray0(DumpContext ctx, String componentType, int length) throws IOException {
String v = newDumpObjectArray(componentType, length, 0);
int indexOfStoredObject = ctx.addToObjects(v);
ctx.indentPrintLn(v + "@" + indexOfStoredObject + ": {");
ctx.incrIndent(1);
for (int index = 0; index < length; index++) {
int parameterizedJmfType = ctx.safeRead();
int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
StandardCodec<?> codec = ctx.getSharedContext().getCodecRegistry().getCodec(jmfType);
if (codec == null)
throw new JMFEncodingException("No codec for JMF type: " + jmfType);
codec.dump(ctx, parameterizedJmfType);
}
ctx.incrIndent(-1);
ctx.indentPrintLn("}");
}
protected String newDumpObjectArray(String componentTypeName, int length, int dimensions) {
StringBuilder sb = new StringBuilder(componentTypeName);
sb.append('[').append(length).append(']');
for (int i = 0; i < dimensions; i++)
sb.append("[]");
return sb.toString();
}
}