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.ByteArrayInputStream; 22 import java.io.DataInputStream; 23 import java.io.IOException; 24 import java.io.UnsupportedEncodingException; 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.TypeDescription; 30 import com.sun.star.uno.Any; 31 import com.sun.star.uno.Enum; 32 import com.sun.star.uno.IBridge; 33 import com.sun.star.uno.Type; 34 import com.sun.star.uno.TypeClass; 35 import com.sun.star.uno.XInterface; 36 import com.sun.star.lib.uno.typedesc.FieldDescription; 37 38 final class Unmarshal { Unmarshal(IBridge bridge, int cacheSize)39 public Unmarshal(IBridge bridge, int cacheSize) { 40 this.bridge = bridge; 41 objectIdCache = new String[cacheSize]; 42 threadIdCache = new ThreadId[cacheSize]; 43 typeCache = new TypeDescription[cacheSize]; 44 reset(new byte[0]); 45 } 46 read8Bit()47 public int read8Bit() { 48 try { 49 return input.readUnsignedByte(); 50 } catch (IOException e) { 51 throw new RuntimeException(e); 52 } 53 } 54 read16Bit()55 public int read16Bit() { 56 try { 57 return input.readUnsignedShort(); 58 } catch (IOException e) { 59 throw new RuntimeException(e); 60 } 61 } 62 readObjectId()63 public String readObjectId() { 64 try { 65 String id = readStringValue(); 66 int index = read16Bit(); 67 if (index == 0xFFFF) { 68 if (id.length() == 0) { 69 id = null; 70 } 71 } else { 72 if (id.length() == 0) { 73 id = objectIdCache[index]; 74 } else { 75 objectIdCache[index] = id; 76 } 77 } 78 return id; 79 } catch (IOException e) { 80 throw new RuntimeException(e); 81 } 82 } 83 readInterface(Type type)84 public Object readInterface(Type type) { 85 String id = readObjectId(); 86 return id == null ? null : bridge.mapInterfaceFrom(id, type); 87 } 88 readThreadId()89 public ThreadId readThreadId() { 90 try { 91 int len = readCompressedNumber(); 92 byte[] data ; 93 ThreadId id = null; 94 if (len != 0) { 95 data = new byte[len]; 96 readBytes(data); 97 id = new ThreadId(data); 98 } 99 int index = read16Bit(); 100 if (index != 0xFFFF) { 101 if (len == 0) { 102 id = threadIdCache[index]; 103 } else { 104 threadIdCache[index] = id; 105 } 106 } 107 return id; 108 } catch (IOException e) { 109 throw new RuntimeException(e); 110 } 111 } 112 readType()113 public TypeDescription readType() { 114 int b = read8Bit(); 115 TypeClass typeClass = TypeClass.fromInt(b & 0x7F); 116 if (typeClass == null) { 117 throw new RuntimeException( 118 "Reading TYPE with bad type class " + (b & 0x7F)); 119 } 120 if (TypeDescription.isTypeClassSimple(typeClass)) { 121 if ((b & 0x80) != 0) { 122 throw new RuntimeException( 123 "Reading TYPE with bad type class/cache flag " + b); 124 } 125 return TypeDescription.getTypeDescription(typeClass); 126 } else { 127 int index = read16Bit(); 128 TypeDescription type; 129 if ((b & 0x80) == 0) { 130 if (index >= typeCache.length) { 131 throw new RuntimeException( 132 "Reading TYPE with bad cache index " + index); 133 } 134 type = typeCache[index]; 135 if (type == null) { 136 throw new RuntimeException( 137 "Reading TYPE with empty cache index " + index); 138 } 139 } else { 140 try { 141 type = TypeDescription.getTypeDescription( 142 readStringValue()); 143 } catch (IOException e) { 144 throw new RuntimeException(e); 145 } catch (ClassNotFoundException e) { 146 throw new RuntimeException(e); 147 } 148 if (index != 0xFFFF) { 149 if (index >= typeCache.length) { 150 throw new RuntimeException( 151 "Reading TYPE with bad cache index " + index); 152 } 153 typeCache[index] = type; 154 } 155 } 156 return type; 157 } 158 } 159 readValue(TypeDescription type)160 public Object readValue(TypeDescription type) { 161 try { 162 switch (type.getTypeClass().getValue()) { 163 case TypeClass.VOID_value: 164 return null; 165 166 case TypeClass.BOOLEAN_value: 167 return readBooleanValue(); 168 169 case TypeClass.BYTE_value: 170 return readByteValue(); 171 172 case TypeClass.SHORT_value: 173 case TypeClass.UNSIGNED_SHORT_value: 174 return readShortValue(); 175 176 case TypeClass.LONG_value: 177 case TypeClass.UNSIGNED_LONG_value: 178 return readLongValue(); 179 180 case TypeClass.HYPER_value: 181 case TypeClass.UNSIGNED_HYPER_value: 182 return readHyperValue(); 183 184 case TypeClass.FLOAT_value: 185 return readFloatValue(); 186 187 case TypeClass.DOUBLE_value: 188 return readDoubleValue(); 189 190 case TypeClass.CHAR_value: 191 return readCharValue(); 192 193 case TypeClass.STRING_value: 194 return readStringValue(); 195 196 case TypeClass.TYPE_value: 197 return readTypeValue(); 198 199 case TypeClass.ANY_value: 200 return readAnyValue(); 201 202 case TypeClass.SEQUENCE_value: 203 return readSequenceValue(type); 204 205 case TypeClass.ENUM_value: 206 return readEnumValue(type); 207 208 case TypeClass.STRUCT_value: 209 return readStructValue(type); 210 211 case TypeClass.EXCEPTION_value: 212 return readExceptionValue(type); 213 214 case TypeClass.INTERFACE_value: 215 return readInterfaceValue(type); 216 217 default: 218 throw new IllegalArgumentException("Bad type descriptor " + type); 219 } 220 } catch (IOException e) { 221 throw new RuntimeException(e); 222 } 223 } 224 hasMore()225 public boolean hasMore() { 226 try { 227 return input.available() > 0; 228 } catch (IOException e) { 229 throw new RuntimeException(e); 230 } 231 } 232 reset(byte[] data)233 public void reset(byte[] data) { 234 input = new DataInputStream(new ByteArrayInputStream(data)); 235 } 236 readBooleanValue()237 private Boolean readBooleanValue() throws IOException { 238 return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; 239 } 240 readByteValue()241 private Byte readByteValue() throws IOException { 242 return Byte.valueOf(input.readByte()); 243 } 244 readShortValue()245 private Short readShortValue() throws IOException { 246 return Short.valueOf(input.readShort()); 247 } 248 readLongValue()249 private Integer readLongValue() throws IOException { 250 return Integer.valueOf(input.readInt()); 251 } 252 readHyperValue()253 private Long readHyperValue() throws IOException { 254 return Long.valueOf(input.readLong()); 255 } 256 readFloatValue()257 private Float readFloatValue() throws IOException { 258 return new Float(input.readFloat()); 259 } 260 readDoubleValue()261 private Double readDoubleValue() throws IOException { 262 return new Double(input.readDouble()); 263 } 264 readCharValue()265 private Character readCharValue() throws IOException { 266 return new Character(input.readChar()); 267 } 268 readStringValue()269 private String readStringValue() throws IOException { 270 int len = readCompressedNumber(); 271 byte[] data = new byte[len]; 272 readBytes(data); 273 try { 274 return new String(data, "UTF8"); 275 } catch (UnsupportedEncodingException e) { 276 throw new RuntimeException(e); 277 } 278 } 279 readTypeValue()280 private Type readTypeValue() { 281 return new Type(readType()); 282 } 283 readAnyValue()284 private Object readAnyValue() throws IOException { 285 TypeDescription type = readType(); 286 switch (type.getTypeClass().getValue()) { 287 case TypeClass.VOID_value: 288 return Any.VOID; 289 290 case TypeClass.BOOLEAN_value: 291 return readBooleanValue(); 292 293 case TypeClass.BYTE_value: 294 return readByteValue(); 295 296 case TypeClass.SHORT_value: 297 return readShortValue(); 298 299 case TypeClass.UNSIGNED_SHORT_value: 300 return new Any(Type.UNSIGNED_SHORT, readShortValue()); 301 302 case TypeClass.LONG_value: 303 return readLongValue(); 304 305 case TypeClass.UNSIGNED_LONG_value: 306 return new Any(Type.UNSIGNED_LONG, readLongValue()); 307 308 case TypeClass.HYPER_value: 309 return readHyperValue(); 310 311 case TypeClass.UNSIGNED_HYPER_value: 312 return new Any(Type.UNSIGNED_HYPER, readHyperValue()); 313 314 case TypeClass.FLOAT_value: 315 return readFloatValue(); 316 317 case TypeClass.DOUBLE_value: 318 return readDoubleValue(); 319 320 case TypeClass.CHAR_value: 321 return readCharValue(); 322 323 case TypeClass.STRING_value: 324 return readStringValue(); 325 326 case TypeClass.TYPE_value: 327 return readTypeValue(); 328 329 case TypeClass.SEQUENCE_value: 330 { 331 Object value = readSequenceValue(type); 332 TypeDescription ctype = type.getComponentType(); 333 while (ctype.getTypeClass() == TypeClass.SEQUENCE) { 334 ctype = ctype.getComponentType(); 335 } 336 switch (ctype.getTypeClass().getValue()) { 337 case TypeClass.UNSIGNED_SHORT_value: 338 case TypeClass.UNSIGNED_LONG_value: 339 case TypeClass.UNSIGNED_HYPER_value: 340 return new Any(new Type(type), value); 341 342 case TypeClass.STRUCT_value: 343 if (ctype.hasTypeArguments()) { 344 return new Any(new Type(type), value); 345 } 346 default: 347 return value; 348 } 349 } 350 351 case TypeClass.ENUM_value: 352 return readEnumValue(type); 353 354 case TypeClass.STRUCT_value: 355 { 356 Object value = readStructValue(type); 357 return type.hasTypeArguments() 358 ? new Any(new Type(type), value) : value; 359 } 360 361 case TypeClass.EXCEPTION_value: 362 return readExceptionValue(type); 363 364 case TypeClass.INTERFACE_value: 365 { 366 Object value = readInterfaceValue(type); 367 return type.getZClass() == XInterface.class 368 ? value : new Any(new Type(type), value); 369 } 370 371 default: 372 throw new RuntimeException( 373 "Reading ANY with bad type " + type.getTypeClass()); 374 } 375 } 376 readSequenceValue(TypeDescription type)377 private Object readSequenceValue(TypeDescription type) throws IOException { 378 int len = readCompressedNumber(); 379 TypeDescription ctype = type.getComponentType(); 380 if (ctype.getTypeClass() == TypeClass.BYTE) { 381 byte[] data = new byte[len]; 382 readBytes(data); 383 return data; 384 } else { 385 Object value = Array.newInstance( 386 ctype.getTypeClass() == TypeClass.ANY 387 ? Object.class : ctype.getZClass(), len); 388 for (int i = 0; i < len; ++i) { 389 Array.set(value, i, readValue(ctype)); 390 } 391 return value; 392 } 393 } 394 readEnumValue(TypeDescription type)395 private Enum readEnumValue(TypeDescription type) throws IOException { 396 try { 397 return (Enum) 398 type.getZClass().getMethod( 399 "fromInt", new Class[] { int.class }). 400 invoke(null, new Object[] { readLongValue() }); 401 } catch (IllegalAccessException e) { 402 throw new RuntimeException(e); 403 } catch (InvocationTargetException e) { 404 throw new RuntimeException(e); 405 } catch (NoSuchMethodException e) { 406 throw new RuntimeException(e); 407 } 408 } 409 readStructValue(TypeDescription type)410 private Object readStructValue(TypeDescription type) { 411 Object value; 412 try { 413 value = type.getZClass().newInstance(); 414 } catch (IllegalAccessException e) { 415 throw new RuntimeException(e); 416 } catch (InstantiationException e) { 417 throw new RuntimeException(e); 418 } 419 readFields(type, value); 420 return value; 421 } 422 readExceptionValue(TypeDescription type)423 private Exception readExceptionValue(TypeDescription type) throws IOException { 424 Exception value; 425 try { 426 value = (Exception) 427 type.getZClass().getConstructor(new Class[] { String.class }). 428 newInstance(new Object[] { readStringValue() }); 429 } catch (IllegalAccessException e) { 430 throw new RuntimeException(e); 431 } catch (InstantiationException e) { 432 throw new RuntimeException(e); 433 } catch (InvocationTargetException e) { 434 throw new RuntimeException(e); 435 } catch (NoSuchMethodException e) { 436 throw new RuntimeException(e); 437 } 438 readFields(type, value); 439 return value; 440 } 441 readInterfaceValue(TypeDescription type)442 private Object readInterfaceValue(TypeDescription type) { 443 return readInterface(new Type(type)); 444 } 445 readCompressedNumber()446 private int readCompressedNumber() throws IOException { 447 int number = read8Bit(); 448 return number < 0xFF ? number : input.readInt(); 449 } 450 readBytes(byte[] data)451 private void readBytes(byte[] data) throws IOException { 452 input.readFully(data); 453 } 454 readFields(TypeDescription type, Object value)455 private void readFields(TypeDescription type, Object value) { 456 FieldDescription[] fields = type.getFieldDescriptions(); 457 for (int i = 0; i < fields.length; ++i) { 458 try { 459 fields[i].getField().set( 460 value, 461 readValue( 462 fields[i].getTypeDescription())); 463 } catch (IllegalAccessException e) { 464 throw new RuntimeException(e); 465 } 466 } 467 } 468 469 private final IBridge bridge; 470 private final String[] objectIdCache; 471 private final ThreadId[] threadIdCache; 472 private final TypeDescription[] typeCache; 473 private DataInputStream input; 474 } 475 476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 477