2 Copyright (C) 2012 Modelon AB
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the BSD style license.
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.
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>.
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>
24 #include "fmi2_xml_model_description_impl.h"
25 #include "fmi2_xml_parser.h"
27 static const char * module = "FMI2XML";
29 #define ATTR_STR(attr) #attr,
30 const char *fmi2_xmlAttrNames[fmi2_xml_attr_number] = {
31 FMI2_XML_ATTRLIST(ATTR_STR)
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}
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}
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}
79 #define fmi2_xml_scheme_Annotations {fmi2_xml_elmID_ScalarVariable, 1, 0}
80 #define fmi2_xml_scheme_VariableTool {fmi2_xml_elmID_Annotations, 0, 1}
82 #define EXPAND_ELM_SCHEME(elm) fmi2_xml_scheme_##elm ,
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)};
89 #define EXPAND_ELM_NAME(elm) { #elm, fmi2_xml_handle_##elm, fmi2_xml_elmID_##elm},
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)
97 void fmi2_xml_parse_free_context(fmi2_xml_parser_context_t *context) {
99 if(context->modelDescription)
100 fmi2_xml_clear_model_description(context->modelDescription);
101 if(context->parser) {
102 XML_ParserFree(context->parser);
105 fmi2_xml_free_parse_buffer(context);
106 if(context->attrMap) {
107 jm_vector_free(jm_named_ptr)(context->attrMap);
108 context->attrMap = 0;
110 if(context->elmMap) {
111 jm_vector_free(fmi2_xml_element_handle_map_t)(context->elmMap);
114 if(context->attrBuffer) {
115 jm_vector_free(jm_string)(context->attrBuffer);
116 context->attrBuffer = 0;
118 jm_stack_free_data(int)(& context->elmStack );
119 jm_vector_free_data(char)( &context->elmData );
121 context->callbacks->free(context);
124 void fmi2_xml_parse_fatal(fmi2_xml_parser_context_t *context, const char* fmt, ...) {
126 va_start (args, fmt);
127 jm_log_fatal_v(context->callbacks, module, fmt, args);
129 XML_StopParser(context->parser,0);
132 void fmi2_xml_parse_error(fmi2_xml_parser_context_t *context, const char* fmt, ...) {
134 va_start (args, fmt);
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);
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);
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) {
148 jm_string elmName, attrName, value;
150 elmName = fmi2_element_handle_map[elmID].elementName;
151 attrName = fmi2_xmlAttrNames[attrID];
152 value = jm_vector_get_item(jm_string)(context->attrBuffer, attrID);
154 jm_vector_set_item(jm_string)(context->attrBuffer, attrID, 0);
157 fmi2_xml_parse_fatal(context, "Parsing XML element '%s': required attribute '%s' not found", elmName, attrName);
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) {
168 jm_string elmName, attrName, val;
170 ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&val);
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);
178 elmName = fmi2_element_handle_map[elmID].elementName;
179 attrName = fmi2_xmlAttrNames[attrID];
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);
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);
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) {
194 jm_string elmName, attrName, strVal;
196 ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
198 if(!strVal && !required) {
203 elmName = fmi2_element_handle_map[elmID].elementName;
204 attrName = fmi2_xmlAttrNames[attrID];
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);
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) {
216 jm_string elmName, attrName, strVal;
218 ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
220 if(!strVal && !required) {
225 elmName = fmi2_element_handle_map[elmID].elementName;
226 attrName = fmi2_xmlAttrNames[attrID];
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);
234 *field = nameMap[i].ID;
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);
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) {
245 jm_string elmName, attrName, strVal;
247 ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
249 if(!strVal && !required) {
254 elmName = fmi2_element_handle_map[elmID].elementName;
255 attrName = fmi2_xmlAttrNames[attrID];
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);
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) {
267 jm_string elmName, attrName, strVal;
270 ret = fmi2_xml_get_attr_str(context, elmID, attrID,required,&strVal);
272 if(!strVal && !required) {
277 elmName = fmi2_element_handle_map[elmID].elementName;
278 attrName = fmi2_xmlAttrNames[attrID];
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);
287 int fmi2_xml_alloc_parse_buffer(fmi2_xml_parser_context_t *context, size_t items) {
289 jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
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");
295 jm_vector_zero(jm_voidp)(parseBuffer);
299 void fmi2_xml_free_parse_buffer(fmi2_xml_parser_context_t *context) {
301 jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
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);
307 jm_vector_free_data(jm_voidp)(parseBuffer);
310 jm_vector(char) * fmi2_xml_reserve_parse_buffer(fmi2_xml_parser_context_t *context, size_t index, size_t size) {
312 jm_vector(jm_voidp)* parseBuffer = &context->parseBuffer;
313 jm_vector(char) * item = jm_vector_get_item(jm_voidp)(parseBuffer,index);
315 item = jm_vector_alloc(char)(size,size,context->callbacks);
316 jm_vector_set_item(jm_voidp)(parseBuffer,index,item);
318 fmi2_xml_parse_fatal(context, "Could not allocate a buffer for parsing XML");
323 if(jm_vector_resize(char)(item, size) < size ) {
324 fmi2_xml_parse_fatal(context, "Could not allocate a buffer for parsing XML");
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);
338 int fmi2_create_attr_map(fmi2_xml_parser_context_t* context) {
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++) {
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);
351 jm_vector_qsort(jm_named_ptr)(context->attrMap, jm_compare_named);
355 int fmi2_create_elm_map(fmi2_xml_parser_context_t* context) {
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);
363 jm_vector_qsort(fmi2_xml_element_handle_map_t)(context->elmMap, fmi2_xml_compare_elmName);
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);
373 currentElMap->elementHandle = fmi2_element_handle_map[id].elementHandle;;
374 currentElMap->elemID = id;
378 static void XMLCALL fmi2_parse_element_start(void *c, const char *elm, const char **attr) {
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;
385 fmi2_xml_parser_context_t *context = c;
386 context->has_produced_data_warning = 0;
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);
394 fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
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);
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);
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;
418 currentID = currentElMap->elemID;
419 /* Check that parent-child & siblings are fine */
421 fmi2_xml_elm_enu_t parentID = context->currentElmID;
422 fmi2_xml_elm_enu_t siblingID = context->lastElmID;
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;
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;
442 int lastSiblingIndex = fmi2_xml_scheme_info[siblingID].siblingIndex;
443 int curSiblingIndex = fmi2_xml_scheme_info[currentID].siblingIndex;
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;
454 context->lastElmID = fmi2_xml_elmID_none;
457 /* process the attributes */
461 /* find attribute by name */
462 currentMap = jm_vector_bsearch(jm_named_ptr)(context->attrMap, &key, jm_compare_named);
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.",
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]);
477 else if(strcmp(localName, "schemaLocation") == 0) {
481 jm_log_error(context->callbacks, module, "Unknown attribute '%s=%s' in XML", attr[i], attr[i+1]);
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)
490 jm_log_warning(context->callbacks, module,
491 "FMI API function fmiGetPartialDerivatives is removed from the specification. Attribute %s will be ignored.", attr[i]);
495 jm_log_error(context->callbacks, module, "Unknown attribute '%s=%s' in XML", attr[i], attr[i+1]);
499 /* save attr value (still as string) for further handling */
500 const char** mapItem = (const char**)currentMap->ptr;
501 *mapItem = attr[i+1];
506 /* handle the element */
507 if( currentElMap->elementHandle(context, 0) ) {
508 /* try to skip and continue anyway */
509 if(!context->skipElementCnt) context->skipElementCnt = 1;
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);
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);
523 context -> currentElmID = currentID;
526 static void XMLCALL fmi2_parse_element_end(void* c, const char *elm) {
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;
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);
539 fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
545 if(context->skipElementCnt) {
546 context->skipElementCnt--;
550 keyEl.elementName = elm;
551 currentElMap = jm_vector_bsearch(fmi2_xml_element_handle_map_t)(context->elmMap, &keyEl, fmi2_xml_compare_elmName);
554 fmi2_xml_parse_fatal(context, "Unknown element end in XML (element: %s)", elm);
557 currentID = currentElMap->elemID;
559 if(currentID != context -> currentElmID) {
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);
566 jm_vector_push_back(char)(&context->elmData, 0);
568 if( currentElMap->elementHandle(context, jm_vector_get_itemp(char)(&context->elmData, 0) )) {
571 jm_vector_resize(char)(&context->elmData, 0);
573 /* record the last handle and pop the stack */
574 context->lastElmID = currentID;
576 if(jm_stack_is_empty(int)(&context->elmStack)) {
577 context -> currentElmID = fmi2_xml_elmID_none;
580 context -> currentElmID = (fmi2_xml_elm_enu_t)jm_stack_pop(int)(&context->elmStack);
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.
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".
592 static void XMLCALL fmi2_parse_element_data(void* c, const XML_Char *s, int len) {
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);
600 fmi2_xml_parse_fatal(context, "User element handle returned non-zero error code %d", ret);
605 if(context->skipElementCnt) {
608 for(i = 0; i< len;i++) {
610 if((ch != '\n') && (ch != ' ') && (ch != '\t')) {
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;
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);
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.",
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);
649 yyfmi2lex_destroy(scanner);
653 int fmi2_xml_parse_model_description(fmi2_xml_model_description_t* md,
654 const char* filename,
655 fmi2_xml_callbacks_t* xml_callbacks,
657 XML_Memory_Handling_Suite memsuite;
658 fmi2_xml_parser_context_t* context;
659 XML_Parser parser = NULL;
662 context = (fmi2_xml_parser_context_t*)md->callbacks->calloc(1, sizeof(fmi2_xml_parser_context_t));
664 jm_log_fatal(md->callbacks, "FMIXML", "Could not allocate memory for XML parser context");
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);
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;
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, "|");
692 fmi2_xml_parse_fatal(context, "Could not initialize XML parsing library.");
693 fmi2_xml_parse_free_context(context);
697 XML_SetUserData( parser, context);
699 XML_SetElementHandler(parser, fmi2_parse_element_start, fmi2_parse_element_end);
701 XML_SetCharacterDataHandler(parser, fmi2_parse_element_data);
703 file = fopen(filename, "rb");
705 fmi2_xml_parse_fatal(context, "Cannot open file '%s' for parsing", filename);
706 fmi2_xml_parse_free_context(context);
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);
714 fmi2_xml_parse_fatal(context, "Error reading from file %s", filename);
716 fmi2_xml_parse_free_context(context);
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)));
724 fmi2_xml_parse_free_context(context);
725 return -1; /* failure */
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);
736 if (configuration & FMI2_XML_NAME_CHECK) {
737 fmi2_check_variable_naming_conventions(md);
740 md->status = fmi2_xml_model_description_enu_ok;
741 context->modelDescription = 0;
742 fmi2_xml_parse_free_context(context);
747 #define JM_TEMPLATE_INSTANCE_TYPE fmi2_xml_element_handle_map_t
748 #include "JM/jm_vector_template.h"