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