blob: c4507e488ee1218014e8764af61f3f63b00b0520 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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 ;
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;
}