/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: Unmarshal.java,v $
* $Revision: 1.18 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
package com.sun.star.lib.uno.protocols.urp;
import com.sun.star.lib.uno.environments.remote.ThreadId;
import com.sun.star.lib.uno.typedesc.TypeDescription;
import com.sun.star.uno.Any;
import com.sun.star.uno.Enum;
import com.sun.star.uno.IBridge;
import com.sun.star.uno.IFieldDescription;
import com.sun.star.uno.Type;
import com.sun.star.uno.TypeClass;
import com.sun.star.uno.XInterface;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
final class Unmarshal {
public Unmarshal(IBridge bridge, int cacheSize) {
this.bridge = bridge;
objectIdCache = new String[cacheSize];
threadIdCache = new ThreadId[cacheSize];
typeCache = new TypeDescription[cacheSize];
reset(new byte[0]);
}
public int read8Bit() {
try {
return input.readUnsignedByte();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
public int read16Bit() {
try {
return input.readUnsignedShort();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
public String readObjectId() {
String id = readStringValue();
int index = read16Bit();
if (index == 0xFFFF) {
if (id.length() == 0) {
id = null;
}
} else {
if (id.length() == 0) {
id = objectIdCache[index];
} else {
objectIdCache[index] = id;
}
}
return id;
}
public Object readInterface(Type type) {
String id = readObjectId();
return id == null ? null : bridge.mapInterfaceFrom(id, type);
}
public ThreadId readThreadId() {
int len = readCompressedNumber();
byte[] data = null;
ThreadId id = null;
if (len != 0) {
data = new byte[len];
readBytes(data);
id = new ThreadId(data);
}
int index = read16Bit();
if (index != 0xFFFF) {
if (len == 0) {
id = threadIdCache[index];
} else {
threadIdCache[index] = id;
}
}
return id;
}
public TypeDescription readType() {
int b = read8Bit();
TypeClass typeClass = TypeClass.fromInt(b & 0x7F);
if (TypeDescription.isTypeClassSimple(typeClass)) {
return TypeDescription.getTypeDescription(typeClass);
} else {
int index = read16Bit();
TypeDescription type = null;
if ((b & 0x80) != 0) {
try {
type = TypeDescription.getTypeDescription(
readStringValue());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.toString());
}
}
if (index != 0xFFFF) {
if ((b & 0x80) == 0) {
type = typeCache[index];
} else {
typeCache[index] = type;
}
}
return type;
}
}
public Object readValue(TypeDescription type) {
switch (type.getTypeClass().getValue()) {
case TypeClass.VOID_value:
return null;
case TypeClass.BOOLEAN_value:
return readBooleanValue();
case TypeClass.BYTE_value:
return readByteValue();
case TypeClass.SHORT_value:
case TypeClass.UNSIGNED_SHORT_value:
return readShortValue();
case TypeClass.LONG_value:
case TypeClass.UNSIGNED_LONG_value:
return readLongValue();
case TypeClass.HYPER_value:
case TypeClass.UNSIGNED_HYPER_value:
return readHyperValue();
case TypeClass.FLOAT_value:
return readFloatValue();
case TypeClass.DOUBLE_value:
return readDoubleValue();
case TypeClass.CHAR_value:
return readCharValue();
case TypeClass.STRING_value:
return readStringValue();
case TypeClass.TYPE_value:
return readTypeValue();
case TypeClass.ANY_value:
return readAnyValue();
case TypeClass.SEQUENCE_value:
return readSequenceValue(type);
case TypeClass.ENUM_value:
return readEnumValue(type);
case TypeClass.STRUCT_value:
return readStructValue(type);
case TypeClass.EXCEPTION_value:
return readExceptionValue(type);
case TypeClass.INTERFACE_value:
return readInterfaceValue(type);
default:
throw new IllegalArgumentException("Bad type descriptor " + type);
}
}
public boolean hasMore() {
try {
return input.available() > 0;
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
public void reset(byte[] data) {
input = new DataInputStream(new ByteArrayInputStream(data));
}
private Boolean readBooleanValue() {
try {
return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Byte readByteValue() {
try {
return new Byte(input.readByte());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Short readShortValue() {
try {
return new Short(input.readShort());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Integer readLongValue() {
try {
return new Integer(input.readInt());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Long readHyperValue() {
try {
return new Long(input.readLong());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Float readFloatValue() {
try {
return new Float(input.readFloat());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Double readDoubleValue() {
try {
return new Double(input.readDouble());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private Character readCharValue() {
try {
return new Character(input.readChar());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private String readStringValue() {
int len = readCompressedNumber();
byte[] data = new byte[len];
readBytes(data);
try {
return new String(data, "UTF8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.toString());
}
}
private Type readTypeValue() {
return new Type(readType());
}
private Object readAnyValue() {
TypeDescription type = readType();
switch (type.getTypeClass().getValue()) {
case TypeClass.VOID_value:
return Any.VOID;
case TypeClass.BOOLEAN_value:
return readBooleanValue();
case TypeClass.BYTE_value:
return readByteValue();
case TypeClass.SHORT_value:
return readShortValue();
case TypeClass.UNSIGNED_SHORT_value:
return new Any(Type.UNSIGNED_SHORT, readShortValue());
case TypeClass.LONG_value:
return readLongValue();
case TypeClass.UNSIGNED_LONG_value:
return new Any(Type.UNSIGNED_LONG, readLongValue());
case TypeClass.HYPER_value:
return readHyperValue();
case TypeClass.UNSIGNED_HYPER_value:
return new Any(Type.UNSIGNED_HYPER, readHyperValue());
case TypeClass.FLOAT_value:
return readFloatValue();
case TypeClass.DOUBLE_value:
return readDoubleValue();
case TypeClass.CHAR_value:
return readCharValue();
case TypeClass.STRING_value:
return readStringValue();
case TypeClass.TYPE_value:
return readTypeValue();
case TypeClass.SEQUENCE_value:
{
Object value = readSequenceValue(type);
TypeDescription ctype = (TypeDescription)
type.getComponentType();
while (ctype.getTypeClass() == TypeClass.SEQUENCE) {
ctype = (TypeDescription) ctype.getComponentType();
}
switch (ctype.getTypeClass().getValue()) {
case TypeClass.UNSIGNED_SHORT_value:
case TypeClass.UNSIGNED_LONG_value:
case TypeClass.UNSIGNED_HYPER_value:
return new Any(new Type(type), value);
case TypeClass.STRUCT_value:
if (ctype.hasTypeArguments()) {
return new Any(new Type(type), value);
}
default:
return value;
}
}
case TypeClass.ENUM_value:
return readEnumValue(type);
case TypeClass.STRUCT_value:
{
Object value = readStructValue(type);
return type.hasTypeArguments()
? new Any(new Type(type), value) : value;
}
case TypeClass.EXCEPTION_value:
return readExceptionValue(type);
case TypeClass.INTERFACE_value:
{
Object value = readInterfaceValue(type);
return type.getZClass() == XInterface.class
? value : new Any(new Type(type), value);
}
default:
throw new RuntimeException(
"Reading ANY with bad type " + type.getTypeClass());
}
}
private Object readSequenceValue(TypeDescription type) {
int len = readCompressedNumber();
TypeDescription ctype = (TypeDescription) type.getComponentType();
if (ctype.getTypeClass() == TypeClass.BYTE) {
byte[] data = new byte[len];
readBytes(data);
return data;
} else {
Object value = Array.newInstance(
ctype.getTypeClass() == TypeClass.ANY
? Object.class : ctype.getZClass(), len);
for (int i = 0; i < len; ++i) {
Array.set(value, i, readValue(ctype));
}
return value;
}
}
private Enum readEnumValue(TypeDescription type) {
try {
return (Enum)
type.getZClass().getMethod(
"fromInt", new Class[] { int.class }).
invoke(null, new Object[] { readLongValue() });
} catch (IllegalAccessException e) {
throw new RuntimeException(e.toString());
} catch (InvocationTargetException e) {
throw new RuntimeException(e.toString());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e.toString());
}
}
private Object readStructValue(TypeDescription type) {
Object value;
try {
value = type.getZClass().newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.toString());
} catch (InstantiationException e) {
throw new RuntimeException(e.toString());
}
readFields(type, value);
return value;
}
private Exception readExceptionValue(TypeDescription type) {
Exception value;
try {
value = (Exception)
type.getZClass().getConstructor(new Class[] { String.class }).
newInstance(new Object[] { readStringValue() });
} catch (IllegalAccessException e) {
throw new RuntimeException(e.toString());
} catch (InstantiationException e) {
throw new RuntimeException(e.toString());
} catch (InvocationTargetException e) {
throw new RuntimeException(e.toString());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e.toString());
}
readFields(type, value);
return value;
}
private Object readInterfaceValue(TypeDescription type) {
return readInterface(new Type(type));
}
private int readCompressedNumber() {
int number = read8Bit();
try {
return number < 0xFF ? number : input.readInt();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private void readBytes(byte[] data) {
try {
input.readFully(data);
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private void readFields(TypeDescription type, Object value) {
IFieldDescription[] fields = type.getFieldDescriptions();
for (int i = 0; i < fields.length; ++i) {
try {
fields[i].getField().set(
value,
readValue(
(TypeDescription) fields[i].getTypeDescription()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e.toString());
}
}
}
private final IBridge bridge;
private final String[] objectIdCache;
private final ThreadId[] threadIdCache;
private final TypeDescription[] typeCache;
private DataInputStream input;
}