/* Copyright (C) 2012 Modelon AB This program is free software: you can redistribute it and/or modify it under the terms of the BSD style license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the FMILIB_License.txt file for more details. You should have received a copy of the FMILIB_License.txt file along with this program. If not, contact Modelon AB . */ #include #include #include #include "fmi_xml_context_impl.h" static char* MODULE="FMIXML"; fmi_xml_context_t* fmi_xml_allocate_context( jm_callbacks* callbacks) { jm_callbacks* cb; fmi_xml_context_t* c; jm_log_debug(callbacks, MODULE, "Allocating context for XML parsing module"); if(callbacks) { cb = callbacks; } else { cb = jm_get_default_callbacks(); } c = cb->malloc(sizeof(fmi_xml_context_t)); if(!c) { jm_log_fatal(callbacks, MODULE, "Could not allocate memory"); return 0; } c->callbacks = callbacks; c->parser = 0; c->fmi_version = fmi_version_unknown_enu; c->configuration = 0; jm_log_debug(callbacks, MODULE, "Returning allocated context"); return c; } void fmi_xml_free_context(fmi_xml_context_t *context) { jm_log_debug(context->callbacks, MODULE, "Releasing XML parsing module memory"); if(!context) return; if(context->parser) { XML_ParserFree(context->parser); context->parser = 0; } context->callbacks->free(context); } void fmi_xml_set_configuration(fmi_xml_context_t *context, int configuration) { context->configuration = configuration; } void fmi_xml_fatal(fmi_xml_context_t *context, const char* fmt, ...) { va_list args; va_start (args, fmt); jm_log_fatal_v(context->callbacks, MODULE, fmt, args); va_end (args); XML_StopParser(context->parser,0); } void XMLCALL fmi_xml_parse_element_start(void *c, const char *elm, const char **attr) { fmi_xml_context_t *context = (fmi_xml_context_t*)c; const char* fmiVersion = 0; int i = 0; if(strcmp(elm, "fmiModelDescription") != 0) { fmi_xml_fatal(context, "First element in XML must be fmiModelDescription"); return; } while(attr[i]) { if(strcmp(attr[i], "fmiVersion") == 0) { fmiVersion = attr[i+1]; break; } i+=2; } if(!fmiVersion) { fmi_xml_fatal(context, "Could not find fmiVersion attribute in the XML. Cannot proceed."); return; } if( strcmp(fmiVersion, "1.0") == 0 ) { jm_log_verbose(context->callbacks, MODULE, "XML specifies FMI 1.0"); context->fmi_version = fmi_version_1_enu; XML_StopParser(context->parser,0); return; } else if( strcmp(fmiVersion, "2.0") == 0 ) { jm_log_verbose(context->callbacks, MODULE, "XML specifies FMI 2.0"); context->fmi_version = fmi_version_2_0_enu; XML_StopParser(context->parser,0); return; } else { fmi_xml_fatal(context, "This version of FMI standard is not supported (fmiVersion=%s)", fmiVersion); return; } } void XMLCALL fmi_xml_parse_element_end(void* c, const char *elm) { } void XMLCALL fmi_xml_parse_element_data(void* c, const XML_Char *s, int len) { } fmi_version_enu_t fmi_xml_get_fmi_version(fmi_xml_context_t* context, const char* filename) { XML_Memory_Handling_Suite memsuite; XML_Parser parser = NULL; FILE* file; jm_log_verbose(context->callbacks, MODULE, "Parsing XML to detect FMI standard version"); memsuite.malloc_fcn = context->callbacks->malloc; memsuite.realloc_fcn = context->callbacks->realloc; memsuite.free_fcn = context->callbacks->free; context -> parser = parser = XML_ParserCreate_MM(0, &memsuite, 0); if(! parser) { fmi_xml_fatal(context, "Could not initialize XML parsing library."); fmi_xml_free_context(context); return fmi_version_unknown_enu; } XML_SetUserData( parser, context); XML_SetElementHandler(parser, fmi_xml_parse_element_start, fmi_xml_parse_element_end); XML_SetCharacterDataHandler(parser, fmi_xml_parse_element_data); file = fopen(filename, "rb"); if (file == NULL) { fmi_xml_fatal(context, "Cannot open file '%s' for parsing", filename); return fmi_version_unknown_enu; } context->fmi_version = fmi_version_unknown_enu; #define XML_BLOCK_SIZE 1000 while (!feof(file)) { char text[XML_BLOCK_SIZE]; int n = (int)fread(text, sizeof(char), XML_BLOCK_SIZE, file); if(ferror(file)) { fmi_xml_fatal(context, "Error reading from file %s", filename); fclose(file); return fmi_version_unknown_enu; } if (!XML_Parse(parser, text, n, feof(file)) && (context->fmi_version == fmi_version_unknown_enu)) { fmi_xml_fatal(context, "Parse error at line %d:\n%s", (int)XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser))); fclose(file); return fmi_version_unknown_enu; /* failure */ } if(context->fmi_version != fmi_version_unknown_enu) break; } fclose(file); if(context->fmi_version == fmi_version_unknown_enu) { fmi_xml_fatal(context, "Could not detect FMI standard version"); } return context->fmi_version; }