Xcos: introduce a new XML based format (using XMI)
[scilab.git] / scilab / modules / scicos / src / cpp / XMIResource_load.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 <cassert>
17 #include <string>
18 #include <vector>
19 #include <algorithm>
20 #include <cstdlib> // for atoi and atof
21 #include <cstring> // for strcmp and strchr
22
23 extern "C"
24 {
25 #include <libxml/tree.h>
26 #include <libxml/parser.h>
27 #include <libxml/xmlerror.h>
28 #include <libxml/xmlreader.h>
29
30 #include "sciprint.h"
31 }
32
33 namespace org_scilab_modules_scicos
34 {
35
36 /**
37  * Display on the Scilab console
38  */
39 void console_print(void *, const char *msg, ...) LIBXML_ATTR_FORMAT(2, 3);
40 void console_print(void *, const char *msg, ...)
41 {
42     //print the message
43     va_list ap;
44     va_start(ap, msg);
45     scivprint(msg, ap);
46     va_end(ap);
47 }
48
49 /**
50  * Helper class to set / reset the XML parser state
51  */
52 struct LibXML2State
53 {
54     LibXML2State()
55     {
56         xmlGenericErrorFunc f = &console_print;
57         initGenericErrorDefaultFunc(&f);
58     }
59     ~LibXML2State()
60     {
61         initGenericErrorDefaultFunc(nullptr);
62     }
63 };
64
65 int XMIResource::load(const char* uri)
66 {
67     int ret;
68
69     LibXML2State state;
70
71     /*
72      * Allocate the reader object, this API is used as it is simpler to use than SAX2 :
73      *  * we have direct access to a node object
74      *  * Strings are interned by libxml2
75      *  * partial SAX2 callbacks are not supported by libxml2
76      */
77     xmlTextReaderPtr reader;
78     /* resolve xinclude and intern strings */
79     reader = xmlReaderForFile(uri, NULL, XML_PARSE_XINCLUDE | XML_PARSE_COMPACT);
80
81     /*
82      * Intern strings to speedup comparaison, this table can be generated using XPath on xcos.ecore .
83      */
84     constXcosNames[e_Annotation] = xmlTextReaderConstString(reader, BAD_CAST ("Annotation"));
85     constXcosNames[e_BaseObject] = xmlTextReaderConstString(reader, BAD_CAST ("BaseObject"));
86     constXcosNames[e_Block] = xmlTextReaderConstString(reader, BAD_CAST ("Block"));
87     constXcosNames[e_CompiledRepresentation] = xmlTextReaderConstString(reader, BAD_CAST ("CompiledRepresentation"));
88     constXcosNames[e_Diagram] = xmlTextReaderConstString(reader, BAD_CAST ("Diagram"));
89     constXcosNames[e_Geometry] = xmlTextReaderConstString(reader, BAD_CAST ("Geometry"));
90     constXcosNames[e_Layer] = xmlTextReaderConstString(reader, BAD_CAST ("Layer"));
91     constXcosNames[e_Link] = xmlTextReaderConstString(reader, BAD_CAST ("Link"));
92     constXcosNames[e_Point] = xmlTextReaderConstString(reader, BAD_CAST ("Point"));
93     constXcosNames[e_Port] = xmlTextReaderConstString(reader, BAD_CAST ("Port"));
94     constXcosNames[e_PortKind] = xmlTextReaderConstString(reader, BAD_CAST ("PortKind"));
95     constXcosNames[e_SimulationConfig] = xmlTextReaderConstString(reader, BAD_CAST ("SimulationConfig"));
96     constXcosNames[e_absoluteTolerance] = xmlTextReaderConstString(reader, BAD_CAST ("absoluteTolerance"));
97     constXcosNames[e_base64] = xmlTextReaderConstString(reader, BAD_CAST ("base64"));
98     constXcosNames[e_blocktype] = xmlTextReaderConstString(reader, BAD_CAST ("blocktype"));
99     constXcosNames[e_child] = xmlTextReaderConstString(reader, BAD_CAST ("child"));
100     constXcosNames[e_color] = xmlTextReaderConstString(reader, BAD_CAST ("color"));
101     constXcosNames[e_connectedSignal] = xmlTextReaderConstString(reader, BAD_CAST ("connectedSignal"));
102     constXcosNames[e_context] = xmlTextReaderConstString(reader, BAD_CAST ("context"));
103     constXcosNames[e_controlPoint] = xmlTextReaderConstString(reader, BAD_CAST ("controlPoint"));
104     constXcosNames[e_datatype] = xmlTextReaderConstString(reader, BAD_CAST ("datatype"));
105     constXcosNames[e_debugLevel] = xmlTextReaderConstString(reader, BAD_CAST ("debugLevel"));
106     constXcosNames[e_deltaH] = xmlTextReaderConstString(reader, BAD_CAST ("deltaH"));
107     constXcosNames[e_deltaT] = xmlTextReaderConstString(reader, BAD_CAST ("deltaT"));
108     constXcosNames[e_dependsOnT] = xmlTextReaderConstString(reader, BAD_CAST ("dependsOnT"));
109     constXcosNames[e_dependsOnU] = xmlTextReaderConstString(reader, BAD_CAST ("dependsOnU"));
110     constXcosNames[e_description] = xmlTextReaderConstString(reader, BAD_CAST ("description"));
111     constXcosNames[e_destinationPort] = xmlTextReaderConstString(reader, BAD_CAST ("destinationPort"));
112     constXcosNames[e_dstate] = xmlTextReaderConstString(reader, BAD_CAST ("dstate"));
113     constXcosNames[e_ein] = xmlTextReaderConstString(reader, BAD_CAST ("ein"));
114     constXcosNames[e_eout] = xmlTextReaderConstString(reader, BAD_CAST ("eout"));
115     constXcosNames[e_equations] = xmlTextReaderConstString(reader, BAD_CAST ("equations"));
116     constXcosNames[e_expression] = xmlTextReaderConstString(reader, BAD_CAST ("expression"));
117     constXcosNames[e_exprs] = xmlTextReaderConstString(reader, BAD_CAST ("exprs"));
118     constXcosNames[e_finalTime] = xmlTextReaderConstString(reader, BAD_CAST ("finalTime"));
119     constXcosNames[e_firing] = xmlTextReaderConstString(reader, BAD_CAST ("firing"));
120     constXcosNames[e_font] = xmlTextReaderConstString(reader, BAD_CAST ("font"));
121     constXcosNames[e_fontSize] = xmlTextReaderConstString(reader, BAD_CAST ("fontSize"));
122     constXcosNames[e_functionAPI] = xmlTextReaderConstString(reader, BAD_CAST ("functionAPI"));
123     constXcosNames[e_functionName] = xmlTextReaderConstString(reader, BAD_CAST ("functionName"));
124     constXcosNames[e_geometry] = xmlTextReaderConstString(reader, BAD_CAST ("geometry"));
125     constXcosNames[e_height] = xmlTextReaderConstString(reader, BAD_CAST ("height"));
126     constXcosNames[e_implicit] = xmlTextReaderConstString(reader, BAD_CAST ("implicit"));
127     constXcosNames[e_in] = xmlTextReaderConstString(reader, BAD_CAST ("in"));
128     constXcosNames[e_interfaceFunction] = xmlTextReaderConstString(reader, BAD_CAST ("interfaceFunction"));
129     constXcosNames[e_ipar] = xmlTextReaderConstString(reader, BAD_CAST ("ipar"));
130     constXcosNames[e_kind] = xmlTextReaderConstString(reader, BAD_CAST ("kind"));
131     constXcosNames[e_label] = xmlTextReaderConstString(reader, BAD_CAST ("label"));
132     constXcosNames[e_lineHeight] = xmlTextReaderConstString(reader, BAD_CAST ("lineHeight"));
133     constXcosNames[e_lineWidth] = xmlTextReaderConstString(reader, BAD_CAST ("lineWidth"));
134     constXcosNames[e_nmode] = xmlTextReaderConstString(reader, BAD_CAST ("nmode"));
135     constXcosNames[e_nzcross] = xmlTextReaderConstString(reader, BAD_CAST ("nzcross"));
136     constXcosNames[e_odstate] = xmlTextReaderConstString(reader, BAD_CAST ("odstate"));
137     constXcosNames[e_opar] = xmlTextReaderConstString(reader, BAD_CAST ("opar"));
138     constXcosNames[e_out] = xmlTextReaderConstString(reader, BAD_CAST ("out"));
139     constXcosNames[e_parent] = xmlTextReaderConstString(reader, BAD_CAST ("parent"));
140     constXcosNames[e_parentDiagram] = xmlTextReaderConstString(reader, BAD_CAST ("parentDiagram"));
141     constXcosNames[e_path] = xmlTextReaderConstString(reader, BAD_CAST ("path"));
142     constXcosNames[e_properties] = xmlTextReaderConstString(reader, BAD_CAST ("properties"));
143     constXcosNames[e_realtimeScale] = xmlTextReaderConstString(reader, BAD_CAST ("realtimeScale"));
144     constXcosNames[e_relativeTolerance] = xmlTextReaderConstString(reader, BAD_CAST ("relativeTolerance"));
145     constXcosNames[e_rpar] = xmlTextReaderConstString(reader, BAD_CAST ("rpar"));
146     constXcosNames[e_solver] = xmlTextReaderConstString(reader, BAD_CAST ("solver"));
147     constXcosNames[e_sourceBlock] = xmlTextReaderConstString(reader, BAD_CAST ("sourceBlock"));
148     constXcosNames[e_sourcePort] = xmlTextReaderConstString(reader, BAD_CAST ("sourcePort"));
149     constXcosNames[e_state] = xmlTextReaderConstString(reader, BAD_CAST ("state"));
150     constXcosNames[e_style] = xmlTextReaderConstString(reader, BAD_CAST ("style"));
151     constXcosNames[e_timeTolerance] = xmlTextReaderConstString(reader, BAD_CAST ("timeTolerance"));
152     constXcosNames[e_title] = xmlTextReaderConstString(reader, BAD_CAST ("title"));
153     constXcosNames[e_type] = xmlTextReaderConstString(reader, BAD_CAST ("type"));
154     constXcosNames[e_uid] = xmlTextReaderConstString(reader, BAD_CAST ("uid"));
155     constXcosNames[e_version] = xmlTextReaderConstString(reader, BAD_CAST ("version"));
156     constXcosNames[e_width] = xmlTextReaderConstString(reader, BAD_CAST ("width"));
157     constXcosNames[e_x] = xmlTextReaderConstString(reader, BAD_CAST ("x"));
158     constXcosNames[e_xcos] = xmlTextReaderConstString(reader, BAD_CAST ("xcos"));
159     constXcosNames[e_y] = xmlTextReaderConstString(reader, BAD_CAST ("y"));
160
161     xcosNamespaceUri = xmlTextReaderConstString(reader, BAD_CAST ("org.scilab.modules.xcos"));
162     xsiNamespaceUri = xmlTextReaderConstString(reader, BAD_CAST ("http://www.w3.org/2001/XMLSchema-instance"));
163
164     unresolved.clear();
165
166     /*
167      * Process the document
168      */
169     if (reader != NULL)
170     {
171         ret = xmlTextReaderRead(reader);
172         while (ret == 1)
173         {
174             ret = processNode(reader);
175             if (ret == 1)
176             {
177                 ret = xmlTextReaderRead(reader);
178             }
179         }
180         /*
181          * Once the document has been fully parsed check the validation results
182          */
183         if (xmlTextReaderIsValid(reader) < 0)
184         {
185             sciprint("Document %s does not validate\n", uri);
186         }
187         xmlFreeTextReader(reader);
188         if (ret < 0)
189         {
190             sciprint("%s : failed to parse\n", uri);
191             return ret;
192         }
193     }
194     else
195     {
196         sciprint("Unable to open %s\n", uri);
197         return -1;
198     }
199
200     /*
201      * After loading the XML file, resolve all references
202      */
203     for (const unresolvedReference& ref : unresolved)
204     {
205         auto it = references.find(ref.m_uid);
206         if (it != references.end())
207         {
208             controller.setObjectProperty(ref.m_id, ref.m_kind, ref.m_prop, it->second);
209         }
210         else
211         {
212             sciprint("Unable to resolve %s\n", ref.m_uid.c_str());
213             return -1;
214         }
215     }
216
217     return ret;
218 }
219
220 /*
221  * Convert an XML UTF-8 string to a model string
222  */
223 std::string to_string(const xmlChar* xmlStr)
224 {
225     if (xmlStr == nullptr)
226     {
227         return "";
228     }
229
230     // the strings in the model are stored as UTF-8 as in libxml2
231     return std::string((const char*) xmlStr);
232 }
233
234 /*
235  * Convert an XML UTF-8 string to a model int
236  */
237 int to_int(const xmlChar* xmlStr)
238 {
239     if (xmlStr == nullptr)
240     {
241         return 0;
242     }
243
244     return std::atoi((const char*) xmlStr);
245 }
246
247 /*
248  * Convert an XML UTF-8 string to a model double
249  */
250 double to_double(const xmlChar* xmlStr)
251 {
252     if (xmlStr == nullptr)
253     {
254         return 0.0;
255     }
256
257     return std::atof((const char*) xmlStr);
258 }
259
260 int XMIResource::loadDoubleArray(xmlTextReaderPtr reader, enum object_properties_t property, const model::BaseObject& o)
261 {
262     std::vector<double> v;
263     controller.getObjectProperty(o.id(), o.kind(), property, v);
264
265     v.push_back(to_double(xmlTextReaderConstValue(reader)));
266
267     controller.setObjectProperty(o.id(), o.kind(), property, v);
268     return 1;
269 }
270
271 int XMIResource::loadIntArray(xmlTextReaderPtr reader, enum object_properties_t property, const model::BaseObject& o)
272 {
273     std::vector<int> v;
274     controller.getObjectProperty(o.id(), o.kind(), property, v);
275
276     v.push_back(to_int(xmlTextReaderConstValue(reader)));
277
278     controller.setObjectProperty(o.id(), o.kind(), property, v);
279     return 1;
280 }
281
282 int XMIResource::loadStringArray(xmlTextReaderPtr reader, enum object_properties_t property, const model::BaseObject& o)
283 {
284     std::vector<std::string> v;
285     controller.getObjectProperty(o.id(), o.kind(), property, v);
286
287     v.push_back(to_string(xmlTextReaderConstValue(reader)));
288
289     controller.setObjectProperty(o.id(), o.kind(), property, v);
290     return 1;
291 }
292
293 int XMIResource::loadBase64(xmlTextReaderPtr reader, enum object_properties_t property, const model::BaseObject& o)
294 {
295     // iterate on attributes
296     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
297     {
298         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
299         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
300
301         switch (current)
302         {
303             case e_base64:
304             {
305                 const xmlChar* base64 = xmlTextReaderConstValue(reader);
306                 std::vector<double> v = base64::decode<std::vector<double> >(to_string(base64));
307                 controller.setObjectProperty(o.id(), o.kind(), property, v);
308                 break;
309             }
310             default:
311                 // ignore other parameters
312                 // TODO: Does other metamodels might be managed there ?
313                 break;
314         }
315     }
316
317     return 1;
318 }
319
320 int XMIResource::loadPoint(xmlTextReaderPtr reader, const model::BaseObject& o)
321 {
322     assert(o.kind() == LINK);
323
324     std::vector<double> points;
325     controller.getObjectProperty(o.id(), o.kind(), CONTROL_POINTS, points);
326
327     // iterate on attributes
328     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
329     {
330         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
331         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
332         switch (current)
333         {
334             case e_x:
335                 points.push_back(to_double(xmlTextReaderConstValue(reader)));
336                 break;
337             case e_y:
338                 points.push_back(to_double(xmlTextReaderConstValue(reader)));
339                 break;
340             default:
341                 // ignore other parameters
342                 // TODO: Does other metamodels might be managed there ?
343                 break;
344         }
345     }
346
347     controller.setObjectProperty(o.id(), o.kind(), CONTROL_POINTS, points);
348     return 1;
349 }
350
351 int XMIResource::loadGeometry(xmlTextReaderPtr reader, const model::BaseObject& o)
352 {
353     assert(o.kind() == BLOCK || o.kind() == ANNOTATION || o.kind() == LINK);
354
355     std::vector<double> geom;
356     controller.getObjectProperty(o.id(), o.kind(), GEOMETRY, geom);
357     geom.resize(4);
358
359     // iterate on attributes
360     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
361     {
362         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
363         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
364         switch (current)
365         {
366             case e_x:
367                 geom[0] = to_double(xmlTextReaderConstValue(reader));
368                 break;
369             case e_y:
370                 geom[1] = to_double(xmlTextReaderConstValue(reader));
371                 break;
372             case e_width:
373                 geom[2] = to_double(xmlTextReaderConstValue(reader));
374                 break;
375             case e_height:
376                 geom[3] = to_double(xmlTextReaderConstValue(reader));
377                 break;
378             default:
379                 // ignore other parameters
380                 // TODO: Does other metamodels might be managed there ?
381                 break;
382         }
383     }
384
385     controller.setObjectProperty(o.id(), o.kind(), GEOMETRY, geom);
386     return 1;
387 }
388
389 int XMIResource::loadAbstractBaseObject(xmlTextReaderPtr reader, const model::BaseObject& o)
390 {
391     assert(o.kind() == BLOCK || o.kind() == ANNOTATION || o.kind() == LINK);
392
393     // abstract Layer is not decoded there as it has no attribute
394
395     // iterate on attributes
396     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
397     {
398         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
399         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
400         switch (current)
401         {
402             case e_uid:
403             {
404                 std::string uid = to_string(xmlTextReaderConstValue(reader));
405                 controller.setObjectProperty(o.id(), o.kind(), UID, uid);
406                 references.insert(std::make_pair(uid, o.id()));
407                 break;
408             }
409             case e_parentDiagram:
410             {
411                 // not lookup needed ; only one diagram is serialized at a time
412                 controller.setObjectProperty(o.id(), o.kind(), PARENT_DIAGRAM, root);
413                 break;
414             }
415             case e_parent:
416             {
417                 // not lookup needed thanks to the XML hierarchy
418                 const model::BaseObject& parent = *(processed.end() - 2);
419                 controller.setObjectProperty(o.id(), o.kind(), PARENT_BLOCK, parent.id());
420                 break;
421             }
422             default:
423                 // ignore other parameters
424                 // TODO: Does other metamodels might be managed there ?
425                 break;
426         }
427     }
428
429     return 1;
430 }
431
432 int XMIResource::loadDiagram(xmlTextReaderPtr reader, const model::BaseObject& o)
433 {
434     assert(o.kind() == DIAGRAM);
435
436     // abstract Layer is not decoded there as it has no attribute
437
438     // iterate on attributes
439     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
440     {
441         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
442         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
443         switch (current)
444         {
445             case e_title:
446                 controller.setObjectProperty(o.id(), o.kind(), TITLE, to_string(xmlTextReaderConstValue(reader)));
447                 break;
448             case e_path:
449                 controller.setObjectProperty(o.id(), o.kind(), PATH, to_string(xmlTextReaderConstValue(reader)));
450                 break;
451             case e_debugLevel:
452                 controller.setObjectProperty(o.id(), o.kind(), DEBUG_LEVEL, to_int(xmlTextReaderConstValue(reader)));
453                 break;
454             case e_version:
455                 controller.setObjectProperty(o.id(), o.kind(), VERSION_NUMBER, to_string(xmlTextReaderConstValue(reader)));
456                 break;
457             default:
458                 // ignore other parameters
459                 // TODO: Does other metamodels might be managed there ?
460                 break;
461         }
462     }
463
464     return 1;
465 }
466
467 int XMIResource::loadSimulationConfig(xmlTextReaderPtr reader, const model::BaseObject& o)
468 {
469     assert(o.kind() == DIAGRAM);
470
471     std::vector<double> properties;
472     controller.getObjectProperty(o.id(), o.kind(), PROPERTIES, properties);
473     properties.resize(8);
474
475     // iterate on attributes
476     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
477     {
478         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
479         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
480         switch (current)
481         {
482             case e_finalTime:
483                 properties[0] = to_double(xmlTextReaderConstValue(reader));
484                 break;
485             case e_absoluteTolerance:
486                 properties[1] = to_double(xmlTextReaderConstValue(reader));
487                 break;
488             case e_relativeTolerance:
489                 properties[2] = to_double(xmlTextReaderConstValue(reader));
490                 break;
491             case e_timeTolerance:
492                 properties[3] = to_double(xmlTextReaderConstValue(reader));
493                 break;
494             case e_deltaT:
495                 properties[4] = to_double(xmlTextReaderConstValue(reader));
496                 break;
497             case e_realtimeScale:
498                 properties[5] = to_double(xmlTextReaderConstValue(reader));
499                 break;
500             case e_solver:
501                 properties[6] = to_double(xmlTextReaderConstValue(reader));
502                 break;
503             case e_deltaH:
504                 properties[7] = to_double(xmlTextReaderConstValue(reader));
505                 break;
506             default:
507                 // ignore other parameters
508                 // TODO: Does other metamodels might be managed there ?
509                 break;
510         }
511     }
512
513     controller.setObjectProperty(o.id(), o.kind(), PROPERTIES, properties);
514     return 1;
515 }
516
517 int XMIResource::loadBlock(xmlTextReaderPtr reader, const model::BaseObject& o)
518 {
519     assert(o.kind() == BLOCK);
520
521     // load the base class
522     int ret = loadAbstractBaseObject(reader, o);
523     if (ret != 1)
524     {
525         return ret;
526     }
527
528     // Layer has no attribute so there is no need to decode it there
529     // Geometry is handled as an element
530
531     // iterate on attributes
532     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
533     {
534         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
535         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
536         switch (current)
537         {
538             case e_description:
539                 controller.setObjectProperty(o.id(), o.kind(), DESCRIPTION, to_string(xmlTextReaderConstValue(reader)));
540                 break;
541             case e_label:
542                 controller.setObjectProperty(o.id(), o.kind(), LABEL, to_string(xmlTextReaderConstValue(reader)));
543                 break;
544             case e_style:
545                 controller.setObjectProperty(o.id(), o.kind(), STYLE, to_string(xmlTextReaderConstValue(reader)));
546                 break;
547             case e_interfaceFunction:
548                 controller.setObjectProperty(o.id(), o.kind(), INTERFACE_FUNCTION, to_string(xmlTextReaderConstValue(reader)));
549                 break;
550             case e_functionName:
551                 controller.setObjectProperty(o.id(), o.kind(), SIM_FUNCTION_NAME, to_string(xmlTextReaderConstValue(reader)));
552                 break;
553             case e_functionAPI:
554                 controller.setObjectProperty(o.id(), o.kind(), SIM_FUNCTION_API, to_int(xmlTextReaderConstValue(reader)));
555                 break;
556             case e_dependsOnT:
557             {
558                 std::vector<int> dep_ut;
559                 controller.getObjectProperty(o.id(), o.kind(), SIM_DEP_UT, dep_ut);
560                 dep_ut.resize(2);
561
562                 dep_ut[1] = to_int(xmlTextReaderConstValue(reader));
563                 controller.setObjectProperty(o.id(), o.kind(), SIM_DEP_UT, dep_ut);
564                 break;
565             }
566             case e_dependsOnU:
567             {
568                 std::vector<int> dep_ut;
569                 controller.getObjectProperty(o.id(), o.kind(), SIM_DEP_UT, dep_ut);
570                 dep_ut.resize(2);
571
572                 dep_ut[0] = to_int(xmlTextReaderConstValue(reader));
573                 controller.setObjectProperty(o.id(), o.kind(), SIM_DEP_UT, dep_ut);
574                 break;
575             }
576             case e_blocktype:
577                 controller.setObjectProperty(o.id(), o.kind(), SIM_BLOCKTYPE, to_string(xmlTextReaderConstValue(reader)));
578                 break;
579             default:
580                 // ignore other parameters
581                 // TODO: Does other metamodels might be managed there ?
582                 break;
583         }
584     }
585
586     /*
587      * Reset some properties loaded as array and initialized with non-empty value
588      */
589     std::vector<int> empty_int_array;
590     controller.setObjectProperty(o.id(), o.kind(), NZCROSS, empty_int_array);
591     controller.setObjectProperty(o.id(), o.kind(), NMODE, empty_int_array);
592
593     return 1;
594 }
595
596 int XMIResource::loadPort(xmlTextReaderPtr reader, const model::BaseObject& o)
597 {
598     assert(o.kind() == PORT);
599
600     // ignore datatype as it is managed as an XML node
601
602     // iterate on attributes
603     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
604     {
605         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
606         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
607         switch (current)
608         {
609             case e_uid:
610             {
611                 std::string uid = to_string(xmlTextReaderConstValue(reader));
612                 controller.setObjectProperty(o.id(), o.kind(), UID, uid);
613                 references.insert(std::make_pair(uid, o.id()));
614                 break;
615             }
616             case e_firing:
617                 controller.setObjectProperty(o.id(), o.kind(), FIRING, to_double(xmlTextReaderConstValue(reader)));
618                 break;
619             case e_sourceBlock:
620             {
621                 // not lookup needed thanks to the XML hierarchy
622                 const model::BaseObject& parent = processed.back();
623                 controller.setObjectProperty(o.id(), o.kind(), SOURCE_BLOCK, parent.id());
624                 break;
625             }
626             case e_kind:
627             {
628                 std::string portKindStr = to_string(xmlTextReaderConstValue(reader));
629                 int k;
630                 if ("in" == portKindStr)
631                 {
632                     k = PORT_IN;
633                 }
634                 else if ("out" == portKindStr)
635                 {
636                     k = PORT_OUT;
637                 }
638                 else if ("ein" == portKindStr)
639                 {
640                     k = PORT_EIN;
641                 }
642                 else if ("eout" == portKindStr)
643                 {
644                     k = PORT_EOUT;
645                 }
646                 else
647                 {
648                     k = PORT_UNDEF;
649                 }
650                 controller.setObjectProperty(o.id(), o.kind(), PORT_KIND, k);
651                 break;
652             }
653             case e_implicit:
654                 controller.setObjectProperty(o.id(), o.kind(), IMPLICIT, to_int(xmlTextReaderConstValue(reader)));
655                 break;
656             case e_connectedSignal:
657                 // will be resolved later
658                 unresolved.push_back(
659                     unresolvedReference(o.id(), o.kind(), CONNECTED_SIGNALS,
660                                         to_string(xmlTextReaderConstValue(reader))));
661                 break;
662             case e_style:
663                 controller.setObjectProperty(o.id(), o.kind(), STYLE, to_string(xmlTextReaderConstValue(reader)));
664                 break;
665             case e_label:
666                 controller.setObjectProperty(o.id(), o.kind(), LABEL, to_string(xmlTextReaderConstValue(reader)));
667                 break;
668             default:
669                 // ignore other parameters
670                 // TODO: Does other metamodels might be managed there ?
671                 break;
672         }
673     }
674
675     return 1;
676 }
677
678 int XMIResource::loadLink(xmlTextReaderPtr reader, const model::BaseObject& o)
679 {
680     assert(o.kind() == LINK);
681
682     // load the base class
683     int ret = loadAbstractBaseObject(reader, o);
684     if (ret != 1)
685     {
686         return ret;
687     }
688
689     // geometry is handled as in independent node
690     // controlPoint is handled as in independent node
691
692     // iterate on attributes
693     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
694     {
695         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
696         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
697         switch (current)
698         {
699             case e_uid:
700             {
701                 std::string uid = to_string(xmlTextReaderConstValue(reader));
702                 controller.setObjectProperty(o.id(), o.kind(), UID, uid);
703                 references.insert(std::make_pair(uid, o.id()));
704                 break;
705             }
706             case e_sourcePort:
707                 // will be resolved later
708                 unresolved.push_back(
709                     unresolvedReference(o.id(), o.kind(), SOURCE_PORT, to_string(xmlTextReaderConstValue(reader))));
710                 break;
711             case e_destinationPort:
712                 // will be resolved later
713                 unresolved.push_back(
714                     unresolvedReference(o.id(), o.kind(), DESTINATION_PORT,
715                                         to_string(xmlTextReaderConstValue(reader))));
716                 break;
717             case e_style:
718                 controller.setObjectProperty(o.id(), o.kind(), STYLE, to_string(xmlTextReaderConstValue(reader)));
719                 break;
720             case e_label:
721                 controller.setObjectProperty(o.id(), o.kind(), LABEL, to_string(xmlTextReaderConstValue(reader)));
722                 break;
723             case e_lineWidth:
724             {
725                 std::vector<double> thick;
726                 controller.getObjectProperty(o.id(), o.kind(), THICK, thick);
727                 thick[0] = to_double(xmlTextReaderConstValue(reader));
728                 controller.setObjectProperty(o.id(), o.kind(), THICK, thick);
729                 break;
730             }
731             case e_lineHeight:
732             {
733                 std::vector<double> thick;
734                 controller.getObjectProperty(o.id(), o.kind(), THICK, thick);
735                 thick[1] = to_double(xmlTextReaderConstValue(reader));
736                 controller.setObjectProperty(o.id(), o.kind(), THICK, thick);
737                 break;
738             }
739             case e_color:
740                 controller.setObjectProperty(o.id(), o.kind(), COLOR, to_int(xmlTextReaderConstValue(reader)));
741                 break;
742             default:
743                 // ignore other parameters
744                 // TODO: Does other metamodels might be managed there ?
745                 break;
746         }
747     }
748
749     return ret;
750 }
751
752 int XMIResource::loadAnnotation(xmlTextReaderPtr reader, const model::BaseObject& o)
753 {
754     assert(o.kind() == ANNOTATION);
755
756     // load the base class
757     int ret = loadAbstractBaseObject(reader, o);
758     if (ret != 1)
759     {
760         return ret;
761     }
762
763     // geometry is handled as a node
764
765     // iterate on attributes
766     for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
767     {
768         auto found = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstName(reader));
769         enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
770         switch (current)
771         {
772             case e_description:
773                 controller.setObjectProperty(o.id(), o.kind(), DESCRIPTION, to_string(xmlTextReaderConstValue(reader)));
774                 break;
775             case e_font:
776                 controller.setObjectProperty(o.id(), o.kind(), FONT, to_string(xmlTextReaderConstValue(reader)));
777                 break;
778             case e_fontSize:
779                 controller.setObjectProperty(o.id(), o.kind(), FONT_SIZE, to_string(xmlTextReaderConstValue(reader)));
780                 break;
781             case e_style:
782                 controller.setObjectProperty(o.id(), o.kind(), STYLE, to_string(xmlTextReaderConstValue(reader)));
783                 break;
784             default:
785                 // ignore other parameters
786                 // TODO: Does other metamodels might be managed there ?
787                 break;
788         }
789     }
790
791     return ret;
792 }
793
794 int XMIResource::processNode(xmlTextReaderPtr reader)
795 {
796     // manage only xcos related XML nodes
797     const xmlChar* nsURI = xmlTextReaderConstNamespaceUri(reader);
798     if (nsURI == xcosNamespaceUri || nsURI == nullptr)
799     {
800         xmlReaderTypes nodeType = (xmlReaderTypes) xmlTextReaderNodeType(reader);
801         switch (nodeType)
802         {
803             case XML_READER_TYPE_NONE:
804                 return 1;
805             case XML_READER_TYPE_ELEMENT:
806                 return processElement(reader);
807             case XML_READER_TYPE_ATTRIBUTE:
808                 sciprint("xmlReader attributes node not supported\n");
809                 return -1;
810             case XML_READER_TYPE_TEXT:
811                 return processText(reader);
812             case XML_READER_TYPE_CDATA:
813                 return processText(reader);
814             case XML_READER_TYPE_ENTITY_REFERENCE:
815                 sciprint("xmlReader entity reference not supported\n");
816                 return -1;
817             case XML_READER_TYPE_ENTITY:
818                 sciprint("xmlReader entity not supported\n");
819                 return -1;
820             case XML_READER_TYPE_PROCESSING_INSTRUCTION:
821                 sciprint("xmlReader processing instruction not supported\n");
822                 return -1;
823             case XML_READER_TYPE_COMMENT:
824                 return 1;
825             case XML_READER_TYPE_DOCUMENT:
826                 return 1;
827             case XML_READER_TYPE_DOCUMENT_TYPE:
828                 sciprint("xmlReader document type not supported\n");
829                 return -1;
830             case XML_READER_TYPE_DOCUMENT_FRAGMENT:
831                 sciprint("xmlReader document fragment not supported\n");
832                 return -1;
833             case XML_READER_TYPE_NOTATION:
834                 sciprint("xmlReader notation not supported\n");
835                 return -1;
836             case XML_READER_TYPE_WHITESPACE:
837                 sciprint("xmlReader whitespace not supported\n");
838                 return -1;
839             case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
840                 return 1; // ignore indent or end-of-line
841             case XML_READER_TYPE_END_ELEMENT:
842                 return processEndElement(reader);
843             case XML_READER_TYPE_END_ENTITY:
844                 sciprint("xmlReader end entity not supported\n");
845                 return -1;
846             case XML_READER_TYPE_XML_DECLARATION:
847                 sciprint("xmlReader XML declaration not supported\n");
848                 return -1;
849         }
850     }
851     else
852     {
853         // TODO mixed model should be preserved in some way and restored back on XMIResource_save.cpp .
854     }
855     sciprint("unable to process node\n");
856     return -1;
857 }
858
859 int XMIResource::processElement(xmlTextReaderPtr reader)
860 {
861     const xmlChar *name = xmlTextReaderConstLocalName(reader);
862     parent = NB_XCOS_NAMES;
863
864     // lookup for known node names
865     // thanks to the string intern-ing, the pointer comparison could be used
866     auto found = std::find(constXcosNames.begin(), constXcosNames.end(), name);
867     enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
868     switch (current)
869     {
870         case e_Diagram:
871         {
872             // the root diagram should be decoded
873             model::BaseObject o(root, DIAGRAM);
874
875             processed.push_back(o);
876             return loadDiagram(reader, o);
877         }
878         case e_child:
879         {
880             // this is a child of a diagram, resolve the type and call the loaders
881             // iterate on attributes to lookup for EMF type
882
883             // iterate on attributes
884             for (int rc = xmlTextReaderMoveToFirstAttribute(reader); rc > 0; rc = xmlTextReaderMoveToNextAttribute(reader))
885             {
886                 const xmlChar* nsURI  = xmlTextReaderConstNamespaceUri(reader);
887                 if (nsURI != xsiNamespaceUri)
888                 {
889                     continue;
890                 }
891
892                 auto foundName = std::find(constXcosNames.begin(), constXcosNames.end(), xmlTextReaderConstLocalName(reader));
893                 enum xcosNames currentName = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), foundName));
894                 if (currentName != e_type)
895                 {
896                     continue;
897                 }
898
899                 const xmlChar* value = xmlTextReaderConstValue(reader);
900                 const xmlChar* valueWithoutPrefix = BAD_CAST(std::strchr((const char*) value, ':'));
901                 if (valueWithoutPrefix == nullptr)
902                 {
903                     valueWithoutPrefix = value;
904                 }
905                 else
906                 {
907                     // remove the leading ':'
908                     valueWithoutPrefix = valueWithoutPrefix + 1;
909                 }
910                 const xmlChar* interned = xmlTextReaderConstString(reader, valueWithoutPrefix);
911
912                 auto found = std::find(constXcosNames.begin(), constXcosNames.end(), interned);
913                 enum xcosNames current = static_cast<enum xcosNames>(std::distance(constXcosNames.begin(), found));
914                 switch (current)
915                 {
916                     case e_Block:
917                     {
918                         ScicosID o = controller.createObject(BLOCK);
919
920                         // assign the child
921                         model::BaseObject parent = processed.back();
922                         std::vector<ScicosID> children;
923                         controller.getObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
924                         children.push_back(o);
925                         controller.setObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
926
927                         model::BaseObject child(o, BLOCK);
928                         processed.push_back(child);
929                         return loadBlock(reader, child);
930                     }
931                     case e_Link:
932                     {
933                         ScicosID o = controller.createObject(LINK);
934
935                         // assign the child
936                         model::BaseObject parent = processed.back();
937                         std::vector<ScicosID> children;
938                         controller.getObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
939                         children.push_back(o);
940                         controller.setObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
941
942                         model::BaseObject child(o, LINK);
943                         processed.push_back(child);
944                         return loadLink(reader, child);
945                     }
946                     case e_Annotation:
947                     {
948                         ScicosID o = controller.createObject(ANNOTATION);
949
950                         // assign the child
951                         model::BaseObject parent = processed.back();
952                         std::vector<ScicosID> children;
953                         controller.getObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
954                         children.push_back(o);
955                         controller.setObjectProperty(parent.id(), parent.kind(), CHILDREN, children);
956
957                         model::BaseObject child(o, ANNOTATION);
958                         return loadAnnotation(reader, child);
959                     }
960                     default:
961                         sciprint("Not handled child type=%s at line %d\n", *found,
962                                  xmlTextReaderGetParserLineNumber(reader) - 1);
963                         return -1;
964                 }
965             }
966             break;
967         }
968         case e_in: // no break on purpose
969         case e_out: // no break on purpose
970         case e_ein: // no break on purpose
971         case e_eout:
972         {
973             ScicosID o = controller.createObject(PORT);
974
975             enum object_properties_t p;
976             switch (current)
977             {
978                 case e_in:
979                     p = INPUTS;
980                     break;
981                 case e_out:
982                     p = OUTPUTS;
983                     break;
984                 case e_ein:
985                     p = EVENT_INPUTS;
986                     break;
987                 case e_eout:
988                     p = EVENT_OUTPUTS;
989                     break;
990                 default:
991                     return -1;
992             }
993
994             model::BaseObject parent = processed.back();
995             // add the port them to the parent
996             std::vector<ScicosID> ports;
997             controller.getObjectProperty(parent.id(), parent.kind(), p, ports);
998             ports.push_back(o);
999             controller.setObjectProperty(parent.id(), parent.kind(), p, ports);
1000
1001             // decode content
1002             model::BaseObject child(o, PORT);
1003             return loadPort(reader, child);
1004         }
1005         case e_geometry:
1006             // geometry is used for rectangle coordinates of its parent
1007             return loadGeometry(reader, processed.back());
1008         case e_nzcross:
1009             // nzcross is a Block property
1010             if (!xmlTextReaderIsEmptyElement(reader))
1011             {
1012                 parent = current;
1013             }
1014             return 1;
1015         case e_nmode:
1016             // nmode is a Block property
1017             if (!xmlTextReaderIsEmptyElement(reader))
1018             {
1019                 parent = current;
1020             }
1021             return 1;
1022         case e_rpar:
1023             // rpar is a Block property
1024             if (!xmlTextReaderIsEmptyElement(reader))
1025             {
1026                 parent = current;
1027             }
1028             return 1;
1029         case e_ipar:
1030             // ipar is a Block property
1031             if (!xmlTextReaderIsEmptyElement(reader))
1032             {
1033                 parent = current;
1034             }
1035             return 1;
1036         case e_opar:
1037             // ipar is a Block property
1038             return loadBase64(reader, OPAR, processed.back());
1039         case e_state:
1040             // state is a Block property
1041             if (!xmlTextReaderIsEmptyElement(reader))
1042             {
1043                 parent = current;
1044             }
1045             return 1;
1046         case e_dstate:
1047             // dstate is a Block property
1048             if (!xmlTextReaderIsEmptyElement(reader))
1049             {
1050                 parent = current;
1051             }
1052             return 1;
1053         case e_odstate:
1054             // odstate is a Block property
1055             return loadBase64(reader, ODSTATE, processed.back());
1056         case e_equations:
1057             // equation is a Block property
1058             return loadBase64(reader, EQUATIONS, processed.back());
1059         case e_expression:
1060             // expression is a Block property
1061             if (!xmlTextReaderIsEmptyElement(reader))
1062             {
1063                 parent = current;
1064             }
1065             return 1;
1066         case e_exprs:
1067             // exprs is a Block property
1068             return loadBase64(reader, EXPRS, processed.back());
1069         case e_controlPoint:
1070             // controlPoint is a link property
1071             return loadPoint(reader, processed.back());
1072         case e_context:
1073             // context is a Layer property
1074             if (!xmlTextReaderIsEmptyElement(reader))
1075             {
1076                 parent = current;
1077             }
1078             return 1;
1079         case e_properties:
1080             // properties is a Diagram property
1081             return loadSimulationConfig(reader, processed.back());
1082         case e_datatype:
1083             // datatype is a Port property
1084             if (!xmlTextReaderIsEmptyElement(reader))
1085             {
1086                 parent = current;
1087             }
1088             return 1;
1089         default:
1090             sciprint("Unknown \"%s\" element name at line %d\n", name, xmlTextReaderGetParserLineNumber(reader) - 1);
1091             return -1;
1092     }
1093
1094     return 1;
1095 }
1096
1097 int XMIResource::processText(xmlTextReaderPtr reader)
1098 {
1099     int ret;
1100
1101     switch (parent)
1102     {
1103         case e_nzcross:
1104             // nzcross is a Block property
1105             ret = loadIntArray(reader, NZCROSS, processed.back());
1106             break;
1107         case e_nmode:
1108             // nmode is a Block property
1109             ret = loadIntArray(reader, NMODE, processed.back());
1110             break;
1111         case e_rpar:
1112             // rpar is a Block property
1113             ret = loadDoubleArray(reader, RPAR, processed.back());
1114             break;
1115         case e_ipar:
1116             // ipar is a Block property
1117             ret = loadIntArray(reader, IPAR, processed.back());
1118             break;
1119         case e_state:
1120             // state is a Block property
1121             ret = loadDoubleArray(reader, RPAR, processed.back());
1122             break;
1123         case e_dstate:
1124             // dstate is a Block property
1125             ret = loadDoubleArray(reader, RPAR, processed.back());
1126             break;
1127         case e_expression:
1128             // expression is a Block property
1129             ret = loadStringArray(reader, EXPRS, processed.back());
1130             break;
1131         case e_context:
1132             // context is a Layer property
1133             ret = loadStringArray(reader, DIAGRAM_CONTEXT, processed.back());
1134             break;
1135         case e_datatype:
1136             // datatype is a port property
1137             ret = loadIntArray(reader, DATATYPE, processed.back());
1138             break;
1139         default:
1140             sciprint("Unable to decode text value at line %d\n", xmlTextReaderGetParserLineNumber(reader) - 1);
1141             ret = -1;
1142             break;
1143     }
1144
1145     return ret;
1146 }
1147
1148 int XMIResource::processEndElement(xmlTextReaderPtr)
1149 {
1150     if (parent == NB_XCOS_NAMES)
1151     {
1152         processed.pop_back();
1153     }
1154     else
1155     {
1156         parent = NB_XCOS_NAMES;
1157     }
1158
1159     return 1;
1160 }
1161
1162 } /* namespace org_scilab_modules_xcos */