Xcos writers: fix ScilabString encoding
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / writer / RawDataWriter.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2015-2015 - Scilab Enterprises - Clement DAVID
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12 package org.scilab.modules.xcos.io.writer;
13
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.Map.Entry;
18 import java.util.logging.Level;
19
20 import javax.xml.stream.XMLStreamException;
21
22 import org.scilab.modules.types.ScilabBoolean;
23 import org.scilab.modules.types.ScilabDouble;
24 import org.scilab.modules.types.ScilabInteger;
25 import org.scilab.modules.types.ScilabString;
26 import org.scilab.modules.types.ScilabType;
27 import org.scilab.modules.types.ScilabTypeEnum;
28 import org.scilab.modules.xcos.Kind;
29 import org.scilab.modules.xcos.ObjectProperties;
30 import org.scilab.modules.xcos.VectorOfDouble;
31 import org.scilab.modules.xcos.VectorOfInt;
32 import org.scilab.modules.xcos.VectorOfString;
33 import org.scilab.modules.xcos.io.ScilabTypeCoder;
34
35 public class RawDataWriter extends ScilabWriter {
36
37     /*
38      * Shared map information used to avoid duplicated entry on the dictionary
39      */
40     private final HashMap<ScilabType, Integer> dictionaryMap;
41
42     public RawDataWriter(XcosWriter writer) {
43         super(writer);
44
45         dictionaryMap = new HashMap<>();
46     }
47
48     /**
49      * Fill the shared dictinary with the encoded mapping
50      */
51     public void fillSharedDictionary() {
52         if (shared.dictionary == null) {
53             return;
54         }
55
56         // reserve the size with null values
57         shared.dictionary.addAll(Collections.nCopies(dictionaryMap.size(), new ScilabDouble()));
58
59         // push all the values
60         for (Entry<ScilabType, Integer> entry : dictionaryMap.entrySet()) {
61             shared.dictionary.set(entry.getValue(), entry.getKey());
62         }
63     }
64
65     @Override
66     public void write(long uid, Kind kind) throws XMLStreamException {
67
68         switch (kind) {
69             case BLOCK:
70                 writeBlock(uid, kind);
71                 break;
72             case DIAGRAM:
73                 writeContext(uid, kind);
74                 break;
75             case LINK: // no break on purpose
76             case ANNOTATION:  // no break on purpose
77             default:
78                 break;
79         }
80     }
81
82     private void writeContext(long uid, Kind kind) throws XMLStreamException {
83         VectorOfString context = new VectorOfString();
84         shared.controller.getObjectProperty(uid, kind, ObjectProperties.DIAGRAM_CONTEXT, context);
85
86         String[] value = new String[context.size()];
87         for (int i = 0; i < value.length; i++) {
88             value[i] = context.get(i);
89         }
90         write(value, "context");
91     }
92
93     private void writeBlock(long uid, Kind kind) throws XMLStreamException {
94         VectorOfDouble vDouble = new VectorOfDouble();
95         VectorOfInt vInt = new VectorOfInt();
96
97         shared.controller.getObjectProperty(uid, kind, ObjectProperties.EXPRS, vDouble);
98         write(new ScilabTypeCoder().vec2var(vDouble), "exprs");
99
100         shared.controller.getObjectProperty(uid, kind, ObjectProperties.RPAR, vDouble);
101         write(vDouble, "realParameters");
102
103         shared.controller.getObjectProperty(uid, kind, ObjectProperties.IPAR, vInt);
104         write(vInt, "integerParameters");
105
106         shared.controller.getObjectProperty(uid, kind, ObjectProperties.OPAR, vDouble);
107         write(new ScilabTypeCoder().vec2var(vDouble), "objectsParameters");
108
109         shared.controller.getObjectProperty(uid, kind, ObjectProperties.NZCROSS, vInt);
110         write(vInt, "nbZerosCrossing");
111
112         shared.controller.getObjectProperty(uid, kind, ObjectProperties.NMODE, vInt);
113         write(vInt, "nmode");
114
115         shared.controller.getObjectProperty(uid, kind, ObjectProperties.DSTATE, vDouble);
116         write(vDouble, "dState");
117
118         shared.controller.getObjectProperty(uid, kind, ObjectProperties.EQUATIONS, vDouble);
119         write(new ScilabTypeCoder().vec2var(vDouble), "equations");
120     }
121
122     /*
123      * Low-level serialization methods
124      */
125
126     private void write(String[] value, String as) throws XMLStreamException {
127         shared.stream.writeStartElement("Array");
128         shared.stream.writeAttribute("as", as);
129         shared.stream.writeAttribute("scilabClass", "String[]");
130
131         for (String v : value) {
132             shared.stream.writeEmptyElement("add");
133             shared.stream.writeAttribute("value", v);
134         }
135
136         shared.stream.writeEndElement(); // Array
137     }
138
139     private void write(ScilabType value, String as) throws XMLStreamException {
140         if (shared.dictionary == null) {
141             writeAsXml(value, as);
142         } else {
143             writeAsBinary(value, as);
144         }
145     }
146
147     private void writeAsXml(ScilabType value, String as) throws XMLStreamException {
148         ScilabTypeEnum type = value.getType();
149         final String localName;
150         final String scilabClass;
151         final String intPrecision;
152
153         /*
154          * Get the right descriptor
155          */
156         switch (type) {
157             case sci_boolean:
158                 localName = "ScilabBoolean";
159                 scilabClass = null;
160                 intPrecision = null;
161                 break;
162             case sci_ints:
163                 localName = "ScilabInteger";
164                 scilabClass = null;
165                 intPrecision = ((ScilabInteger) value).getPrec().name();
166                 break;
167             case sci_matrix:
168                 localName = "ScilabDouble";
169                 scilabClass = null;
170                 intPrecision = null;
171                 break;
172             case sci_strings:
173                 localName = "ScilabString";
174                 scilabClass = null;
175                 intPrecision = null;
176                 break;
177             case sci_list:
178                 localName = "Array";
179                 scilabClass = "ScilabList";
180                 intPrecision = null;
181                 break;
182             case sci_mlist:
183                 localName = "Array";
184                 scilabClass = "ScilabMList";
185                 intPrecision = null;
186                 break;
187             case sci_tlist:
188                 localName = "Array";
189                 scilabClass = "ScilabTList";
190                 intPrecision = null;
191                 break;
192             default:
193                 throw new XMLStreamException("Not handled ScilabType : " + type.name());
194         }
195
196         /*
197          * Emit the elements
198          */
199         final int len = value.getHeight() * value.getWidth();
200         if (len > 0) {
201             shared.stream.writeStartElement(localName);
202         } else {
203             shared.stream.writeEmptyElement(localName);
204         }
205         if (as != null) {
206             shared.stream.writeAttribute("as", as);
207         }
208
209         if (value instanceof ArrayList) {
210             writeList(value, scilabClass);
211         } else {
212             writeMatrix(value, type, intPrecision);
213         }
214
215         if (len > 0) {
216             shared.stream.writeEndElement();
217         }
218     }
219
220     private void writeList(ScilabType value, final String scilabClass) throws XMLStreamException {
221         shared.stream.writeAttribute("scilabClass", scilabClass);
222
223         @SuppressWarnings("unchecked")
224         ArrayList<ScilabType> values = (ArrayList<ScilabType>) value;
225         for (ScilabType subValue : values) {
226             write(subValue, null);
227         }
228     }
229
230     @SuppressWarnings("incomplete-switch")
231     private void writeMatrix(ScilabType value, ScilabTypeEnum type, final String intPrecision) throws XMLStreamException {
232         shared.stream.writeAttribute("height", Integer.toString(value.getHeight()));
233         shared.stream.writeAttribute("width", Integer.toString(value.getWidth()));
234         if (intPrecision != null) {
235             shared.stream.writeAttribute("intPrecision", value.getType().name());
236         }
237
238         for (int i = 0; i < value.getHeight(); i++) {
239             for (int j = 0; j < value.getWidth(); j++) {
240                 shared.stream.writeEmptyElement("data");
241                 shared.stream.writeAttribute("column", Integer.toString(i));
242                 shared.stream.writeAttribute("line", Integer.toString(j));
243
244                 switch (type) {
245                     case sci_boolean:
246                         boolean bValue = ((ScilabBoolean)value).getElement(i, j);
247                         shared.stream.writeAttribute("value", Boolean.toString(bValue));
248                         break;
249                     case sci_ints:
250                         long lValue = ((ScilabInteger) value).getElement(i, j);
251                         shared.stream.writeAttribute("value", Long.toString(lValue));
252                         break;
253                     case sci_matrix:
254                         double dValue = ((ScilabDouble) value).getRealElement(i, j);
255                         shared.stream.writeAttribute("realPart", Double.toString(dValue));
256                         if (!((ScilabDouble) value).isReal()) {
257                             dValue  = ((ScilabDouble) value).getImaginaryElement(i, j);
258                             shared.stream.writeAttribute("imaginaryPart", Double.toString(dValue));
259                         }
260                         break;
261                     case sci_strings:
262                         String strValue = ((ScilabString) value).getData()[i][j];
263                         shared.stream.writeAttribute("value", strValue);
264                         break;
265                 }
266             }
267         }
268     }
269
270     private void writeAsBinary(ScilabType value, String as) throws XMLStreamException {
271         ScilabTypeEnum type = value.getType();
272         final String localName;
273
274         /*
275          * Get the right descriptor
276          */
277         switch (type) {
278             case sci_boolean:
279                 localName = "ScilabBoolean";
280                 break;
281             case sci_ints:
282                 localName = "ScilabInteger";
283                 break;
284             case sci_matrix:
285                 localName = "ScilabDouble";
286                 break;
287             case sci_strings:
288                 localName = "ScilabString";
289                 break;
290             case sci_list: // no break on purpose
291             case sci_mlist: // no break on purpose
292             case sci_tlist:
293                 localName = "Array";
294                 break;
295             default:
296                 throw new XMLStreamException("Not handled ScilabType : " + type.name());
297         }
298
299         /*
300          * Emit the elements
301          */
302
303         // if the value has not been mapped, then use the next position (map.size())
304         // else re-use the same position
305         Integer expectedPosition = dictionaryMap.size();
306         Integer position = dictionaryMap.putIfAbsent(value, expectedPosition);
307         if (position == null) {
308             position = expectedPosition;
309         }
310
311         if (XcosWriter.LOG.isLoggable(Level.FINE)) {
312             XcosWriter.LOG.fine("put:" + value.toString() + ":" + position.toString());
313         }
314
315         shared.stream.writeEmptyElement(localName);
316         shared.stream.writeAttribute("as", as);
317         shared.stream.writeAttribute("binary", "true");
318         shared.stream.writeAttribute("position", position.toString());
319     }
320
321     private void write(VectorOfDouble value, String as) throws XMLStreamException {
322         // recreate a ScilabDouble for the encoding
323         final int length = value.size();
324         if (length > 0) {
325             double[][] data = new double[1][length];
326             value.asByteBuffer(0, length).asDoubleBuffer().get(data[0]);
327             write(new ScilabDouble(data), as);
328         } else {
329             write(new ScilabDouble(), as);
330         }
331     }
332
333     private void write(VectorOfInt value, String as) throws XMLStreamException {
334         // recreate a ScilabInteger for the encoding
335         final int length = value.size();
336         if (length > 0) {
337             int[][] data = new int[1][length];
338             value.asByteBuffer(0, length).asIntBuffer().get(data[0]);
339             write(new ScilabInteger(data, false), as);
340         } else {
341             write(new ScilabDouble(), as);
342         }
343     }
344 }