ec58021a3b30a5486e003fddf9bb5be45ed544c1
[scilab.git] / scilab / modules / scicos / src / cpp / XMIResource_save.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2016-2016 - Scilab Enterprises - Clement DAVID
4  *
5  * This file is hereby licensed under the terms of the GNU GPL v2.0,
6  * pursuant to article 5.3.4 of the CeCILL v.2.1.
7  * This file was originally licensed under the terms of the CeCILL v2.1,
8  * and continues to be available under such terms.
9  * For more information, see the COPYING file which you should have received
10  * along with this program.
11  */
12
13 #include "XMIResource.hxx"
14 #include "base64.hxx"
15
16 #include <string>
17 #include <sstream>
18 #include <vector>
19 #include <cmath> // for trunc
20
21 extern "C" {
22 #include "sci_types.h"
23
24 #include <libxml/xmlwriter.h>
25 }
26
27 namespace org_scilab_modules_scicos
28 {
29
30 int XMIResource::save(const char* uri)
31 {
32     int status;
33
34     xmlTextWriterPtr writer = xmlNewTextWriterFilename(uri, 0);
35     if (writer == NULL)
36     {
37         return -1;
38     }
39
40     status = xmlTextWriterSetIndent(writer, 1);
41     if (status == -1)
42     {
43         xmlFreeTextWriter(writer);
44         return status;
45     }
46
47     status = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
48     if (status == -1)
49     {
50         xmlFreeTextWriter(writer);
51         return status;
52     }
53
54     status = writeDiagram(writer);
55     if (status == -1)
56     {
57         xmlFreeTextWriter(writer);
58         return status;
59     }
60
61     status = xmlTextWriterEndDocument(writer);
62     xmlFreeTextWriter(writer);
63     return status;
64 }
65
66 static bool is_empty_matrix(const std::vector<double>& v)
67 {
68     // v == {1, 2, 0, 0, 0}
69     return v.size() == 5 && v[0] == sci_matrix && v[1] == 2. && v[2] == 0. && v[3] == 0. && v[4] == 0.;
70 }
71
72 static bool is_empty_list(const std::vector<double>& v)
73 {
74     // v == {15, 0}
75     return v.size() == 2 && v[0] == sci_list && v[1] == 0.;
76 }
77
78 static bool is_string_vector(const std::vector<double>& v)
79 {
80     return v.size() > 2 && v[0] == sci_strings && v[1] != 0;
81 }
82
83 static std::string to_string(int v)
84 {
85     return std::to_string(v);
86 }
87
88 static std::string to_string(bool v)
89 {
90     if (v)
91     {
92         return "true";
93     }
94     else
95     {
96         return "false";
97     }
98 }
99
100 static std::string to_string(double v)
101 {
102     if (std::trunc(v) == v)
103     {
104         return to_string((int) v);
105     }
106
107     std::string str(15, '\0');
108     // std::snprintf(const_cast<char*>(str.data()), str.size(), "%.6E", v);
109     std::sprintf(const_cast<char*>(str.data()), "%.6E", v);
110     return str;
111 }
112
113 /* helper function to decode simple string EXPRS */
114 static std::vector<std::string> to_string_vector(const std::vector<double>& v)
115 {
116     std::vector<std::string> ret;
117     std::vector<double>::const_iterator it = v.begin();
118
119     int strHeader = *it++;
120     if (strHeader != sci_strings)
121     {
122         return ret;
123     }
124     unsigned int iDims = *it++;
125
126     // manage multi-dimensional arrays (will be serialized as a vector)
127     unsigned int iElements = 1;
128     for (unsigned int i = 0; i < iDims; ++i)
129     {
130         iElements *= static_cast<unsigned int>(*it++);
131     }
132
133     // retrieve the length of each encoded string, stored as a stack
134     std::vector<unsigned int> stringsLength;
135     stringsLength.reserve(iElements + 1);
136     stringsLength.push_back(0);
137     for (unsigned int i = 0; i < iElements; ++i)
138     {
139         stringsLength.push_back(*it++);
140     }
141
142     // Retrieving the pointers (already UTF-8 encoded char*) and store them as strings
143     ret.reserve(ret.size() + iElements);
144     for (unsigned int i = 0; i < iElements; ++i)
145     {
146         // push the data
147         const double* strData = &(*(it + stringsLength[i]));
148         ret.emplace_back((char*) strData);
149     }
150
151     return ret;
152 }
153
154
155 static int writeBase64(xmlTextWriterPtr writer, const char* name, const std::vector<double>& v)
156 {
157     int status;
158
159     // convert values as big endian (network endianess)
160     // convert the big endian value to Base64
161     std::string content = base64::encode(v);
162
163     // write the XML data
164     status = xmlTextWriterStartElement(writer, BAD_CAST(name));
165     if (status == -1)
166     {
167         return status;
168     }
169
170     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("base64"), BAD_CAST(content.data()));
171     if (status == -1)
172     {
173         return status;
174     }
175
176     status = xmlTextWriterEndElement(writer);
177     return status;
178 }
179
180 int XMIResource::writeDatatype(xmlTextWriterPtr writer, const std::vector<int>& datatype)
181 {
182     int status;
183
184     status = xmlTextWriterStartElement(writer, BAD_CAST("datatype"));
185     if (status == -1)
186     {
187         return status;
188     }
189
190     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("type"), BAD_CAST(to_string(datatype[2]).c_str()));
191     if (status == -1)
192     {
193         return status;
194     }
195
196     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("rows"), BAD_CAST(to_string(datatype[0]).c_str()));
197     if (status == -1)
198     {
199         return status;
200     }
201
202     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("columns"), BAD_CAST(to_string(datatype[1]).c_str()));
203     if (status == -1)
204     {
205         return status;
206     }
207
208     status = xmlTextWriterEndElement(writer);
209     if (status == -1)
210     {
211         return status;
212     }
213
214     return status;
215 }
216
217 int XMIResource::writePoint(xmlTextWriterPtr writer, double x, double y)
218 {
219     int status;
220
221     status = xmlTextWriterStartElement(writer, BAD_CAST("controlPoint"));
222     if (status == -1)
223     {
224         return status;
225     }
226
227     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("x"), BAD_CAST(to_string(x).c_str()));
228     if (status == -1)
229     {
230         return status;
231     }
232
233     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("y"), BAD_CAST(to_string(y).c_str()));
234     if (status == -1)
235     {
236         return status;
237     }
238
239     status = xmlTextWriterEndElement(writer);
240     if (status == -1)
241     {
242         return status;
243     }
244
245     return status;
246 }
247
248 int XMIResource::writeGeometry(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
249 {
250     int status;
251
252     status = xmlTextWriterStartElement(writer, BAD_CAST("geometry"));
253     if (status == -1)
254     {
255         return status;
256     }
257
258     std::vector<double> doubleArrayValue;
259     controller.getObjectProperty(id, kind, GEOMETRY, doubleArrayValue);
260     unsigned int i = 0;
261     if (doubleArrayValue.size() > i && doubleArrayValue[i])
262     {
263         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("x"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
264         if (status == -1)
265         {
266             return status;
267         }
268     }
269
270     i++;
271     if (doubleArrayValue.size() > i && doubleArrayValue[i])
272     {
273         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("y"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
274         if (status == -1)
275         {
276             return status;
277         }
278     }
279
280     i++;
281     if (doubleArrayValue.size() > i && doubleArrayValue[i])
282     {
283         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("width"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
284         if (status == -1)
285         {
286             return status;
287         }
288     }
289
290     i++;
291     if (doubleArrayValue.size() > i && doubleArrayValue[i])
292     {
293         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("height"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
294         if (status == -1)
295         {
296             return status;
297         }
298     }
299
300     status = xmlTextWriterEndElement(writer);
301     if (status == -1)
302     {
303         return status;
304     }
305
306     return status;
307 }
308
309 int XMIResource::writeAbstractLayer(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
310 {
311     int status = 1;
312
313     std::vector<std::string> context;
314     controller.getObjectProperty(id, kind, DIAGRAM_CONTEXT, context);
315     for (const std::string& c : context)
316     {
317         status = xmlTextWriterStartElement(writer, BAD_CAST("context"));
318         if (status == -1)
319         {
320             return status;
321         }
322
323         if (!c.empty())
324         {
325             status = xmlTextWriterWriteCDATA(writer, BAD_CAST(c.c_str()));
326             if (status == -1)
327             {
328                 return status;
329             }
330         }
331
332         status = xmlTextWriterEndElement(writer);
333         if (status == -1)
334         {
335             return status;
336         }
337     }
338
339     std::vector<ScicosID> children;
340     controller.getObjectProperty(id, kind, CHILDREN, children);
341     for (ScicosID child : children)
342     {
343         kind_t kind = controller.getKind(child);
344         switch (kind)
345         {
346             case BLOCK:
347                 status = writeBlock(writer, child);
348                 break;
349             case LINK:
350                 status = writeLink(writer, child);
351                 break;
352             case ANNOTATION:
353                 status = writeAnnotation(writer, child);
354                 break;
355             default:
356                 status =  -1;
357                 break;
358         }
359
360         if (status == -1)
361         {
362             return status;
363         }
364     }
365
366     return status;
367 }
368
369 int XMIResource::writeAbstractBaseObject(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
370 {
371     int status;
372
373     std::string uid;
374     controller.getObjectProperty(id, kind, UID, uid);
375     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("uid"), BAD_CAST(uid.c_str()));
376     if (status == -1)
377     {
378         return status;
379     }
380
381     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("parentDiagram"), BAD_CAST("/"));
382     if (status == -1)
383     {
384         return status;
385     }
386
387     /* parent / child relation is not serialized as this relation is the XML tree */
388     return status;
389 }
390
391 int XMIResource::writeDiagram(xmlTextWriterPtr writer)
392 {
393     int status;
394
395     status = xmlTextWriterStartElementNS(writer, BAD_CAST("xcos"), BAD_CAST("Diagram"), BAD_CAST("org.scilab.modules.xcos"));
396     if (status == -1)
397     {
398         return status;
399     }
400
401     /*
402      * Write default xmlns
403      */
404     status = xmlTextWriterWriteAttributeNS(writer, BAD_CAST("xmi"), BAD_CAST("version"), BAD_CAST("http://www.omg.org/XMI"), BAD_CAST("2.0"));
405     if (status == -1)
406     {
407         return status;
408     }
409     status = xmlTextWriterWriteAttributeNS(writer, BAD_CAST("xsi"), BAD_CAST("schemaLocation"), BAD_CAST("http://www.w3.org/2001/XMLSchema-instance"), BAD_CAST("org.scilab.modules.xcos xcos.ecore"));
410     if (status == -1)
411     {
412         return status;
413     }
414
415     /*
416      * Diagram values
417      */
418     std::string strValue;
419     controller.getObjectProperty(root, DIAGRAM, TITLE, strValue);
420     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("title"), BAD_CAST(strValue.c_str()));
421     if (status == -1)
422     {
423         return status;
424     }
425
426     strValue.clear();
427     controller.getObjectProperty(root, DIAGRAM, PATH, strValue);
428     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("path"), BAD_CAST(strValue.c_str()));
429     if (status == -1)
430     {
431         return status;
432     }
433
434     int intValue;
435     controller.getObjectProperty(root, DIAGRAM, DEBUG_LEVEL, intValue);
436     strValue = to_string(intValue);
437     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("debugLevel"), BAD_CAST(strValue.c_str()));
438     if (status == -1)
439     {
440         return status;
441     }
442
443     strValue.clear();
444     controller.getObjectProperty(root, DIAGRAM, VERSION_NUMBER, strValue);
445     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("version"), BAD_CAST(strValue.c_str()));
446     if (status == -1)
447     {
448         return status;
449     }
450
451     status = writeAbstractLayer(writer, root, DIAGRAM);
452     if (status == -1)
453     {
454         return status;
455     }
456
457     status = writeSimulationConfig(writer, root);
458     if (status == -1)
459     {
460         return status;
461     }
462
463     status = xmlTextWriterEndElement(writer);
464     if (status == -1)
465     {
466         return status;
467     }
468
469     return status;
470 }
471
472 int XMIResource::writeSimulationConfig(xmlTextWriterPtr writer, ScicosID id)
473 {
474     int status;
475
476     status = xmlTextWriterStartElement(writer, BAD_CAST("properties"));
477     if (status == -1)
478     {
479         return status;
480     }
481
482     std::vector<double> doubleArrayValue;
483     controller.getObjectProperty(id, DIAGRAM, PROPERTIES, doubleArrayValue);
484     unsigned int i = 0;
485     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("finalTime"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
486     if (status == -1)
487     {
488         return status;
489     }
490
491     i++;
492     if (i >= doubleArrayValue.size())
493     {
494         return -1;
495     }
496     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("absoluteTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
497     if (status == -1)
498     {
499         return status;
500     }
501
502     i++;
503     if (i >= doubleArrayValue.size())
504     {
505         return -1;
506     }
507     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("relativeTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
508     if (status == -1)
509     {
510         return status;
511     }
512
513     i++;
514     if (i >= doubleArrayValue.size())
515     {
516         return -1;
517     }
518     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("timeTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
519     if (status == -1)
520     {
521         return status;
522     }
523
524     i++;
525     if (i >= doubleArrayValue.size())
526     {
527         return -1;
528     }
529     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("deltaT"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
530     if (status == -1)
531     {
532         return status;
533     }
534
535     i++;
536     if (i >= doubleArrayValue.size())
537     {
538         return -1;
539     }
540     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("realtimeScale"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
541     if (status == -1)
542     {
543         return status;
544     }
545
546     i++;
547     if (i >= doubleArrayValue.size())
548     {
549         return -1;
550     }
551     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("solver"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
552     if (status == -1)
553     {
554         return status;
555     }
556
557     i++;
558     if (i >= doubleArrayValue.size())
559     {
560         return -1;
561     }
562     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("deltaH"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
563     if (status == -1)
564     {
565         return status;
566     }
567
568     status = xmlTextWriterEndElement(writer);
569     if (status == -1)
570     {
571         return status;
572     }
573
574     return status;
575 }
576
577 int XMIResource::writeBlock(xmlTextWriterPtr writer, ScicosID id)
578 {
579     int status;
580
581     status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
582     if (status == -1)
583     {
584         return status;
585     }
586
587     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Block"));
588     if (status == -1)
589     {
590         return status;
591     }
592
593     status = writeAbstractBaseObject(writer, id, BLOCK);
594     if (status == -1)
595     {
596         return status;
597     }
598
599     std::string strValue;
600     controller.getObjectProperty(id, BLOCK, DESCRIPTION, strValue);
601     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("description"), BAD_CAST(strValue.c_str()));
602     if (status == -1)
603     {
604         return status;
605     }
606
607     strValue.clear();
608     controller.getObjectProperty(id, BLOCK, LABEL, strValue);
609     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
610     if (status == -1)
611     {
612         return status;
613     }
614
615     strValue.clear();
616     controller.getObjectProperty(id, BLOCK, STYLE, strValue);
617     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
618     if (status == -1)
619     {
620         return status;
621     }
622
623     strValue.clear();
624     controller.getObjectProperty(id, BLOCK, INTERFACE_FUNCTION, strValue);
625     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("interfaceFunction"), BAD_CAST(strValue.c_str()));
626     if (status == -1)
627     {
628         return status;
629     }
630
631     strValue.clear();
632     controller.getObjectProperty(id, BLOCK, SIM_FUNCTION_NAME, strValue);
633     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("functionName"), BAD_CAST(strValue.c_str()));
634     if (status == -1)
635     {
636         return status;
637     }
638
639     int intValue;
640     controller.getObjectProperty(id, BLOCK, SIM_FUNCTION_API, intValue);
641     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("functionAPI"), BAD_CAST(to_string(intValue).c_str()));
642     if (status == -1)
643     {
644         return status;
645     }
646
647     std::vector<int> intArrayValue;
648     controller.getObjectProperty(id, BLOCK, SIM_DEP_UT, intArrayValue);
649     unsigned int i = 0;
650     if (intArrayValue.size() > i && intArrayValue[i])
651     {
652         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("dependsOnU"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
653         if (status == -1)
654         {
655             return status;
656         }
657     }
658     i++;
659     if (intArrayValue.size() > i && intArrayValue[i])
660     {
661         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("dependsOnT"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
662         if (status == -1)
663         {
664             return status;
665         }
666     }
667
668     strValue.clear();
669     controller.getObjectProperty(id, BLOCK, SIM_BLOCKTYPE, strValue);
670     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("blocktype"), BAD_CAST(strValue.c_str()));
671     if (status == -1)
672     {
673         return status;
674     }
675
676     status = writeAbstractLayer(writer, id, BLOCK);
677     if (status == -1)
678     {
679         return status;
680     }
681
682     status = writeGeometry(writer, id, BLOCK);
683     if (status == -1)
684     {
685         return status;
686     }
687
688     std::vector<double> doubleArrayValue;
689     controller.getObjectProperty(id, BLOCK, EXPRS, doubleArrayValue);
690     if (is_empty_matrix(doubleArrayValue))
691     {
692         // we do not serialize the default value
693     }
694     else if (is_string_vector(doubleArrayValue))
695     {
696         // if this is a string the expression is used
697         std::vector<std::string> values = to_string_vector(doubleArrayValue);
698
699         for (const std::string& s : values)
700         {
701             status = xmlTextWriterStartElement(writer, BAD_CAST("expression"));
702             if (status == -1)
703             {
704                 return status;
705             }
706             status = xmlTextWriterWriteCDATA(writer, BAD_CAST(s.c_str()));
707             if (status == -1)
708             {
709                 return status;
710             }
711
712             status = xmlTextWriterEndElement(writer);
713             if (status == -1)
714             {
715                 return status;
716             }
717         }
718     }
719     else
720     {
721         // encode the value as base64 binary
722         status = writeBase64(writer, "exprs", doubleArrayValue);
723         if (status == -1)
724         {
725             return status;
726         }
727     }
728
729     intArrayValue.clear();
730     controller.getObjectProperty(id, BLOCK, NZCROSS, intArrayValue);
731     for (int i : intArrayValue)
732     {
733         status = xmlTextWriterWriteElement(writer, BAD_CAST("nzcross"), BAD_CAST(to_string(i).c_str()));
734         if (status == -1)
735         {
736             return status;
737         }
738     }
739
740     intArrayValue.clear();
741     controller.getObjectProperty(id, BLOCK, NMODE, intArrayValue);
742     for (int i : intArrayValue)
743     {
744         status = xmlTextWriterWriteElement(writer, BAD_CAST("nmode"), BAD_CAST(to_string(i).c_str()));
745         if (status == -1)
746         {
747             return status;
748         }
749     }
750
751     doubleArrayValue.clear();
752     controller.getObjectProperty(id, BLOCK, EQUATIONS, doubleArrayValue);
753     if (!doubleArrayValue.empty() && !is_empty_list(doubleArrayValue))
754     {
755         status = writeBase64(writer, "equations", doubleArrayValue);
756         if (status == -1)
757         {
758             return status;
759         }
760     }
761
762     std::vector<ScicosID> scicosIDArrayValue;
763     controller.getObjectProperty(id, BLOCK, INPUTS, scicosIDArrayValue);
764     for (ScicosID p : scicosIDArrayValue)
765     {
766         status = writePort(writer, INPUTS, p);
767         if (status == -1)
768         {
769             return status;
770         }
771     }
772
773     scicosIDArrayValue.clear();
774     controller.getObjectProperty(id, BLOCK, OUTPUTS, scicosIDArrayValue);
775     for (ScicosID p : scicosIDArrayValue)
776     {
777         status = writePort(writer, OUTPUTS, p);
778         if (status == -1)
779         {
780             return status;
781         }
782     }
783
784     controller.getObjectProperty(id, BLOCK, EVENT_INPUTS, scicosIDArrayValue);
785     for (ScicosID p : scicosIDArrayValue)
786     {
787         status = writePort(writer, EVENT_INPUTS, p);
788         if (status == -1)
789         {
790             return status;
791         }
792     }
793
794     controller.getObjectProperty(id, BLOCK, EVENT_OUTPUTS, scicosIDArrayValue);
795     for (ScicosID p : scicosIDArrayValue)
796     {
797         status = writePort(writer, EVENT_OUTPUTS, p);
798         if (status == -1)
799         {
800             return status;
801         }
802     }
803
804     doubleArrayValue.clear();
805     controller.getObjectProperty(id, BLOCK, RPAR, doubleArrayValue);
806     for (double d : doubleArrayValue)
807     {
808         status = xmlTextWriterWriteElement(writer, BAD_CAST("rpar"), BAD_CAST(to_string(d).c_str()));
809         if (status == -1)
810         {
811             return status;
812         }
813     }
814
815     intArrayValue.clear();
816     controller.getObjectProperty(id, BLOCK, IPAR, intArrayValue);
817     for (int i : intArrayValue)
818     {
819         status = xmlTextWriterWriteElement(writer, BAD_CAST("ipar"), BAD_CAST(to_string(i).c_str()));
820         if (status == -1)
821         {
822             return status;
823         }
824     }
825
826     doubleArrayValue.clear();
827     controller.getObjectProperty(id, BLOCK, OPAR, doubleArrayValue);
828     if (!is_empty_list(doubleArrayValue))
829     {
830         status = writeBase64(writer, "opar", doubleArrayValue);
831         if (status == -1)
832         {
833             return status;
834         }
835     }
836
837     doubleArrayValue.clear();
838     controller.getObjectProperty(id, BLOCK, STATE, doubleArrayValue);
839     for (double d : doubleArrayValue)
840     {
841         status = xmlTextWriterWriteElement(writer, BAD_CAST("state"), BAD_CAST(to_string(d).c_str()));
842         if (status == -1)
843         {
844             return status;
845         }
846     }
847
848     doubleArrayValue.clear();
849     controller.getObjectProperty(id, BLOCK, DSTATE, doubleArrayValue);
850     for (double d : doubleArrayValue)
851     {
852         status = xmlTextWriterWriteElement(writer, BAD_CAST("dstate"), BAD_CAST(to_string(d).c_str()));
853         if (status == -1)
854         {
855             return status;
856         }
857     }
858
859     doubleArrayValue.clear();
860     controller.getObjectProperty(id, BLOCK, ODSTATE, doubleArrayValue);
861     if (!is_empty_list(doubleArrayValue))
862     {
863         status = writeBase64(writer, "odstate", doubleArrayValue);
864         if (status == -1)
865         {
866             return status;
867         }
868     }
869
870     status = xmlTextWriterEndElement(writer);
871     if (status == -1)
872     {
873         return status;
874     }
875
876     return status;
877 }
878
879 int XMIResource::writePort(xmlTextWriterPtr writer, enum object_properties_t container, ScicosID id)
880 {
881     int status;
882
883     std::string element;
884     switch (container)
885     {
886         case INPUTS:
887             element = "in";
888             break;
889         case OUTPUTS:
890             element = "out";
891             break;
892         case EVENT_INPUTS:
893             element = "ein";
894             break;
895         case EVENT_OUTPUTS:
896             element = "eout";
897             break;
898         default:
899             return -1;
900     }
901
902     status = xmlTextWriterStartElement(writer, BAD_CAST(element.c_str()));
903     if (status == -1)
904     {
905         return status;
906     }
907
908     std::string strValue;
909     controller.getObjectProperty(id, PORT, UID, strValue);
910     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("uid"), BAD_CAST(strValue.c_str()));
911     if (status == -1)
912     {
913         return status;
914     }
915
916     ScicosID idValue;
917     controller.getObjectProperty(id, PORT, SOURCE_BLOCK, idValue);
918     controller.getObjectProperty(idValue, BLOCK, UID, strValue);
919     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("sourceBlock"), BAD_CAST(strValue.c_str()));
920     if (status == -1)
921     {
922         return status;
923     }
924
925     const std::vector<std::string> elementName = {"portUndefined", "in", "out", "ein", "eout"};
926     int portKind;
927     controller.getObjectProperty(id, PORT, PORT_KIND, portKind);
928     if (portKind < 0 && elementName.size() <= (unsigned int) portKind)
929     {
930         return -1;
931     }
932     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("kind"), BAD_CAST(elementName[portKind].c_str()));
933     if (status == -1)
934     {
935         return status;
936     }
937
938     bool implicit;
939     controller.getObjectProperty(id, PORT, IMPLICIT, implicit);
940     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("implicit"), BAD_CAST(to_string(implicit).c_str()));
941     if (status == -1)
942     {
943         return status;
944     }
945
946     controller.getObjectProperty(id, PORT, CONNECTED_SIGNALS, idValue);
947     if (idValue != 0)
948     {
949         strValue.clear();
950         controller.getObjectProperty(idValue, LINK, UID, strValue);
951
952         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("connectedSignal"), BAD_CAST(strValue.c_str()));
953         if (status == -1)
954         {
955             return status;
956         }
957     }
958
959     strValue.clear();
960     controller.getObjectProperty(id, PORT, STYLE, strValue);
961     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
962     if (status == -1)
963     {
964         return status;
965     }
966
967     strValue.clear();
968     controller.getObjectProperty(id, PORT, LABEL, strValue);
969     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
970     if (status == -1)
971     {
972         return status;
973     }
974
975     std::vector<int> intArrayValue;
976     controller.getObjectProperty(id, PORT, DATATYPE, intArrayValue);
977     status = writeDatatype(writer, intArrayValue);
978     if (status == -1)
979     {
980         return status;
981     }
982
983     status = xmlTextWriterEndElement(writer);
984     if (status == -1)
985     {
986         return status;
987     }
988
989     return status;
990 }
991
992 int XMIResource::writeLink(xmlTextWriterPtr writer, ScicosID id)
993 {
994     int status;
995
996     status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
997     if (status == -1)
998     {
999         return status;
1000     }
1001
1002     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Link"));
1003     if (status == -1)
1004     {
1005         return status;
1006     }
1007
1008     status = writeAbstractBaseObject(writer, id, LINK);
1009     if (status == -1)
1010     {
1011         return status;
1012     }
1013
1014     ScicosID idValue;
1015     std::string strValue;
1016     controller.getObjectProperty(id, LINK, SOURCE_PORT, idValue);
1017     if (idValue != 0)
1018     {
1019         strValue.clear();
1020         controller.getObjectProperty(idValue, PORT, UID, strValue);
1021
1022         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("sourcePort"), BAD_CAST(strValue.c_str()));
1023         if (status == -1)
1024         {
1025             return status;
1026         }
1027     }
1028
1029     controller.getObjectProperty(id, LINK, DESTINATION_PORT, idValue);
1030     if (idValue != 0)
1031     {
1032         strValue.clear();
1033         controller.getObjectProperty(idValue, PORT, UID, strValue);
1034
1035         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("destinationPort"), BAD_CAST(strValue.c_str()));
1036         if (status == -1)
1037         {
1038             return status;
1039         }
1040     }
1041
1042     strValue.clear();
1043     controller.getObjectProperty(id, LINK, STYLE, strValue);
1044     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
1045     if (status == -1)
1046     {
1047         return status;
1048     }
1049
1050     strValue.clear();
1051     controller.getObjectProperty(id, LINK, LABEL, strValue);
1052     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
1053     if (status == -1)
1054     {
1055         return status;
1056     }
1057
1058     int intValue;
1059     controller.getObjectProperty(id, LINK, COLOR, intValue);
1060     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("color"), BAD_CAST(to_string(intValue).c_str()));
1061     if (status == -1)
1062     {
1063         return status;
1064     }
1065
1066     std::vector<int> intArrayValue;
1067     controller.getObjectProperty(id, LINK, THICK, intArrayValue);
1068     unsigned int i = 0;
1069     if (i < intArrayValue.size())
1070     {
1071         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("lineWidth"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
1072         if (status == -1)
1073         {
1074             return status;
1075         }
1076     }
1077     i++;
1078     if (i < intArrayValue.size())
1079     {
1080         status = xmlTextWriterWriteAttribute(writer, BAD_CAST("lineHeight"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
1081         if (status == -1)
1082         {
1083             return status;
1084         }
1085     }
1086
1087     status = writeGeometry(writer, id, LINK);
1088     if (status == -1)
1089     {
1090         return status;
1091     }
1092
1093     std::vector<double> dblArrayValue;
1094     controller.getObjectProperty(id, LINK, CONTROL_POINTS, dblArrayValue);
1095     for (unsigned int i = 0; i < dblArrayValue.size(); i += 2)
1096     {
1097         status = writePoint(writer, dblArrayValue[i], dblArrayValue[i + 1]);
1098         if (status == -1)
1099         {
1100             return status;
1101         }
1102     }
1103
1104     status = xmlTextWriterEndElement(writer);
1105     if (status == -1)
1106     {
1107         return status;
1108     }
1109
1110     return status;
1111 }
1112
1113 int XMIResource::writeAnnotation(xmlTextWriterPtr writer, ScicosID id)
1114 {
1115     int status;
1116
1117     status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
1118     if (status == -1)
1119     {
1120         return status;
1121     }
1122
1123     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Annotation"));
1124     if (status == -1)
1125     {
1126         return status;
1127     }
1128
1129     status = writeAbstractBaseObject(writer, id, ANNOTATION);
1130     if (status == -1)
1131     {
1132         return status;
1133     }
1134
1135     std::string strValue;
1136     controller.getObjectProperty(id, ANNOTATION, DESCRIPTION, strValue);
1137     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("description"), BAD_CAST(strValue.c_str()));
1138     if (status == -1)
1139     {
1140         return status;
1141     }
1142
1143     strValue.clear();
1144     controller.getObjectProperty(id, ANNOTATION, FONT, strValue);
1145     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("font"), BAD_CAST(strValue.c_str()));
1146     if (status == -1)
1147     {
1148         return status;
1149     }
1150
1151     strValue.clear();
1152     controller.getObjectProperty(id, ANNOTATION, FONT_SIZE, strValue);
1153     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("fontSize"), BAD_CAST(strValue.c_str()));
1154     if (status == -1)
1155     {
1156         return status;
1157     }
1158
1159     strValue.clear();
1160     controller.getObjectProperty(id, ANNOTATION, STYLE, strValue);
1161     status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
1162     if (status == -1)
1163     {
1164         return status;
1165     }
1166
1167     status = xmlTextWriterEndElement(writer);
1168     if (status == -1)
1169     {
1170         return status;
1171     }
1172
1173     return status;
1174 }
1175
1176 } /* namespace org_scilab_modules_xcos */