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