/************************************************************** * * 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.ByteArrayOutputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; final class Marshal { public Marshal(IBridge bridge, short cacheSize) { this.bridge = bridge; objectIdCache = new Cache(cacheSize); threadIdCache = new Cache(cacheSize); typeCache = new Cache(cacheSize); } public void write8Bit(int value) { try { output.writeByte(value); } catch (IOException e) { throw new RuntimeException(e.toString()); } } public void write16Bit(int value) { try { output.writeShort(value); } catch (IOException e) { throw new RuntimeException(e.toString()); } } public void writeObjectId(String objectId) { if (objectId == null) { writeStringValue(null); write16Bit(0xFFFF); } else { boolean[] found = new boolean[1]; int index = objectIdCache.add(found, objectId); writeStringValue(found[0] ? null : objectId); write16Bit(index); } } public void writeInterface(XInterface object, Type type) { writeObjectId((String) bridge.mapInterfaceTo(object, type)); } public void writeThreadId(ThreadId threadId) { byte[] data = threadId.getBytes(); boolean[] found = new boolean[1]; int index = threadIdCache.add(found, data); if (found[0]) { writeCompressedNumber(0); } else { writeCompressedNumber(data.length); writeBytes(data); } write16Bit(index); } public void writeType(TypeDescription type) { TypeClass typeClass = type.getTypeClass(); if (TypeDescription.isTypeClassSimple(typeClass)) { write8Bit(typeClass.getValue()); } else { boolean[] found = new boolean[1]; int index = typeCache.add(found, type.getTypeName()); write8Bit(typeClass.getValue() | (found[0] ? 0 : 0x80)); write16Bit(index); if (!found[0]) { writeStringValue(type.getTypeName()); } } } public void writeValue(TypeDescription type, Object value) { switch(type.getTypeClass().getValue()) { case TypeClass.VOID_value: break; case TypeClass.BOOLEAN_value: writeBooleanValue((Boolean) value); break; case TypeClass.BYTE_value: writeByteValue((Byte) value); break; case TypeClass.SHORT_value: case TypeClass.UNSIGNED_SHORT_value: writeShortValue((Short) value); break; case TypeClass.LONG_value: case TypeClass.UNSIGNED_LONG_value: writeLongValue((Integer) value); break; case TypeClass.HYPER_value: case TypeClass.UNSIGNED_HYPER_value: writeHyperValue((Long) value); break; case TypeClass.FLOAT_value: writeFloatValue((Float) value); break; case TypeClass.DOUBLE_value: writeDoubleValue((Double) value); break; case TypeClass.CHAR_value: writeCharValue((Character) value); break; case TypeClass.STRING_value: writeStringValue((String) value); break; case TypeClass.TYPE_value: writeTypeValue((Type) value); break; case TypeClass.ANY_value: writeAnyValue(value); break; case TypeClass.SEQUENCE_value: writeSequenceValue(type, value); break; case TypeClass.ENUM_value: writeEnumValue(type, (Enum) value); break; case TypeClass.STRUCT_value: writeStructValue(type, value); break; case TypeClass.EXCEPTION_value: writeExceptionValue(type, (Exception) value); break; case TypeClass.INTERFACE_value: writeInterfaceValue(type, (XInterface) value); break; default: throw new IllegalArgumentException("Bad type descriptor " + type); } } public byte[] reset() { byte[] data = buffer.toByteArray(); buffer.reset(); return data; } private void writeBooleanValue(Boolean value) { try { output.writeBoolean(value != null && value.booleanValue()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeByteValue(Byte value) { write8Bit(value == null ? 0 : value.byteValue()); } private void writeShortValue(Short value) { write16Bit(value == null ? 0 : value.shortValue()); } private void writeLongValue(Integer value) { write32Bit(value == null ? 0 : value.intValue()); } private void writeHyperValue(Long value) { try { output.writeLong(value == null ? 0 : value.longValue()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeFloatValue(Float value) { try { output.writeFloat(value == null ? 0 : value.floatValue()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeDoubleValue(Double value) { try { output.writeDouble(value == null ? 0 : value.doubleValue()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeCharValue(Character value) { try { output.writeChar(value == null ? 0 : value.charValue()); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeStringValue(String value) { if (value == null) { writeCompressedNumber(0); } else { byte[] data; try { data = value.getBytes("UTF8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } writeCompressedNumber(data.length); writeBytes(data); } } private void writeTypeValue(Type value) { try { writeType( TypeDescription.getTypeDescription( value == null ? Type.VOID : value)); } catch (ClassNotFoundException e) { throw new RuntimeException(e.toString()); } } private void writeAnyValue(Object value) { TypeDescription type; if (value == null || value instanceof XInterface) { type = TypeDescription.getTypeDescription(XInterface.class); } else if (value instanceof Any) { Any any = (Any) value; try { type = TypeDescription.getTypeDescription(any.getType()); } catch (ClassNotFoundException e) { throw new RuntimeException(e.toString()); } value = any.getObject(); } else if (value.getClass() == Object.class) { // Avoid StackOverflowError: throw new IllegalArgumentException( "Object instance does not represent UNO value"); } else { type = TypeDescription.getTypeDescription(value.getClass()); } writeType(type); writeValue(type, value); } private void writeSequenceValue(TypeDescription type, Object value) { if (value == null) { writeCompressedNumber(0); } else { TypeDescription ctype = (TypeDescription) type.getComponentType(); if (ctype.getTypeClass() == TypeClass.BYTE) { byte[] data = (byte[]) value; writeCompressedNumber(data.length); writeBytes(data); } else { int len = Array.getLength(value); writeCompressedNumber(len); for (int i = 0; i < len; ++i) { writeValue(ctype, Array.get(value, i)); } } } } private void writeEnumValue(TypeDescription type, Enum value) { int n; if (value == null) { try { n = ((Enum) (type.getZClass().getMethod("getDefault", null). invoke(null, null))). getValue(); } catch (IllegalAccessException e) { throw new RuntimeException(e.toString()); } catch (InvocationTargetException e) { throw new RuntimeException(e.toString()); } catch (NoSuchMethodException e) { throw new RuntimeException(e.toString()); } } else { n = value.getValue(); } write32Bit(n); } private void writeStructValue(TypeDescription type, Object value) { IFieldDescription[] fields = type.getFieldDescriptions(); for (int i = 0; i < fields.length; ++i) { try { writeValue( (TypeDescription) fields[i].getTypeDescription(), value == null ? null : fields[i].getField().get(value)); } catch (IllegalAccessException e) { throw new RuntimeException(e.toString()); } } } private void writeExceptionValue(TypeDescription type, Exception value) { writeStringValue(value == null ? null : value.getMessage()); writeStructValue(type, value); } private void writeInterfaceValue(TypeDescription type, XInterface value) { writeInterface(value, new Type(type)); } private void write32Bit(int value) { try { output.writeInt(value); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private void writeCompressedNumber(int number) { if (number >= 0 && number < 0xFF) { write8Bit(number); } else { write8Bit(0xFF); write32Bit(number); } } private void writeBytes(byte[] data) { try { output.write(data); } catch (IOException e) { throw new RuntimeException(e.toString()); } } private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); private final DataOutput output = new DataOutputStream(buffer); private final IBridge bridge; private final Cache objectIdCache; private final Cache threadIdCache; private final Cache typeCache; }