1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 package com.sun.star.lib.uno.protocols.urp; 28 29 import com.sun.star.lib.uno.environments.remote.ThreadId; 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.IFieldDescription; 35 import com.sun.star.uno.Type; 36 import com.sun.star.uno.TypeClass; 37 import com.sun.star.uno.XInterface; 38 import java.io.ByteArrayOutputStream; 39 import java.io.DataOutput; 40 import java.io.DataOutputStream; 41 import java.io.IOException; 42 import java.io.UnsupportedEncodingException; 43 import java.lang.reflect.Array; 44 import java.lang.reflect.InvocationTargetException; 45 46 final class Marshal { 47 public Marshal(IBridge bridge, short cacheSize) { 48 this.bridge = bridge; 49 objectIdCache = new Cache(cacheSize); 50 threadIdCache = new Cache(cacheSize); 51 typeCache = new Cache(cacheSize); 52 } 53 54 public void write8Bit(int value) { 55 try { 56 output.writeByte(value); 57 } catch (IOException e) { 58 throw new RuntimeException(e.toString()); 59 } 60 } 61 62 public void write16Bit(int value) { 63 try { 64 output.writeShort(value); 65 } catch (IOException e) { 66 throw new RuntimeException(e.toString()); 67 } 68 } 69 70 public void writeObjectId(String objectId) { 71 if (objectId == null) { 72 writeStringValue(null); 73 write16Bit(0xFFFF); 74 } else { 75 boolean[] found = new boolean[1]; 76 int index = objectIdCache.add(found, objectId); 77 writeStringValue(found[0] ? null : objectId); 78 write16Bit(index); 79 } 80 } 81 82 public void writeInterface(XInterface object, Type type) { 83 writeObjectId((String) bridge.mapInterfaceTo(object, type)); 84 } 85 86 public void writeThreadId(ThreadId threadId) { 87 byte[] data = threadId.getBytes(); 88 boolean[] found = new boolean[1]; 89 int index = threadIdCache.add(found, data); 90 if (found[0]) { 91 writeCompressedNumber(0); 92 } else { 93 writeCompressedNumber(data.length); 94 writeBytes(data); 95 } 96 write16Bit(index); 97 } 98 99 public void writeType(TypeDescription type) { 100 TypeClass typeClass = type.getTypeClass(); 101 if (TypeDescription.isTypeClassSimple(typeClass)) { 102 write8Bit(typeClass.getValue()); 103 } else { 104 boolean[] found = new boolean[1]; 105 int index = typeCache.add(found, type.getTypeName()); 106 write8Bit(typeClass.getValue() | (found[0] ? 0 : 0x80)); 107 write16Bit(index); 108 if (!found[0]) { 109 writeStringValue(type.getTypeName()); 110 } 111 } 112 } 113 114 public void writeValue(TypeDescription type, Object value) { 115 switch(type.getTypeClass().getValue()) { 116 case TypeClass.VOID_value: 117 break; 118 119 case TypeClass.BOOLEAN_value: 120 writeBooleanValue((Boolean) value); 121 break; 122 123 case TypeClass.BYTE_value: 124 writeByteValue((Byte) value); 125 break; 126 127 case TypeClass.SHORT_value: 128 case TypeClass.UNSIGNED_SHORT_value: 129 writeShortValue((Short) value); 130 break; 131 132 case TypeClass.LONG_value: 133 case TypeClass.UNSIGNED_LONG_value: 134 writeLongValue((Integer) value); 135 break; 136 137 case TypeClass.HYPER_value: 138 case TypeClass.UNSIGNED_HYPER_value: 139 writeHyperValue((Long) value); 140 break; 141 142 case TypeClass.FLOAT_value: 143 writeFloatValue((Float) value); 144 break; 145 146 case TypeClass.DOUBLE_value: 147 writeDoubleValue((Double) value); 148 break; 149 150 case TypeClass.CHAR_value: 151 writeCharValue((Character) value); 152 break; 153 154 case TypeClass.STRING_value: 155 writeStringValue((String) value); 156 break; 157 158 case TypeClass.TYPE_value: 159 writeTypeValue((Type) value); 160 break; 161 162 case TypeClass.ANY_value: 163 writeAnyValue(value); 164 break; 165 166 case TypeClass.SEQUENCE_value: 167 writeSequenceValue(type, value); 168 break; 169 170 case TypeClass.ENUM_value: 171 writeEnumValue(type, (Enum) value); 172 break; 173 174 case TypeClass.STRUCT_value: 175 writeStructValue(type, value); 176 break; 177 178 case TypeClass.EXCEPTION_value: 179 writeExceptionValue(type, (Exception) value); 180 break; 181 182 case TypeClass.INTERFACE_value: 183 writeInterfaceValue(type, (XInterface) value); 184 break; 185 186 default: 187 throw new IllegalArgumentException("Bad type descriptor " + type); 188 } 189 } 190 191 public byte[] reset() { 192 byte[] data = buffer.toByteArray(); 193 buffer.reset(); 194 return data; 195 } 196 197 private void writeBooleanValue(Boolean value) { 198 try { 199 output.writeBoolean(value != null && value.booleanValue()); 200 } catch (IOException e) { 201 throw new RuntimeException(e.toString()); 202 } 203 } 204 205 private void writeByteValue(Byte value) { 206 write8Bit(value == null ? 0 : value.byteValue()); 207 } 208 209 private void writeShortValue(Short value) { 210 write16Bit(value == null ? 0 : value.shortValue()); 211 } 212 213 private void writeLongValue(Integer value) { 214 write32Bit(value == null ? 0 : value.intValue()); 215 } 216 217 private void writeHyperValue(Long value) { 218 try { 219 output.writeLong(value == null ? 0 : value.longValue()); 220 } catch (IOException e) { 221 throw new RuntimeException(e.toString()); 222 } 223 } 224 225 private void writeFloatValue(Float value) { 226 try { 227 output.writeFloat(value == null ? 0 : value.floatValue()); 228 } catch (IOException e) { 229 throw new RuntimeException(e.toString()); 230 } 231 } 232 233 private void writeDoubleValue(Double value) { 234 try { 235 output.writeDouble(value == null ? 0 : value.doubleValue()); 236 } catch (IOException e) { 237 throw new RuntimeException(e.toString()); 238 } 239 } 240 241 private void writeCharValue(Character value) { 242 try { 243 output.writeChar(value == null ? 0 : value.charValue()); 244 } catch (IOException e) { 245 throw new RuntimeException(e.toString()); 246 } 247 } 248 249 private void writeStringValue(String value) { 250 if (value == null) { 251 writeCompressedNumber(0); 252 } else { 253 byte[] data; 254 try { 255 data = value.getBytes("UTF8"); 256 } catch (UnsupportedEncodingException e) { 257 throw new RuntimeException(e.toString()); 258 } 259 writeCompressedNumber(data.length); 260 writeBytes(data); 261 } 262 } 263 264 private void writeTypeValue(Type value) { 265 try { 266 writeType( 267 TypeDescription.getTypeDescription( 268 value == null ? Type.VOID : value)); 269 } catch (ClassNotFoundException e) { 270 throw new RuntimeException(e.toString()); 271 } 272 } 273 274 private void writeAnyValue(Object value) { 275 TypeDescription type; 276 if (value == null || value instanceof XInterface) { 277 type = TypeDescription.getTypeDescription(XInterface.class); 278 } else if (value instanceof Any) { 279 Any any = (Any) value; 280 try { 281 type = TypeDescription.getTypeDescription(any.getType()); 282 } catch (ClassNotFoundException e) { 283 throw new RuntimeException(e.toString()); 284 } 285 value = any.getObject(); 286 } else if (value.getClass() == Object.class) { 287 // Avoid StackOverflowError: 288 throw new IllegalArgumentException( 289 "Object instance does not represent UNO value"); 290 } else { 291 type = TypeDescription.getTypeDescription(value.getClass()); 292 } 293 writeType(type); 294 writeValue(type, value); 295 } 296 297 private void writeSequenceValue(TypeDescription type, Object value) { 298 if (value == null) { 299 writeCompressedNumber(0); 300 } else { 301 TypeDescription ctype = (TypeDescription) type.getComponentType(); 302 if (ctype.getTypeClass() == TypeClass.BYTE) { 303 byte[] data = (byte[]) value; 304 writeCompressedNumber(data.length); 305 writeBytes(data); 306 } else { 307 int len = Array.getLength(value); 308 writeCompressedNumber(len); 309 for (int i = 0; i < len; ++i) { 310 writeValue(ctype, Array.get(value, i)); 311 } 312 } 313 } 314 } 315 316 private void writeEnumValue(TypeDescription type, Enum value) { 317 int n; 318 if (value == null) { 319 try { 320 n = ((Enum) 321 (type.getZClass().getMethod("getDefault", null). 322 invoke(null, null))). 323 getValue(); 324 } catch (IllegalAccessException e) { 325 throw new RuntimeException(e.toString()); 326 } catch (InvocationTargetException e) { 327 throw new RuntimeException(e.toString()); 328 } catch (NoSuchMethodException e) { 329 throw new RuntimeException(e.toString()); 330 } 331 } else { 332 n = value.getValue(); 333 } 334 write32Bit(n); 335 } 336 337 private void writeStructValue(TypeDescription type, Object value) { 338 IFieldDescription[] fields = type.getFieldDescriptions(); 339 for (int i = 0; i < fields.length; ++i) { 340 try { 341 writeValue( 342 (TypeDescription) fields[i].getTypeDescription(), 343 value == null ? null : fields[i].getField().get(value)); 344 } catch (IllegalAccessException e) { 345 throw new RuntimeException(e.toString()); 346 } 347 } 348 } 349 350 private void writeExceptionValue(TypeDescription type, Exception value) { 351 writeStringValue(value == null ? null : value.getMessage()); 352 writeStructValue(type, value); 353 } 354 355 private void writeInterfaceValue(TypeDescription type, XInterface value) { 356 writeInterface(value, new Type(type)); 357 } 358 359 private void write32Bit(int value) { 360 try { 361 output.writeInt(value); 362 } catch (IOException e) { 363 throw new RuntimeException(e.toString()); 364 } 365 } 366 367 private void writeCompressedNumber(int number) { 368 if (number >= 0 && number < 0xFF) { 369 write8Bit(number); 370 } else { 371 write8Bit(0xFF); 372 write32Bit(number); 373 } 374 } 375 376 private void writeBytes(byte[] data) { 377 try { 378 output.write(data); 379 } catch (IOException e) { 380 throw new RuntimeException(e.toString()); 381 } 382 } 383 384 private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 385 private final DataOutput output = new DataOutputStream(buffer); 386 private final IBridge bridge; 387 private final Cache objectIdCache; 388 private final Cache threadIdCache; 389 private final Cache typeCache; 390 } 391