1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 package com.sun.star.lib.uno.protocols.urp; 20 21 import java.io.ByteArrayOutputStream; 22 import java.io.DataOutput; 23 import java.io.DataOutputStream; 24 import java.io.IOException; 25 import java.lang.reflect.Array; 26 import java.lang.reflect.InvocationTargetException; 27 28 import com.sun.star.lib.uno.environments.remote.ThreadId; 29 import com.sun.star.lib.uno.typedesc.FieldDescription; 30 import com.sun.star.lib.uno.typedesc.TypeDescription; 31 import com.sun.star.uno.Any; 32 import com.sun.star.uno.Enum; 33 import com.sun.star.uno.IBridge; 34 import com.sun.star.uno.Type; 35 import com.sun.star.uno.TypeClass; 36 import com.sun.star.uno.XInterface; 37 38 final class Marshal { Marshal(IBridge bridge, short cacheSize)39 public Marshal(IBridge bridge, short cacheSize) { 40 this.bridge = bridge; 41 objectIdCache = new Cache(cacheSize); 42 threadIdCache = new Cache(cacheSize); 43 typeCache = new Cache(cacheSize); 44 } 45 write8Bit(int value)46 public void write8Bit(int value) { 47 try { 48 output.writeByte(value); 49 } catch (IOException e) { 50 throw new RuntimeException(e); 51 } 52 } 53 write16Bit(int value)54 public void write16Bit(int value) { 55 try { 56 output.writeShort(value); 57 } catch (IOException e) { 58 throw new RuntimeException(e); 59 } 60 } 61 writeObjectId(String objectId)62 public void writeObjectId(String objectId) { 63 try { 64 if (objectId == null) { 65 writeStringValue(null); 66 write16Bit(0xFFFF); 67 } else { 68 boolean[] found = new boolean[1]; 69 int index = objectIdCache.add(found, objectId); 70 writeStringValue(found[0] ? null : objectId); 71 write16Bit(index); 72 } 73 } catch (IOException e) { 74 throw new RuntimeException(e); 75 } 76 } 77 writeInterface(XInterface object, Type type)78 public void writeInterface(XInterface object, Type type) { 79 writeObjectId((String) bridge.mapInterfaceTo(object, type)); 80 } 81 writeThreadId(ThreadId threadId)82 public void writeThreadId(ThreadId threadId) { 83 try { 84 byte[] data = threadId.getBytes(); 85 boolean[] found = new boolean[1]; 86 int index = threadIdCache.add(found, data); 87 if (found[0]) { 88 writeCompressedNumber(0); 89 } else { 90 writeCompressedNumber(data.length); 91 writeBytes(data); 92 } 93 write16Bit(index); 94 } catch (IOException e) { 95 throw new RuntimeException(e); 96 } 97 } 98 writeType(TypeDescription type)99 public void writeType(TypeDescription type) { 100 try { 101 TypeClass typeClass = type.getTypeClass(); 102 if (TypeDescription.isTypeClassSimple(typeClass)) { 103 write8Bit(typeClass.getValue()); 104 } else { 105 boolean[] found = new boolean[1]; 106 int index = typeCache.add(found, type.getTypeName()); 107 write8Bit(typeClass.getValue() | (found[0] ? 0 : 0x80)); 108 write16Bit(index); 109 if (!found[0]) { 110 writeStringValue(type.getTypeName()); 111 } 112 } 113 } catch (IOException e) { 114 throw new RuntimeException(e); 115 } 116 } 117 writeValue(TypeDescription type, Object value)118 public void writeValue(TypeDescription type, Object value) { 119 try { 120 switch(type.getTypeClass().getValue()) { 121 case TypeClass.VOID_value: 122 break; 123 124 case TypeClass.BOOLEAN_value: 125 writeBooleanValue((Boolean) value); 126 break; 127 128 case TypeClass.BYTE_value: 129 writeByteValue((Byte) value); 130 break; 131 132 case TypeClass.SHORT_value: 133 case TypeClass.UNSIGNED_SHORT_value: 134 writeShortValue((Short) value); 135 break; 136 137 case TypeClass.LONG_value: 138 case TypeClass.UNSIGNED_LONG_value: 139 writeLongValue((Integer) value); 140 break; 141 142 case TypeClass.HYPER_value: 143 case TypeClass.UNSIGNED_HYPER_value: 144 writeHyperValue((Long) value); 145 break; 146 147 case TypeClass.FLOAT_value: 148 writeFloatValue((Float) value); 149 break; 150 151 case TypeClass.DOUBLE_value: 152 writeDoubleValue((Double) value); 153 break; 154 155 case TypeClass.CHAR_value: 156 writeCharValue((Character) value); 157 break; 158 159 case TypeClass.STRING_value: 160 writeStringValue((String) value); 161 break; 162 163 case TypeClass.TYPE_value: 164 writeTypeValue((Type) value); 165 break; 166 167 case TypeClass.ANY_value: 168 writeAnyValue(value); 169 break; 170 171 case TypeClass.SEQUENCE_value: 172 writeSequenceValue(type, value); 173 break; 174 175 case TypeClass.ENUM_value: 176 writeEnumValue(type, (Enum) value); 177 break; 178 179 case TypeClass.STRUCT_value: 180 writeStructValue(type, value); 181 break; 182 183 case TypeClass.EXCEPTION_value: 184 writeExceptionValue(type, (Exception) value); 185 break; 186 187 case TypeClass.INTERFACE_value: 188 writeInterfaceValue(type, (XInterface) value); 189 break; 190 191 default: 192 throw new IllegalArgumentException("Bad type descriptor " + type); 193 } 194 } catch (ClassNotFoundException e) { 195 throw new RuntimeException(e); 196 } catch (IllegalAccessException e) { 197 throw new RuntimeException(e); 198 } catch (IOException e) { 199 throw new RuntimeException(e); 200 } catch (InvocationTargetException e) { 201 throw new RuntimeException(e); 202 } catch (NoSuchMethodException e) { 203 throw new RuntimeException(e); 204 } 205 } 206 reset()207 public byte[] reset() { 208 byte[] data = buffer.toByteArray(); 209 buffer.reset(); 210 return data; 211 } 212 writeBooleanValue(Boolean value)213 private void writeBooleanValue(Boolean value) throws IOException { 214 output.writeBoolean(value != null && value.booleanValue()); 215 } 216 writeByteValue(Byte value)217 private void writeByteValue(Byte value) { 218 write8Bit(value == null ? 0 : value.byteValue()); 219 } 220 writeShortValue(Short value)221 private void writeShortValue(Short value) { 222 write16Bit(value == null ? 0 : value.shortValue()); 223 } 224 writeLongValue(Integer value)225 private void writeLongValue(Integer value) throws IOException { 226 write32Bit(value == null ? 0 : value.intValue()); 227 } 228 writeHyperValue(Long value)229 private void writeHyperValue(Long value) throws IOException { 230 output.writeLong(value == null ? 0 : value.longValue()); 231 } 232 writeFloatValue(Float value)233 private void writeFloatValue(Float value) throws IOException { 234 output.writeFloat(value == null ? 0 : value.floatValue()); 235 } 236 writeDoubleValue(Double value)237 private void writeDoubleValue(Double value) throws IOException { 238 output.writeDouble(value == null ? 0 : value.doubleValue()); 239 } 240 writeCharValue(Character value)241 private void writeCharValue(Character value) throws IOException { 242 output.writeChar(value == null ? 0 : value.charValue()); 243 } 244 writeStringValue(String value)245 private void writeStringValue(String value) throws IOException { 246 if (value == null) { 247 writeCompressedNumber(0); 248 } else { 249 byte[] data = value.getBytes("UTF8"); 250 writeCompressedNumber(data.length); 251 writeBytes(data); 252 } 253 } 254 writeTypeValue(Type value)255 private void writeTypeValue(Type value) throws ClassNotFoundException { 256 writeType( 257 TypeDescription.getTypeDescription( 258 value == null ? Type.VOID : value)); 259 } 260 writeAnyValue(Object value)261 private void writeAnyValue(Object value) throws ClassNotFoundException { 262 TypeDescription type; 263 if (value == null || value instanceof XInterface) { 264 type = TypeDescription.getTypeDescription(XInterface.class); 265 } else if (value instanceof Any) { 266 Any any = (Any) value; 267 type = TypeDescription.getTypeDescription(any.getType()); 268 value = any.getObject(); 269 } else if (value.getClass() == Object.class) { 270 // Avoid StackOverflowError: 271 throw new IllegalArgumentException( 272 "Object instance does not represent UNO value"); 273 } else { 274 type = TypeDescription.getTypeDescription(value.getClass()); 275 } 276 writeType(type); 277 writeValue(type, value); 278 } 279 writeSequenceValue(TypeDescription type, Object value)280 private void writeSequenceValue(TypeDescription type, Object value) throws IOException { 281 if (value == null) { 282 writeCompressedNumber(0); 283 } else { 284 TypeDescription ctype = type.getComponentType(); 285 if (ctype.getTypeClass() == TypeClass.BYTE) { 286 byte[] data = (byte[]) value; 287 writeCompressedNumber(data.length); 288 writeBytes(data); 289 } else { 290 int len = Array.getLength(value); 291 writeCompressedNumber(len); 292 for (int i = 0; i < len; ++i) { 293 writeValue(ctype, Array.get(value, i)); 294 } 295 } 296 } 297 } 298 writeEnumValue(TypeDescription type, Enum value)299 private void writeEnumValue(TypeDescription type, Enum value) throws IllegalAccessException, IOException, InvocationTargetException, NoSuchMethodException { 300 int n; 301 if (value == null) { 302 n = ((Enum) 303 (type.getZClass().getMethod("getDefault", (Class[]) null). 304 invoke(null, (Object[]) null))). 305 getValue(); 306 } else { 307 n = value.getValue(); 308 } 309 write32Bit(n); 310 } 311 writeStructValue(TypeDescription type, Object value)312 private void writeStructValue(TypeDescription type, Object value) throws IllegalAccessException { 313 FieldDescription[] fields = type.getFieldDescriptions(); 314 for (int i = 0; i < fields.length; ++i) { 315 writeValue( 316 fields[i].getTypeDescription(), 317 value == null ? null : fields[i].getField().get(value)); 318 } 319 } 320 writeExceptionValue(TypeDescription type, Exception value)321 private void writeExceptionValue(TypeDescription type, Exception value) throws IllegalAccessException, IOException { 322 writeStringValue(value == null ? null : value.getMessage()); 323 writeStructValue(type, value); 324 } 325 writeInterfaceValue(TypeDescription type, XInterface value)326 private void writeInterfaceValue(TypeDescription type, XInterface value) { 327 writeInterface(value, new Type(type)); 328 } 329 write32Bit(int value)330 private void write32Bit(int value) throws IOException { 331 output.writeInt(value); 332 } 333 writeCompressedNumber(int number)334 private void writeCompressedNumber(int number) throws IOException { 335 if (number >= 0 && number < 0xFF) { 336 write8Bit(number); 337 } else { 338 write8Bit(0xFF); 339 write32Bit(number); 340 } 341 } 342 writeBytes(byte[] data)343 private void writeBytes(byte[] data) throws IOException { 344 output.write(data); 345 } 346 347 private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 348 private final DataOutput output = new DataOutputStream(buffer); 349 private final IBridge bridge; 350 private final Cache objectIdCache; 351 private final Cache threadIdCache; 352 private final Cache typeCache; 353 } 354 355 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 356