]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_parser.c
Switch to full JavaSE-11+ compatibility
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / XML / src / FMI2 / fmi2_xml_parser.c
1 /*
2     Copyright (C) 2012 Modelon AB
3
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the BSD style license.
6
7      This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     FMILIB_License.txt file for more details.
11
12     You should have received a copy of the FMILIB_License.txt file
13     along with this program. If not, contact Modelon AB <http://www.modelon.com>.
14 */
15
16 #include <string.h>
17 #include <stdio.h>
18
19 /* For checking variable naming conventions */
20 #include <fmi2_xml_variable_name_parser.tab.h>
21 #define YYSTYPE YYFMI2STYPE
22 #include <fmi2_xml_variable_name_lex.h>
23
24 #include "fmi2_xml_model_description_impl.h"
25 #include "fmi2_xml_parser.h"
26
27 static const char * module = "FMI2XML";
28
29 #define ATTR_STR(attr) #attr,
30 const char *fmi2_xmlAttrNames[fmi2_xml_attr_number] = {
31     FMI2_XML_ATTRLIST(ATTR_STR)
32 };
33
34 /* fmi2_xml_scheme_ defines give parent ID, the index in a sequence among siblings, flag if multiple elems are allowed */
35 #define fmi2_xml_scheme_fmiModelDescription {fmi2_xml_elmID_none, 0, 0}
36 #define fmi2_xml_scheme_ModelExchange {fmi2_xml_elmID_fmiModelDescription, 0, 0}
37 #define fmi2_xml_scheme_SourceFiles {fmi2_xml_elmID_ModelExchange, 0, 0}
38 #define fmi2_xml_scheme_File {fmi2_xml_elmID_SourceFiles, 0, 1}
39 #define fmi2_xml_scheme_CoSimulation {fmi2_xml_elmID_fmiModelDescription, 1, 0}
40 #define fmi2_xml_scheme_SourceFilesCS {fmi2_xml_elmID_CoSimulation, 0, 0}
41 #define fmi2_xml_scheme_FileCS {fmi2_xml_elmID_SourceFilesCS, 0, 1}
42 #define fmi2_xml_scheme_UnitDefinitions {fmi2_xml_elmID_fmiModelDescription, 2, 0}
43 #define fmi2_xml_scheme_Unit {fmi2_xml_elmID_UnitDefinitions, 0, 1}
44 #define fmi2_xml_scheme_BaseUnit {fmi2_xml_elmID_Unit, 0, 0}
45 #define fmi2_xml_scheme_DisplayUnit {fmi2_xml_elmID_Unit, 1, 1}
46 #define fmi2_xml_scheme_TypeDefinitions {fmi2_xml_elmID_fmiModelDescription, 3, 0}
47 #define fmi2_xml_scheme_SimpleType {fmi2_xml_elmID_TypeDefinitions, 0, 1}
48 #define fmi2_xml_scheme_Real {fmi2_xml_elmID_SimpleType, 0, 0}
49 #define fmi2_xml_scheme_Integer {fmi2_xml_elmID_SimpleType, 0, 0}
50 #define fmi2_xml_scheme_Boolean {fmi2_xml_elmID_SimpleType, 0, 0}
51 #define fmi2_xml_scheme_String {fmi2_xml_elmID_SimpleType, 0, 0}
52 #define fmi2_xml_scheme_Enumeration {fmi2_xml_elmID_SimpleType, 0, 0}
53 #define fmi2_xml_scheme_Item {fmi2_xml_elmID_Enumeration, 0, 1}
54 #define fmi2_xml_scheme_LogCategories {fmi2_xml_elmID_fmiModelDescription, 4, 0}
55 #define fmi2_xml_scheme_Category {fmi2_xml_elmID_LogCategories, 0, 1}
56 #define fmi2_xml_scheme_DefaultExperiment {fmi2_xml_elmID_fmiModelDescription, 5, 0}
57 #define fmi2_xml_scheme_VendorAnnotations {fmi2_xml_elmID_fmiModelDescription, 6, 0}
58 #define fmi2_xml_scheme_Tool {fmi2_xml_elmID_VendorAnnotations, 0, 1}
59 #define fmi2_xml_scheme_ModelVariables {fmi2_xml_elmID_fmiModelDescription, 7, 0}
60 #define fmi2_xml_scheme_ScalarVariable {fmi2_xml_elmID_ModelVariables, 0, 1}
61
62 #define fmi2_xml_scheme_ModelStructure {fmi2_xml_elmID_fmiModelDescription, 8, 0}
63 #define fmi2_xml_scheme_Outputs {fmi2_xml_elmID_ModelStructure, 0, 0}
64 /*#define fmi2_xml_scheme_OutputUnknown {fmi2_xml_elmID_Outputs, 0, 1}*/
65 #define fmi2_xml_scheme_Unknown {fmi2_xml_elmID_Outputs, 0, 1}
66 #define fmi2_xml_scheme_Derivatives {fmi2_xml_elmID_ModelStructure, 1, 0}
67 #define fmi2_xml_scheme_DerivativeUnknown {fmi2_xml_elmID_Derivatives, 0, 1}
68 #define fmi2_xml_scheme_DiscreteStates {fmi2_xml_elmID_ModelStructure, 2, 0}
69 #define fmi2_xml_scheme_DiscreteStateUnknown {fmi2_xml_elmID_DiscreteStates, 0, 1}
70 #define fmi2_xml_scheme_InitialUnknowns {fmi2_xml_elmID_ModelStructure, 3, 0}
71 #define fmi2_xml_scheme_InitialUnknown {fmi2_xml_elmID_InitialUnknowns, 0, 1}
72
73 #define fmi2_xml_scheme_RealVariable {fmi2_xml_elmID_ScalarVariable, 0, 0}
74 #define fmi2_xml_scheme_IntegerVariable {fmi2_xml_elmID_ScalarVariable, 0, 0}
75 #define fmi2_xml_scheme_BooleanVariable {fmi2_xml_elmID_ScalarVariable, 0, 0}
76 #define fmi2_xml_scheme_StringVariable {fmi2_xml_elmID_ScalarVariable, 0, 0}
77 #define fmi2_xml_scheme_EnumerationVariable {fmi2_xml_elmID_ScalarVariable, 0, 0}
78
79 #define fmi2_xml_scheme_Annotations {fmi2_xml_elmID_ScalarVariable, 1, 0}
80 #define fmi2_xml_scheme_VariableTool {fmi2_xml_elmID_Annotations, 0, 1}
81
82 #define EXPAND_ELM_SCHEME(elm) fmi2_xml_scheme_##elm ,
83
84 fmi2_xml_scheme_info_t fmi2_xml_scheme_info[fmi2_xml_elm_number] = {
85     FMI2_XML_ELMLIST(EXPAND_ELM_SCHEME)
86         {fmi2_xml_elm_actual_number,0,0},
87         FMI2_XML_ELMLIST_ALT(EXPAND_ELM_SCHEME)};
88
89 #define EXPAND_ELM_NAME(elm) { #elm, fmi2_xml_handle_##elm, fmi2_xml_elmID_##elm},
90
91 fmi2_xml_element_handle_map_t fmi2_element_handle_map[fmi2_xml_elm_number] = {
92     FMI2_XML_ELMLIST(EXPAND_ELM_NAME)
93         { NULL, NULL, fmi2_xml_elm_actual_number},
94         FMI2_XML_ELMLIST_ALT(EXPAND_ELM_NAME)
95 };
96
97 void fmi2_xml_parse_free_context(fmi2_xml_parser_context_t *context) {
98     if(!context) return;
99     if(context->modelDescription)
100         fmi2_xml_clear_model_description(context->modelDescription);
101     if(context->parser) {
102         XML_ParserFree(context->parser);
103         context->parser = 0;
104     }
105     fmi2_xml_free_parse_buffer(context);
106     if(context->attrMap) {
107         jm_vector_free(jm_named_ptr)(context->attrMap);
108         context->attrMap = 0;
109     }
110     if(context->elmMap) {
111         jm_vector_free(fmi2_xml_element_handle_map_t)(context->elmMap);
112         context->elmMap = 0;
113     }
114     if(context->attrBuffer) {
115         jm_vector_free(jm_string)(context->attrBuffer);
116         context->attrBuffer = 0;
117     }
118     jm_stack_free_data(int)(& context->elmStack );
119     jm_vector_free_data(char)( &context->elmData );
120
121     context->callbacks->free(context);
122 }
123
124 void fmi2_xml_parse_fatal(fmi2_xml_parser_context_t *context, const char* fmt, ...) {
125     va_list args;
126     va_start (args, fmt);
127         jm_log_fatal_v(context->callbacks, module, fmt, args);
128     va_end (args);
129     XML_StopParser(context->parser,0);
130 }
131
132 void fmi2_xml_parse_error(fmi2_xml_parser_context_t *context, const char* fmt, ...) {
133     va_list args;
134     va_start (args, fmt);
135         if(context->parser)
136                 jm_log_info(context->callbacks, module, "[Line:%u] Detected during parsing:", XML_GetCurrentLineNumber(context->parser));
137         jm_log_error_v(context->callbacks, module,fmt, args);
138     va_end (args);
139 }
140
141
142 int fmi2_xml_is_attr_defined(fmi2_xml_parser_context_t *context, fmi2_xml_attr_enu_t attrID) {
143     return ( jm_vector_get_item(jm_string)(context->attrBuffer, attrID) != 0);
144 }
145
146 int fmi2_xml_get_attr_str(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required,const char** valp) {
147
148     jm_string elmName, attrName, value;
149
150     elmName = fmi2_element_handle_map[elmID].elementName;
151     attrName = fmi2_xmlAttrNames[attrID];
152     value = jm_vector_get_item(jm_string)(context->attrBuffer, attrID);
153     *valp =  value;
154     jm_vector_set_item(jm_string)(context->attrBuffer, attrID, 0);
155     if(!(*valp)) {
156         if (required) {
157             fmi2_xml_parse_fatal(context, "Parsing XML element '%s': required attribute '%s' not found", elmName, attrName);
158             return -1;
159         }
160         else
161             return 0;
162     }
163     return 0;
164 }
165
166 int fmi2_xml_set_attr_string(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, jm_vector(char)* field) {
167     int ret;
168     jm_string elmName, attrName, val;
169     size_t len;
170     ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&val);
171     if(ret) return ret;
172     if((!val || !val[0]) && !required) {
173         jm_vector_resize(char)(field, 1);
174         jm_vector_set_item(char)(field, 0, 0);
175         jm_vector_resize(char)(field, 0);
176         return 0;
177     }
178     elmName = fmi2_element_handle_map[elmID].elementName;
179     attrName = fmi2_xmlAttrNames[attrID];
180
181     len = strlen(val) + 1;
182     if(jm_vector_resize(char)(field, len) < len) {
183         fmi2_xml_parse_fatal(context, "XML element '%s': could not allocate memory for setting '%s'='%s'", elmName, attrName, val);
184         return -1;
185     }
186     /* copy terminating 0 as well but set vector size to be actual string length */
187     memcpy(jm_vector_get_itemp(char)(field,0), val, len);
188     jm_vector_resize(char)(field, len - 1);
189     return 0;
190 }
191
192 int fmi2_xml_set_attr_uint(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, unsigned int* field, unsigned int defaultVal) {
193     int ret;
194     jm_string elmName, attrName, strVal;    
195
196     ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
197     if(ret) return ret;
198     if(!strVal && !required) {
199         *field = defaultVal;
200         return 0;
201     }
202
203     elmName = fmi2_element_handle_map[elmID].elementName;
204     attrName = fmi2_xmlAttrNames[attrID];
205
206     if(sscanf(strVal, "%u", field) != 1) {
207         fmi2_xml_parse_error(context, "XML element '%s': could not parse value for unsigned attribute '%s'='%s'", elmName, attrName, strVal);
208         return -1;
209     }
210     return 0;
211 }
212
213
214 int fmi2_xml_set_attr_enum(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, unsigned int* field, unsigned int defaultVal, jm_name_ID_map_t* nameMap) {
215     int ret, i;
216     jm_string elmName, attrName, strVal;
217
218     ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
219     if(ret) return ret;
220     if(!strVal && !required) {
221         *field = defaultVal;
222         return 0;
223     }
224
225     elmName = fmi2_element_handle_map[elmID].elementName;
226     attrName = fmi2_xmlAttrNames[attrID];
227
228     i = 0;
229     while(nameMap[i].name && strcmp(nameMap[i].name, strVal)) i++;
230     if(!nameMap[i].name) {
231         fmi2_xml_parse_error(context, "XML element '%s': could not parse value for enumeration attribute '%s'='%s'", elmName, attrName, strVal);
232         return -1;
233     }
234     *field = nameMap[i].ID;
235     return 0;
236 }
237
238 int fmi2_xml_set_attr_boolean(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, unsigned int* field, unsigned int defaultVal) {
239     jm_name_ID_map_t fmi_boolean_i_dMap[] = {{"true", 1},{"false", 0}, {"1", 1},{"0", 0}, {0,0}};
240     return fmi2_xml_set_attr_enum(context,elmID, attrID,required, field, defaultVal, fmi_boolean_i_dMap);
241 }
242
243 int fmi2_xml_set_attr_int(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, int* field, int defaultVal) {
244     int ret;
245     jm_string elmName, attrName, strVal;
246
247     ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
248     if(ret) return ret;
249     if(!strVal && !required) {        
250                 *field = defaultVal;
251         return 0;
252     }
253
254     elmName = fmi2_element_handle_map[elmID].elementName;
255     attrName = fmi2_xmlAttrNames[attrID];
256
257     if(sscanf(strVal, "%d", field) != 1) {
258         fmi2_xml_parse_error(context, "XML element '%s': could not parse value for integer attribute '%s'='%s'", elmName, attrName, strVal);
259         return -1;
260     }
261     return 0;
262 }
263
264 int fmi2_xml_set_attr_double(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t elmID, fmi2_xml_attr_enu_t attrID, int required, double* field, double defaultVal) {
265
266     int ret;
267     jm_string elmName, attrName, strVal;
268
269
270     ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
271     if(ret) return ret;
272     if(!strVal && !required) {
273         *field = defaultVal;
274         return 0;
275     }
276
277     elmName = fmi2_element_handle_map[elmID].elementName;
278     attrName = fmi2_xmlAttrNames[attrID];
279
280     if(sscanf(strVal, "%lf", field) != 1) {
281         fmi2_xml_parse_error(context, "XML element '%s': could not parse value for real attribute '%s'='%s'", elmName, attrName, strVal);
282         return -1;
283     }
284     return 0;
285 }
286
287 int fmi2_xml_alloc_parse_buffer(fmi2_xml_parser_context_t *context, size_t items) {
288
289     jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
290
291     if(jm_vector_init(jm_voidp)(parseBuffer,items,context->callbacks) < items) {
292         fmi2_xml_parse_fatal(context, "Could not allocate buffer for parsing XML");
293         return -1;
294     }
295     jm_vector_zero(jm_voidp)(parseBuffer);
296     return 0;
297 }
298
299 void fmi2_xml_free_parse_buffer(fmi2_xml_parser_context_t *context) {
300     size_t i;
301     jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
302
303     for(i=0; i < jm_vector_get_size(jm_voidp)(parseBuffer); i++) {
304         jm_vector(char) * item = jm_vector_get_item(jm_voidp)(parseBuffer,i);
305         if(item) jm_vector_free(char)(item);
306     }
307     jm_vector_free_data(jm_voidp)(parseBuffer);
308 }
309
310 jm_vector(char) * fmi2_xml_reserve_parse_buffer(fmi2_xml_parser_context_t *context, size_t index, size_t size) {
311
312     jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
313     jm_vector(char) * item = jm_vector_get_item(jm_voidp)(parseBuffer,index);
314     if(!item) {
315         item = jm_vector_alloc(char)(size,size,context->callbacks);
316         jm_vector_set_item(jm_voidp)(parseBuffer,index,item);
317         if(!item) {
318             fmi2_xml_parse_fatal(context, "Could not allocate a buffer for parsing XML");
319             return 0;
320         }
321     }
322     else {
323         if(jm_vector_resize(char)(item, size) < size ) {
324             fmi2_xml_parse_fatal(context, "Could not allocate a buffer for parsing XML");
325             return 0;
326         }
327     }
328     return item;
329 }
330
331 jm_vector(char) * fmi2_xml_get_parse_buffer(fmi2_xml_parser_context_t *context, size_t index) {
332     jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
333     return jm_vector_get_item(jm_voidp)(parseBuffer,index);
334 }
335
336
337
338 int fmi2_create_attr_map(fmi2_xml_parser_context_t* context) {
339     int i;
340     context->attrBuffer = jm_vector_alloc(jm_string)(fmi2_xml_attr_number, fmi2_xml_attr_number, context->callbacks);
341     if(!context->attrBuffer) return -1;
342     context->attrMap = jm_vector_alloc(jm_named_ptr)(fmi2_xml_attr_number, fmi2_xml_attr_number, context->callbacks);
343     if(!context->attrMap) return -1;
344     for(i = 0; i < fmi2_xml_attr_number; i++) {
345         jm_named_ptr map;
346         jm_vector_set_item(jm_string)(context->attrBuffer, i, 0);
347         map.name = fmi2_xmlAttrNames[i];
348         map.ptr = (void*)(jm_vector_get_itemp(jm_string)(context->attrBuffer, i));
349         jm_vector_set_item(jm_named_ptr)(context->attrMap, i, map);
350     }
351     jm_vector_qsort(jm_named_ptr)(context->attrMap, jm_compare_named);
352     return 0;
353 }
354
355 int fmi2_create_elm_map(fmi2_xml_parser_context_t* context) {
356     size_t i;
357     context->elmMap = jm_vector_alloc(fmi2_xml_element_handle_map_t)(fmi2_xml_elm_actual_number, fmi2_xml_elm_number, context->callbacks);
358     if(!context->elmMap) return -1;
359     for(i = 0; i < fmi2_xml_elm_actual_number; i++) {
360         fmi2_xml_element_handle_map_t item = fmi2_element_handle_map[i];
361         jm_vector_set_item(fmi2_xml_element_handle_map_t)(context->elmMap, i, item);
362     }
363     jm_vector_qsort(fmi2_xml_element_handle_map_t)(context->elmMap, fmi2_xml_compare_elmName);
364     return 0;
365 }
366
367 void fmi2_xml_set_element_handle(fmi2_xml_parser_context_t *context, const char* elm, fmi2_xml_elm_enu_t id) {
368     fmi2_xml_element_handle_map_t keyEl;
369     fmi2_xml_element_handle_map_t* currentElMap;
370         keyEl.elementName = elm;
371     currentElMap = jm_vector_bsearch(fmi2_xml_element_handle_map_t)(context->elmMap, &keyEl, fmi2_xml_compare_elmName);
372         
373         currentElMap->elementHandle = fmi2_element_handle_map[id].elementHandle;;
374         currentElMap->elemID = id;
375 }
376
377
378 static void XMLCALL fmi2_parse_element_start(void *c, const char *elm, const char **attr) {
379     jm_named_ptr key;
380     fmi2_xml_element_handle_map_t keyEl;
381     fmi2_xml_element_handle_map_t* currentElMap;
382     jm_named_ptr* currentMap;
383         fmi2_xml_elm_enu_t currentID;
384     int i;
385     fmi2_xml_parser_context_t *context = c;
386     context->has_produced_data_warning = 0;
387
388         if(context->useAnyHandleFlg) {
389                 fmi2_xml_callbacks_t* anyH = context->anyHandle;
390                 context->anyElmCount++;
391                 if(anyH && anyH->startHandle) {
392             int ret = anyH->startHandle(anyH->context, context->anyToolName, context->anyParent, elm, attr);            
393                         if(ret != 0) {
394                                 fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
395                         }
396                 }
397                 return;
398         }
399
400         if(context->skipElementCnt) {
401                 context->skipElementCnt++;
402         jm_log_warning(context->callbacks, module, "[Line:%u] Skipping nested XML element '%s'",
403                         XML_GetCurrentLineNumber(context->parser), elm);
404                 return;
405         }
406         
407         keyEl.elementName = elm;
408         /* find the element handle by name */
409     currentElMap = jm_vector_bsearch(fmi2_xml_element_handle_map_t)(context->elmMap, &keyEl, fmi2_xml_compare_elmName);
410     if(!currentElMap) {
411         /* not found error*/
412         jm_log_error(context->callbacks, module, "[Line:%u] Unknown element '%s' in XML, skipping",
413                         XML_GetCurrentLineNumber(context->parser), elm);
414                 context->skipElementCnt = 1;
415         return;
416     }
417
418     currentID = currentElMap->elemID;
419         /* Check that parent-child & siblings are fine */
420         {
421                 fmi2_xml_elm_enu_t parentID = context->currentElmID;
422                 fmi2_xml_elm_enu_t siblingID =  context->lastElmID;
423
424                 if(fmi2_xml_scheme_info[currentID].parentID != parentID) {
425                                 jm_log_error(context->callbacks, module, 
426                                         "[Line:%u] XML element '%s' cannot be placed inside '%s', skipping",
427                                         XML_GetCurrentLineNumber(context->parser), elm, fmi2_element_handle_map[parentID].elementName);
428                                 context->skipElementCnt = 1;
429                                 return;
430                 }
431                 if(siblingID != fmi2_xml_elmID_none) {
432                         if(siblingID == currentID) {
433                                 if(!fmi2_xml_scheme_info[currentID].multipleAllowed) {
434                                         jm_log_error(context->callbacks, module, 
435                                                 "[Line:%u] Multiple instances of XML element '%s' are not allowed, skipping",
436                                                 XML_GetCurrentLineNumber(context->parser), elm);
437                                         context->skipElementCnt = 1;
438                                         return;
439                                 }
440                         }
441                         else {
442                                 int lastSiblingIndex = fmi2_xml_scheme_info[siblingID].siblingIndex;
443                                 int curSiblingIndex = fmi2_xml_scheme_info[currentID].siblingIndex;
444
445                                 if(lastSiblingIndex >= curSiblingIndex) {
446                                         jm_log_error(context->callbacks, module, 
447                                                 "[Line:%u] XML element '%s' cannot be placed after element '%s', skipping",
448                                                 XML_GetCurrentLineNumber(context->parser), elm, fmi2_element_handle_map[siblingID].elementName);
449                                         context->skipElementCnt = 1;
450                                         return;
451                                 }
452                         }
453                 }
454                 context->lastElmID = fmi2_xml_elmID_none;
455         }
456
457     /* process the attributes  */
458     i = 0;
459     while(attr[i]) {
460         key.name = attr[i];
461         /* find attribute by name  */
462         currentMap = jm_vector_bsearch(jm_named_ptr)(context->attrMap, &key, jm_compare_named);
463         if(!currentMap) {
464 #define XMLSchema_instance "http://www.w3.org/2001/XMLSchema-instance"
465                         const size_t stdNSlen = strlen(XMLSchema_instance);
466             const size_t attrStrLen = strlen(attr[i]);
467                         if((attrStrLen > stdNSlen) && (attr[i][stdNSlen] == '|') && (strncmp(attr[i], XMLSchema_instance, stdNSlen) == 0)) {
468                                 const char* localName = attr[i] + stdNSlen + 1;
469                                 if(     strcmp(localName, "noNamespaceSchemaLocation") == 0)
470                                         jm_log_warning(context->callbacks, module, "Attribute noNamespaceSchemaLocation='%s' is ignored. Using standard fmiModelDescription.xsd.",
471                                         attr[i+1]);
472                                 else if((strcmp(localName, "nil") == 0)
473                                         ||  (strcmp(localName, "type") == 0)) {
474                                                 jm_log_warning(context->callbacks, module, "Attribute {" XMLSchema_instance "}%s=%s is ignored",
475                                                         localName, attr[i+1]);
476                                 }
477                                 else if(strcmp(localName, "schemaLocation") == 0) {
478                                         /* just skip this */
479                                 }
480                                 else {
481                                         jm_log_error(context->callbacks, module, "Unknown attribute '%s=%s' in XML", attr[i], attr[i+1]);
482                                 }
483                         }
484                         else if(
485                                 (strcmp("providesPartialDerivativesOf_DerivativeFunction_wrt_States", attr[i]) == 0) ||\r
486                                 (strcmp("providesPartialDerivativesOf_DerivativeFunction_wrt_Inputs", attr[i]) == 0) ||\r
487                                 (strcmp("providesPartialDerivativesOf_OutputFunction_wrt_States", attr[i]) == 0) ||
488                                 (strcmp("providesPartialDerivativesOf_OutputFunction_wrt_Inputs", attr[i]) == 0)
489                                 ) {
490                                         jm_log_warning(context->callbacks, module, 
491                                                 "FMI API function fmiGetPartialDerivatives is removed from the specification. Attribute %s will be ignored.", attr[i]);
492                         }
493                         else {
494                                 /* not found error*/
495                                 jm_log_error(context->callbacks, module, "Unknown attribute '%s=%s' in XML", attr[i], attr[i+1]);
496                         }
497         }
498                 else  {
499             /* save attr value (still as string) for further handling  */
500             const char** mapItem = (const char**)currentMap->ptr;
501             *mapItem = attr[i+1];
502         }
503         i += 2;
504     }
505
506     /* handle the element */
507         if( currentElMap->elementHandle(context, 0) ) {
508                 /* try to skip and continue anyway */
509         if(!context->skipElementCnt) context->skipElementCnt = 1; 
510     }
511         if(context->skipElementCnt) return;
512     /* check that the element handle had process all the attributes */
513     for(i = 0; i < fmi2_xml_attr_number; i++) {
514         if(jm_vector_get_item(jm_string)(context->attrBuffer, i)) {
515             if(!context->skipOneVariableFlag)
516                 jm_log_warning(context->callbacks,module, "Attribute '%s' not processed by element '%s' handle", fmi2_xmlAttrNames[i], elm);
517             jm_vector_set_item(jm_string)(context->attrBuffer, i,0);
518         }
519     }
520     if(context -> currentElmID != fmi2_xml_elmID_none) { /* with nested elements: put the parent on the stack*/
521         jm_stack_push(int)(&context->elmStack, context -> currentElmID);
522     }
523     context -> currentElmID = currentID;
524 }
525
526 static void XMLCALL fmi2_parse_element_end(void* c, const char *elm) {
527
528     fmi2_xml_element_handle_map_t keyEl;
529     fmi2_xml_element_handle_map_t* currentElMap;
530         fmi2_xml_elm_enu_t currentID;
531     fmi2_xml_parser_context_t *context = c;
532
533         if(context->useAnyHandleFlg && (context->anyElmCount > 0)) {
534                 fmi2_xml_callbacks_t* anyH = context->anyHandle;
535                 context->anyElmCount--;
536                 if(anyH && anyH->endHandle) {
537                         int ret = anyH->endHandle(anyH->context, elm);
538                         if(ret != 0) {
539                                 fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
540                         }
541                 }
542                 return;
543         }
544
545         if(context->skipElementCnt) {
546                 context->skipElementCnt--;
547                 return;
548         }
549
550     keyEl.elementName = elm;
551     currentElMap = jm_vector_bsearch(fmi2_xml_element_handle_map_t)(context->elmMap, &keyEl, fmi2_xml_compare_elmName);
552     if(!currentElMap) {
553         /* not found error*/
554         fmi2_xml_parse_fatal(context, "Unknown element end in XML (element: %s)", elm);
555         return;
556     }
557     currentID = currentElMap->elemID;
558
559     if(currentID != context -> currentElmID) {
560         /* missmatch error*/
561         fmi2_xml_parse_fatal(context, "Element end '%s' does not match element start '%s' in XML", elm, 
562                         fmi2_element_handle_map[context -> currentElmID].elementName);
563         return;
564     }
565
566     jm_vector_push_back(char)(&context->elmData, 0);
567
568         if( currentElMap->elementHandle(context, jm_vector_get_itemp(char)(&context->elmData, 0) )) {
569         return;
570     }
571     jm_vector_resize(char)(&context->elmData, 0);
572
573     /* record the last handle and pop the stack */
574     context->lastElmID = currentID;
575
576     if(jm_stack_is_empty(int)(&context->elmStack)) {
577         context -> currentElmID = fmi2_xml_elmID_none;
578     }
579     else {
580         context -> currentElmID = (fmi2_xml_elm_enu_t)jm_stack_pop(int)(&context->elmStack);
581     }
582 }
583
584 /*
585 *  Called to handle element data, e.g. "xy" in <Name>xy</Name>
586 *  Can be called many times, e.g. with "x" and then with "y" in the example above.
587 *  Feature in expat:
588 *  For some reason, if the element data is the empty string (Eg. <a></a>)
589 *  instead of an empty string with len == 0 we get "\n". The workaround is
590 *  to replace this with the empty string whenever we encounter "\n".
591 */
592 static void XMLCALL fmi2_parse_element_data(void* c, const XML_Char *s, int len) {
593                 int i;
594         fmi2_xml_parser_context_t *context = c;
595                 if(context->useAnyHandleFlg && (context->anyElmCount > 0)) {
596                         fmi2_xml_callbacks_t* anyH = context->anyHandle;
597                         if(anyH && anyH->dataHandle) {
598                                 int ret = anyH->dataHandle(anyH->context, s, len);
599                                 if(ret != 0) {
600                                         fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
601                                 }
602                         }
603                         return;
604                 }
605                 if(context->skipElementCnt) {
606                         return;
607                 }
608                 for(i = 0; i< len;i++) {
609             char ch = s[i];
610             if((ch != '\n') && (ch != ' ') && (ch != '\t')) {
611                 break;
612             }
613         }
614
615                 if((i != len) && !context->has_produced_data_warning) {
616                         jm_log_warning(context->callbacks, module, "[Line:%u] Skipping unexpected XML element data",
617                                         XML_GetCurrentLineNumber(context->parser));
618                         context->has_produced_data_warning = 1;
619                 }
620 }
621
622 void fmi2_check_variable_naming_conventions(fmi2_xml_model_description_t *md) {
623     size_t n = jm_vector_get_size(jm_named_ptr)(&md->variablesByName);
624     size_t k;
625     yyscan_t scanner;
626     YY_BUFFER_STATE buf;
627
628     /* check for duplicate variable names */
629     for (k = 1; k < n; k++) {
630         const char *v1 = jm_vector_get_item(jm_named_ptr)(&md->variablesByName, k - 1).name;
631         const char *v2 = jm_vector_get_item(jm_named_ptr)(&md->variablesByName, k).name;
632         if(strcmp(v1, v2) == 0) {
633             jm_log_error(md->callbacks, module,
634                     "Two variables with the same name %s found. This is not allowed.",
635                     v1);
636         }
637     }
638
639     /* check variable name syntax */
640     if (md->namingConvension == fmi2_naming_enu_structured) {
641         yyfmi2lex_init(&scanner);
642         for (k = 0; k < n; k++) {
643             char *name = ((fmi2_xml_variable_t *) jm_vector_get_item(jm_voidp)(
644                     md->variablesOrigOrder, k))->name;
645             buf = yyfmi2_scan_string(name, scanner);
646             yyfmi2parse(scanner, md->callbacks, name);
647             yyfmi2_delete_buffer(buf, scanner);
648         }
649         yyfmi2lex_destroy(scanner);
650     }
651 }
652
653 int fmi2_xml_parse_model_description(fmi2_xml_model_description_t* md,
654                                      const char* filename,
655                                      fmi2_xml_callbacks_t* xml_callbacks,
656                                      int configuration) {
657     XML_Memory_Handling_Suite memsuite;
658     fmi2_xml_parser_context_t* context;
659     XML_Parser parser = NULL;
660     FILE* file;
661
662     context = (fmi2_xml_parser_context_t*)md->callbacks->calloc(1, sizeof(fmi2_xml_parser_context_t));
663     if(!context) {
664         jm_log_fatal(md->callbacks, "FMIXML", "Could not allocate memory for XML parser context");
665     }
666     context->callbacks = md->callbacks;
667     context->modelDescription = md;
668     if(fmi2_xml_alloc_parse_buffer(context, 16)) return -1;
669     if(fmi2_create_attr_map(context) || fmi2_create_elm_map(context)) {
670         fmi2_xml_parse_fatal(context, "Error in parsing initialization");
671         fmi2_xml_parse_free_context(context);
672         return -1;
673     }
674     context->lastBaseUnit = 0;
675     context->skipOneVariableFlag = 0;
676         context->skipElementCnt = 0;
677     jm_stack_init(int)(&context->elmStack,  context->callbacks);
678     jm_vector_init(char)(&context->elmData, 0, context->callbacks);
679     context->lastElmID = fmi2_xml_elmID_none;
680     context->currentElmID = fmi2_xml_elmID_none;
681         context->anyElmCount = 0;
682         context->useAnyHandleFlg = 0;
683     context->anyParent = 0;
684         context->anyHandle = xml_callbacks;
685
686     memsuite.malloc_fcn = context->callbacks->malloc;
687     memsuite.realloc_fcn = context->callbacks->realloc;
688     memsuite.free_fcn = context->callbacks->free;
689     context -> parser = parser = XML_ParserCreate_MM(0, &memsuite, "|");
690
691     if(! parser) {
692         fmi2_xml_parse_fatal(context, "Could not initialize XML parsing library.");
693         fmi2_xml_parse_free_context(context);
694         return -1;
695     }
696
697     XML_SetUserData( parser, context);
698
699     XML_SetElementHandler(parser, fmi2_parse_element_start, fmi2_parse_element_end);
700
701     XML_SetCharacterDataHandler(parser, fmi2_parse_element_data);
702
703     file = fopen(filename, "rb");
704     if (file == NULL) {
705         fmi2_xml_parse_fatal(context, "Cannot open file '%s' for parsing", filename);
706         fmi2_xml_parse_free_context(context);
707         return -1;
708     }
709
710     while (!feof(file)) {
711         char * text = jm_vector_get_itemp(char)(fmi2_xml_reserve_parse_buffer(context,0,XML_BLOCK_SIZE),0);
712         int n = (int)fread(text, sizeof(char), XML_BLOCK_SIZE, file);
713         if(ferror(file)) {
714             fmi2_xml_parse_fatal(context, "Error reading from file %s", filename);
715             fclose(file);
716                 fmi2_xml_parse_free_context(context);
717             return -1;
718         }
719         if (!XML_Parse(parser, text, n, feof(file))) {
720              fmi2_xml_parse_fatal(context, "Parse error at line %d:\n%s",
721                          (int)XML_GetCurrentLineNumber(parser),
722                          XML_ErrorString(XML_GetErrorCode(parser)));
723              fclose(file);
724                      fmi2_xml_parse_free_context(context);
725              return -1; /* failure */
726         }        
727     }
728     fclose(file);
729     /* done later XML_ParserFree(parser);*/
730     if(!jm_stack_is_empty(int)(&context->elmStack)) {
731         fmi2_xml_parse_fatal(context, "Unexpected end of file (not all elements ended) when parsing %s", filename);
732         fmi2_xml_parse_free_context(context);
733         return -1;
734     }
735
736     if (configuration & FMI2_XML_NAME_CHECK) {
737         fmi2_check_variable_naming_conventions(md);
738     }
739
740     md->status = fmi2_xml_model_description_enu_ok;
741     context->modelDescription = 0;
742     fmi2_xml_parse_free_context(context);
743
744     return 0;
745 }
746
747 #define JM_TEMPLATE_INSTANCE_TYPE fmi2_xml_element_handle_map_t
748 #include "JM/jm_vector_template.h"