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