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