/* * Copyright QTronic GmbH. All rights reserved. */ /* See ptolemy/actor/lib/fmi/ma/fmusdk-license.htm for the complete FMUSDK License. */ /* ---------------------------------------------------------------------------* * XmlElement.cpp * Elements implementation that describe content of model description of a * FMI 2.0 model. All elements have Element as parent class. Elements have * attributes and other specific content. * * Author: Adrian Tirea * ---------------------------------------------------------------------------*/ #include "XmlElement.h" #include "XmlParserException.h" #include #include #ifdef STANDALONE_XML_PARSER #define logThis(n, ...) printf(__VA_ARGS__);printf("\n") #else extern "C" { #include "logging.h" // logThis } // closing brace for extern "C" #endif // STANDALONE_XML_PARSER Element::~Element() { for (std::map::iterator it = attributes.begin(); it != attributes.end(); ++it ) { free(it->second); } attributes.clear(); } template void Element::deleteListOfElements(std::vector list) { // https://stackoverflow.com/questions/3144604/stdvectortiterator-it-doesnt-compile typename std::vector::iterator it; for (it = list.begin(); it != list.end(); ++it) { delete *it; } } void Element::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { throw XmlParserException("Elements are not expected inside '%s'. Found instead '%s'", XmlParser::elmNames[type], childName); } void Element::printElement(int indent) { std::string indentS(indent, ' '); logThis(ERROR_INFO, "%s%s", indentS.c_str(), XmlParser::elmNames[type]); for (std::map::iterator it = attributes.begin(); it != attributes.end(); ++it ) { logThis(ERROR_INFO, "%s%s=%s", indentS.c_str(), XmlParser::attNames[it->first], it->second); } } template void Element::printListOfElements(int indent, std::vector list) { typename std::vector::iterator it; for (it = list.begin(); it != list.end(); ++it) { (*it)->printElement(indent); } } const char *Element::getAttributeValue(XmlParser::Att att) { std::map::iterator it = attributes.find(att); if(it != attributes.end()) { return it->second; } return NULL; } int Element::getAttributeInt(XmlParser::Att att, XmlParser::ValueStatus *vs) { int n = 0; const char *value = getAttributeValue(att); if (!value) { *vs = XmlParser::valueMissing; return n; } *vs = (1 == sscanf(value, "%d", &n)) ? XmlParser::valueDefined : XmlParser::valueIllegal; return n; } unsigned int Element::getAttributeUInt(XmlParser::Att att, XmlParser::ValueStatus *vs) { unsigned int u = -1; const char* value = getAttributeValue(att); if (!value) { *vs = XmlParser::valueMissing; return u; } *vs = (1 == sscanf(value, "%u", &u)) ? XmlParser::valueDefined : XmlParser::valueIllegal; return u; } double Element::getAttributeDouble(XmlParser::Att att, XmlParser::ValueStatus *vs) { double d = 0; const char* value = getAttributeValue(att); if (!value) { *vs = XmlParser::valueMissing; return d; } *vs = (1 == sscanf(value, "%lf", &d)) ? XmlParser::valueDefined : XmlParser::valueIllegal; return d; } bool Element::getAttributeBool(XmlParser::Att att, XmlParser::ValueStatus *vs) { const char* value = getAttributeValue(att); if (!value) { *vs = XmlParser::valueMissing; return false; }; *vs = XmlParser::valueDefined; if (!strcmp(value, "true")) return true; if (!strcmp(value, "false")) return false; *vs = XmlParser::valueIllegal; return false; } ListElement::~ListElement() { deleteListOfElements(list); } void ListElement::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); if (childType == XmlParser::elm_Item) { Element *item = new Element; item->type = childType; parser->parseElementAttributes(item); if (!isEmptyElement) { parser->parseEndElement(); } list.push_back(item); } else { throw XmlParserException( "Expected '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_Item], XmlParser::elmNames[XmlParser::elm_Enumeration], childName); } } void ListElement::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; printListOfElements(childIndent, list); } Unit::Unit() { baseUnit = NULL; } Unit::~Unit() { if (baseUnit) delete baseUnit; deleteListOfElements(displayUnits); } void Unit::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); if (childType == XmlParser::elm_BaseUnit) { baseUnit = new Element; baseUnit->type = childType; parser->parseElementAttributes(baseUnit); if (!isEmptyElement) { parser->parseEndElement(); } } else if (childType == XmlParser::elm_DisplayUnit) { Element *displayUnit = new Element; displayUnit->type = childType; parser->parseElementAttributes(displayUnit); displayUnits.push_back(displayUnit); if (!isEmptyElement) { parser->parseEndElement(); } } else { throw XmlParserException( "Expected '%s' | '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_BaseUnit], XmlParser::elmNames[XmlParser::elm_DisplayUnit], XmlParser::elmNames[XmlParser::elm_SourceFiles], childName); } } void Unit::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; if (baseUnit) baseUnit->printElement(childIndent); printListOfElements(childIndent, displayUnits); } SimpleType::SimpleType() { typeSpec = NULL; } SimpleType::~SimpleType() { if (typeSpec) delete typeSpec; } void SimpleType::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); switch (childType) { case XmlParser::elm_Real: case XmlParser::elm_Integer: case XmlParser::elm_Boolean: case XmlParser::elm_String: { typeSpec = new Element; typeSpec->type = childType; parser->parseElementAttributes(typeSpec); if (!isEmptyElement) { parser->parseEndElement(); } break; } case XmlParser::elm_Enumeration: { typeSpec = new ListElement; typeSpec->type = childType; parser->parseElementAttributes(typeSpec); if (!isEmptyElement) { parser->parseChildElements(typeSpec); } break; } default: { throw XmlParserException( "Expected '%s' | '%s' | '%s' | '%s' | '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_Real], XmlParser::elmNames[XmlParser::elm_Integer], XmlParser::elmNames[XmlParser::elm_Boolean], XmlParser::elmNames[XmlParser::elm_String], XmlParser::elmNames[XmlParser::elm_Enumeration], XmlParser::elmNames[type], childName); } } } void SimpleType::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; if (typeSpec) typeSpec->printElement(childIndent); } Component::~Component() { deleteListOfElements(files); } void Component::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); if (childType == XmlParser::elm_SourceFiles) { if (!isEmptyElement) { parser->parseChildElements(this); } } else if (childType == XmlParser::elm_File) { Element *sourceFile = new Element; sourceFile->type = childType; parser->parseElementAttributes(sourceFile); if (!isEmptyElement) { parser->parseEndElement(); } files.push_back(sourceFile); } else { throw XmlParserException("Expected '%s' | '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_File], XmlParser::elmNames[XmlParser::elm_SourceFiles], XmlParser::elmNames[type], childName); } } void Component::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; printListOfElements(childIndent, files); } ScalarVariable::ScalarVariable() { typeSpec = NULL; } ScalarVariable::~ScalarVariable() { delete typeSpec; deleteListOfElements(annotations); } void ScalarVariable::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); switch (childType) { case XmlParser::elm_Real: case XmlParser::elm_Integer: case XmlParser::elm_Boolean: case XmlParser::elm_String: case XmlParser::elm_Enumeration: { typeSpec = new Element; typeSpec->type = childType; parser->parseElementAttributes(typeSpec); if (!isEmptyElement) { parser->parseEndElement(); } break; } case XmlParser::elm_Annotations: { // no attributes expected; this class handles also the Tool if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_Tool: { Element *tool = new Element; tool->type = childType; parser->parseElementAttributes(tool); if (!isEmptyElement) { parser->parseSkipChildElement(); } annotations.push_back(tool); break; } default: { throw XmlParserException( "Expected '%s' | '%s' | '%s' | '%s' | '%s' | '%s' | '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_Real], XmlParser::elmNames[XmlParser::elm_Integer], XmlParser::elmNames[XmlParser::elm_Boolean], XmlParser::elmNames[XmlParser::elm_String], XmlParser::elmNames[XmlParser::elm_Enumeration], XmlParser::elmNames[XmlParser::elm_Annotations], XmlParser::elmNames[XmlParser::elm_Tool], XmlParser::elmNames[type], childName); } } } fmiValueReference ScalarVariable::getValueReference() { XmlParser::ValueStatus vs; fmiValueReference ref = getAttributeUInt(XmlParser::att_valueReference, &vs); assert(vs == XmlParser::valueDefined); // this is a required attribute return ref; } XmlParser::Enu ScalarVariable::getVariability() { const char *value = getAttributeValue(XmlParser::att_variability); if (!value) { return XmlParser::enu_continuous; // default } try { return XmlParser::checkEnumValue(value); } catch (XmlParserException& ) { return XmlParser::enu_BAD_DEFINED; } } XmlParser::Enu ScalarVariable::getCausality() { const char *value = getAttributeValue(XmlParser::att_causality); if (!value) { return XmlParser::enu_local; // default } try { return XmlParser::checkEnumValue(value); } catch (XmlParserException& ) { return XmlParser::enu_BAD_DEFINED; } } void ScalarVariable::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; typeSpec->printElement(childIndent); printListOfElements(childIndent, annotations); } ModelStructure::~ModelStructure() { deleteListOfElements(outputs); deleteListOfElements(derivatives); deleteListOfElements(discreteStates); deleteListOfElements(initialUnknowns); } void ModelStructure::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); switch (childType) { case XmlParser::elm_Outputs: case XmlParser::elm_Derivatives: case XmlParser::elm_DiscreteStates: case XmlParser::elm_InitialUnknowns: { // no attributes expected; this class handles also the Unknown if (!isEmptyElement) { unknownParentType = childType; parser->parseChildElements(this); } break; } case XmlParser::elm_Unknown: { Element *unknown = new Element; unknown->type = childType; parser->parseElementAttributes(unknown); if (!isEmptyElement) { parser->parseEndElement(); } switch (unknownParentType) { case XmlParser::elm_Outputs: outputs.push_back(unknown); break; case XmlParser::elm_Derivatives: derivatives.push_back(unknown); break; case XmlParser::elm_DiscreteStates: discreteStates.push_back(unknown); break; case XmlParser::elm_InitialUnknowns: initialUnknowns.push_back(unknown); break; default: { throw XmlParserException( "Element '%s' must be inside of '%s' | '%s' | '%s' | '%s'.", XmlParser::elmNames[XmlParser::elm_Unknown], XmlParser::elmNames[XmlParser::elm_Outputs], XmlParser::elmNames[XmlParser::elm_Derivatives], XmlParser::elmNames[XmlParser::elm_DiscreteStates], XmlParser::elmNames[XmlParser::elm_InitialUnknowns]); } } break; } default: { throw XmlParserException("Expected '%s' | '%s' | '%s' | '%s' | '%s' element inside '%s'. Found instead: '%s'.", XmlParser::elmNames[XmlParser::elm_Outputs], XmlParser::elmNames[XmlParser::elm_Derivatives], XmlParser::elmNames[XmlParser::elm_DiscreteStates], XmlParser::elmNames[XmlParser::elm_InitialUnknowns], XmlParser::elmNames[XmlParser::elm_Unknown], XmlParser::elmNames[type], childName); } } } void ModelStructure::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; printListOfElements(childIndent, outputs); printListOfElements(childIndent, derivatives); printListOfElements(childIndent, discreteStates); printListOfElements(childIndent, initialUnknowns); } ModelDescription::ModelDescription() { modelExchange = NULL; coSimulation = NULL; defaultExperiment = NULL; modelStructure = NULL; } ModelDescription::~ModelDescription() { deleteListOfElements(unitDefinitions); deleteListOfElements(typeDefinitions); if (coSimulation) delete coSimulation; if (modelExchange) delete modelExchange; deleteListOfElements(logCategories); if (defaultExperiment) delete defaultExperiment; deleteListOfElements(vendorAnnotations); deleteListOfElements(modelVariables); if (modelStructure) delete modelStructure; } void ModelDescription::handleElement(XmlParser *parser, const char *childName, int isEmptyElement) { XmlParser::Elm childType = parser->checkElement(childName); switch (childType) { case XmlParser::elm_CoSimulation: { coSimulation = new Component; coSimulation->type = childType; parser->parseElementAttributes(coSimulation); if (!isEmptyElement) { parser->parseChildElements(coSimulation); } break; } case XmlParser::elm_ModelExchange: { modelExchange = new Component; modelExchange->type = childType; parser->parseElementAttributes(modelExchange); if (!isEmptyElement) { parser->parseChildElements(modelExchange); } break; } case XmlParser::elm_UnitDefinitions: { // no attributes expected; this class handles the Category if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_Unit: { Unit *unit = new Unit; unit->type = childType; parser->parseElementAttributes(unit); if (!isEmptyElement) { parser->parseChildElements(unit); } unitDefinitions.push_back(unit); break; } case XmlParser::elm_TypeDefinitions: { // no attributes expected; this class handles also the SimpleType if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_SimpleType: { SimpleType *type = new SimpleType; type->type = childType; parser->parseElementAttributes(type); if (!isEmptyElement) { parser->parseChildElements(type); } typeDefinitions.push_back(type); break; } case XmlParser::elm_DefaultExperiment: { defaultExperiment = new Element; defaultExperiment->type = childType; parser->parseElementAttributes(defaultExperiment); if (!isEmptyElement) { parser->parseEndElement(); } break; } case XmlParser::elm_LogCategories: { // no attributes expected; this class handles also the Category if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_Category: { Element *category = new Element; category->type = childType; parser->parseElementAttributes(category); if (!isEmptyElement) { parser->parseEndElement(); } logCategories.push_back(category); break; } case XmlParser::elm_VendorAnnotations: { // no attributes expected; this class handles also the Tool if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_Tool: { Element *tool = new Element; tool->type = childType; parser->parseElementAttributes(tool); if (!isEmptyElement) { parser->parseSkipChildElement(); } vendorAnnotations.push_back(tool); break; } case XmlParser::elm_ModelVariables: { // no attributes expected; this class handles also the ScalarVariable if (!isEmptyElement) parser->parseChildElements(this); break; } case XmlParser::elm_ScalarVariable: { ScalarVariable *variable = new ScalarVariable; variable->type = childType; parser->parseElementAttributes(variable); if (!isEmptyElement) { parser->parseChildElements(variable); } modelVariables.push_back(variable); break; } case XmlParser::elm_ModelStructure: { modelStructure = new ModelStructure; modelStructure->type = childType; parser->parseElementAttributes(modelStructure); if (!isEmptyElement) { parser->parseChildElements(modelStructure); } break; } default: throw XmlParserException("Element '%s' is not expected inside of '%s' | '%s' | '%s' | '%s'.", childName, XmlParser::elmNames[type]); } } void ModelDescription::printElement(int indent) { Element::printElement(indent); int childIndent = indent + 1; if (coSimulation) coSimulation->printElement(childIndent); if (modelExchange) modelExchange->printElement(childIndent); for (std::vector::iterator it = unitDefinitions.begin(); it != unitDefinitions.end(); ++it) { (*it)->printElement(childIndent); } for (std::vector::iterator it = typeDefinitions.begin(); it != typeDefinitions.end(); ++it) { (*it)->printElement(childIndent); } for (std::vector::iterator it = logCategories.begin(); it != logCategories.end(); ++it) { (*it)->printElement(childIndent); } if (defaultExperiment) defaultExperiment->printElement(childIndent); for (std::vector::iterator it = vendorAnnotations.begin(); it != vendorAnnotations.end(); ++it) { (*it)->printElement(childIndent); } for (std::vector::iterator it = modelVariables.begin(); it != modelVariables.end(); ++it) { (*it)->printElement(childIndent); } if (modelStructure) modelStructure->printElement(childIndent); } SimpleType *ModelDescription::getSimpleType(const char *name) { for (std::vector::iterator it = typeDefinitions.begin(); it != typeDefinitions.end(); ++it) { const char *typeName = (*it)->getAttributeValue(XmlParser::att_name); if (typeName && 0 == strcmp(typeName, name)) { return (*it); } } return NULL; } ScalarVariable *ModelDescription::getVariable(const char *name) { if (!name) return NULL; for (std::vector::iterator it = modelVariables.begin(); it != modelVariables.end(); ++it) { const char *varName = (*it)->getAttributeValue(XmlParser::att_name); if (varName && 0 == strcmp(name, varName)) { return (*it); } } return NULL; } const char *ModelDescription::getDescriptionForVariable(ScalarVariable *sv) { const char *desc = sv->getAttributeValue(XmlParser::att_description); // found description if (desc) return desc; // look for description of declared type: const char *typeName = sv->typeSpec->getAttributeValue(XmlParser::att_declaredType); if (typeName) { SimpleType *type = getSimpleType(typeName); if (type) return type->getAttributeValue(XmlParser::att_description); } return NULL; } const char *ModelDescription::getAttributeFromTypeOrDeclaredType(ScalarVariable *sv, XmlParser::Att a) { const char *stringValue = sv->typeSpec->getAttributeValue(a); if (stringValue) return stringValue; // look for stringValue inside declared type: const char *typeName = sv->typeSpec->getAttributeValue(XmlParser::att_declaredType); if (typeName) { SimpleType *type = getSimpleType(typeName); if (type) return type->getAttributeValue(a); } return NULL; }