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