File indexing completed on 2025-01-05 05:23:43

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010, 2011, 2012 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "osdparser.hpp"
0010 
0011 #include "datainformationfactory.hpp"
0012 #include "../datatypes/array/arraydatainformation.hpp"
0013 #include "../datatypes/uniondatainformation.hpp"
0014 #include "../datatypes/structuredatainformation.hpp"
0015 #include "../datatypes/strings/stringdatainformation.hpp"
0016 #include "../datatypes/strings/stringdata.hpp"
0017 #include "../datatypes/primitivefactory.hpp"
0018 #include "../datatypes/topleveldatainformation.hpp"
0019 #include "../datatypes/dummydatainformation.hpp"
0020 #include "../structuredefinitionfile.hpp"
0021 #include "../script/scriptlogger.hpp"
0022 #include "../script/scriptengineinitializer.hpp"
0023 #include <structureslogging.hpp>
0024 // Qt
0025 #include <QFile>
0026 #include <QFileInfo>
0027 #include <QDomDocument>
0028 #include <QScriptEngine>
0029 // Std
0030 #include <memory>
0031 
0032 using namespace ParserStrings;
0033 
0034 OsdParser::OsdParser(const QString& pluginName, const QString& absolutePath)
0035     : AbstractStructureParser(pluginName, absolutePath)
0036 {
0037 }
0038 
0039 OsdParser::OsdParser(const QString& xml)
0040     : AbstractStructureParser(QString(), QString())
0041     , mXmlString(xml)
0042 {
0043 }
0044 
0045 OsdParser::~OsdParser() = default;
0046 
0047 QDomDocument OsdParser::openDoc(ScriptLogger* logger) const
0048 {
0049     return mXmlString.isEmpty() ? openDocFromFile(logger) : openDocFromString(logger);
0050 }
0051 
0052 QDomDocument OsdParser::openDocFromString(ScriptLogger* logger) const
0053 {
0054     Q_CHECK_PTR(logger);
0055     Q_ASSERT(!mXmlString.isEmpty());
0056     int errorLine, errorColumn;
0057     QString errorMsg;
0058     QDomDocument doc;
0059     if (!doc.setContent(mXmlString, false, &errorMsg, &errorLine, &errorColumn)) {
0060         const QString errorOutput =
0061             QStringLiteral("error reading XML: %1\n error line=%2\nerror column=%3")
0062             .arg(errorMsg, QString::number(errorLine), QString::number(errorColumn));
0063         logger->error() << errorOutput;
0064         logger->info() << "XML was:" << mXmlString;
0065 
0066         return {};
0067     }
0068     return doc;
0069 }
0070 
0071 QDomDocument OsdParser::openDocFromFile(ScriptLogger* logger) const
0072 {
0073     Q_CHECK_PTR(logger);
0074     QFileInfo fileInfo(mAbsolutePath);
0075     if (!fileInfo.exists()) {
0076         logger->error() << "File" << mAbsolutePath << "does not exist!";
0077         return {};
0078     }
0079     QFile file(fileInfo.absoluteFilePath());
0080     if (!file.open(QIODevice::ReadOnly)) {
0081         const QString errorOutput = QLatin1String("Could not open file ") + mAbsolutePath;
0082         logger->error() << errorOutput;
0083         return {};
0084     }
0085     int errorLine, errorColumn;
0086     QString errorMsg;
0087     QDomDocument doc;
0088     if (!doc.setContent(&file, false, &errorMsg, &errorLine, &errorColumn)) {
0089         const QString errorOutput =
0090             QStringLiteral("error reading XML: %1\n error line=%2\nerror column=%3")
0091             .arg(errorMsg, QString::number(errorLine), QString::number(errorColumn));
0092         logger->error() << errorOutput;
0093         logger->info() << "File was:" << mAbsolutePath;
0094     }
0095     file.close();
0096     return doc;
0097 }
0098 
0099 QStringList OsdParser::parseStructureNames() const
0100 {
0101     QStringList ret;
0102     std::unique_ptr<ScriptLogger> rootLogger(new ScriptLogger); // only needed if we get an error right now
0103     rootLogger->setLogToStdOut(true); // we cannot get our messages into the script console, so do this instead
0104     QDomDocument document = openDoc(rootLogger.get());
0105     if (document.isNull()) {
0106         return {};
0107     }
0108     QDomElement rootElem = document.firstChildElement(QStringLiteral("data"));
0109     if (rootElem.isNull()) {
0110         return {};
0111     }
0112     for (QDomElement childElement = rootElem.firstChildElement();
0113          !childElement.isNull(); childElement = childElement.nextSiblingElement()) {
0114         QString tag = childElement.tagName();
0115         if (tag == TYPE_STRUCT() || tag == TYPE_ARRAY() || tag == TYPE_BITFIELD() || tag == TYPE_PRIMITIVE()
0116             || tag == TYPE_UNION() || tag == TYPE_ENUM() || tag == TYPE_FLAGS() || tag == TYPE_STRING()) {
0117             // TODO allow e.g. <uint8 name="asfd">
0118             ret.append(readProperty(childElement, PROPERTY_NAME(), i18n("<invalid name>")));
0119         } else if (tag == TYPE_ENUMDEF()) {
0120             // skip enum defs
0121         } else {
0122             rootLogger->error(QString()).nospace() << "Unknown tag name in plugin " << mPluginName << " :"
0123                                                    << tag;
0124         }
0125     }
0126 
0127     return ret;
0128 }
0129 
0130 QVector<TopLevelDataInformation*> OsdParser::parseStructures() const
0131 {
0132     QFileInfo fileInfo(mAbsolutePath);
0133 
0134     QVector<TopLevelDataInformation*> structures;
0135     std::unique_ptr<ScriptLogger> rootLogger(new ScriptLogger()); // only needed in we get an error right now
0136     QDomDocument document = openDoc(rootLogger.get());
0137     if (document.isNull()) {
0138         structures.append(new TopLevelDataInformation(
0139                               new DummyDataInformation(nullptr, fileInfo.fileName()), rootLogger.release(), nullptr, fileInfo));
0140         return structures;
0141     }
0142 
0143     QDomElement rootElem = document.firstChildElement(QStringLiteral("data"));
0144     if (rootElem.isNull()) {
0145         rootLogger->error() << "Missing top level <data> element!";
0146         structures.append(new TopLevelDataInformation(
0147                               new DummyDataInformation(nullptr, fileInfo.fileName()), rootLogger.release(), nullptr, fileInfo));
0148         return structures;
0149     }
0150     int count = 1;
0151     for (QDomElement elem = rootElem.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement()) {
0152         if (elem.tagName() == TYPE_ENUMDEF()) {
0153             continue; // skip enum defs
0154 
0155         }
0156         QScriptEngine* eng = ScriptEngineInitializer::newEngine(); // we need this for dynamic arrays
0157         auto* logger = new ScriptLogger();
0158         QVector<EnumDefinition::Ptr> enums = parseEnums(rootElem, logger);
0159         OsdParserInfo info(QString(), logger, nullptr, eng, enums);
0160 
0161         DataInformation* data = parseElement(elem, info);
0162 
0163         if (!data) {
0164             QString name = readProperty(elem, PROPERTY_NAME());
0165             if (name.isEmpty()) {
0166                 name = fileInfo.absoluteFilePath() + QLatin1String("_element") + QString::number(count);
0167             }
0168             qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "Failed to parse element" << elem.tagName() << name;
0169             qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "Parsing messages were:" << logger->messages();
0170             data = new DummyDataInformation(nullptr, name);
0171         }
0172         auto* topData = new TopLevelDataInformation(data, logger, eng, fileInfo);
0173         QString lockOffsetStr = readProperty(elem, PROPERTY_DEFAULT_LOCK_OFFSET());
0174         if (!lockOffsetStr.isEmpty()) {
0175             ParsedNumber<quint64> offset = ParserUtils::uint64FromString(lockOffsetStr);
0176             if (!offset.isValid) {
0177                 data->logError() << "Default lock offset is not a valid number:" << offset.string;
0178             } else {
0179                 data->logInfo() << "Default lock offset is " << offset.string;
0180                 topData->setDefaultLockOffset(offset.value);
0181             }
0182         }
0183         structures.append(topData);
0184         count++;
0185     }
0186 
0187     return structures;
0188 }
0189 
0190 // TODO make type depend on the user not the definition
0191 QVector<EnumDefinition::Ptr> OsdParser::parseEnums(const QDomElement& rootElem, ScriptLogger* logger)
0192 {
0193     QVector<EnumDefinition::Ptr> ret;
0194     for (QDomElement elem = rootElem.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement()) {
0195         if (elem.tagName() != TYPE_ENUMDEF()) {
0196             continue;
0197         }
0198 
0199         QMap<AllPrimitiveTypes, QString> defs;
0200         const QString enumName = readProperty(elem, PROPERTY_NAME(), i18n("<invalid name>"));
0201         const QString typeStr = readProperty(elem, PROPERTY_TYPE());
0202         if (typeStr.isEmpty()) {
0203             logger->error(enumName) << "Skipping enum definition, since no type attribute was found.";
0204             continue;
0205         }
0206         LoggerWithContext lwc(logger, QLatin1String("enum values (") + enumName + QLatin1Char(')'));
0207         PrimitiveDataType type = PrimitiveFactory::typeStringToType(typeStr, lwc);
0208         // handle all entries
0209         for (QDomElement child = elem.firstChildElement(); !child.isNull(); child =
0210                  child.nextSiblingElement()) {
0211             if (child.tagName() != QLatin1String("entry")) {
0212                 continue;
0213             }
0214 
0215             QString name = readProperty(child, PROPERTY_NAME());
0216             if (name.isEmpty()) {
0217                 lwc.warn() << "Entry is missing name, skipping it!";
0218                 continue;
0219             }
0220             QString value = readProperty(child, PROPERTY_VALUE());
0221             QPair<AllPrimitiveTypes, QString> converted =
0222                 EnumDefinition::convertToEnumEntry(name, value, lwc, type);
0223             if (converted == QPair<AllPrimitiveTypes, QString>()) {
0224                 continue;
0225             }
0226             defs.insert(converted.first, converted.second);
0227         }
0228 
0229         // now add this enum to the list of enums
0230         if (defs.isEmpty()) {
0231             lwc.error() << "Enum definition contains no valid elements!";
0232         } else {
0233             EnumDefinition::Ptr enumDef = EnumDefinition::Ptr(new EnumDefinition(defs, enumName, type));
0234             ret.append(enumDef);
0235         }
0236     }
0237 
0238     return ret;
0239 }
0240 
0241 // Datatypes
0242 
0243 ArrayDataInformation* OsdParser::arrayFromXML(const QDomElement& xmlElem, const OsdParserInfo& info)
0244 {
0245     ArrayParsedData apd(info);
0246     QString lengthStr = readProperty(xmlElem, PROPERTY_LENGTH());
0247     if (lengthStr.isEmpty()) {
0248         info.error() << "No array length specified!";
0249         return nullptr;
0250     }
0251     // must wrap in parentheses, cannot just evaluate. See https://bugreports.qt-project.org/browse/QTBUG-5757
0252     const QScriptValue lengthFunc = ParserUtils::functionSafeEval(info.engine, lengthStr);
0253     if (lengthFunc.isValid()) {
0254         apd.length = lengthFunc;
0255     } else {
0256         apd.length = QScriptValue(lengthStr);
0257     }
0258     // first check whether there is a <type> element and use the inner element
0259     // if that doesn't exist use the first child element as the type, but only if there is only one child
0260     apd.arrayType = parseType(xmlElem, info, NAME_ARRAY_TYPE());
0261     if (!apd.arrayType) {
0262         // was not specified as <type> element or type="attribute", use first child
0263         DummyDataInformation dummy(info.parent, info.name); // dummy so that we have a proper chain
0264         OsdChildrenParser typeParser(info, xmlElem.firstChildElement());
0265         typeParser.setParent(&dummy);
0266         if (typeParser.hasNext()) {
0267             apd.arrayType = typeParser.next();
0268             if (typeParser.hasNext()) {
0269                 info.error() << "More than one possible type for array!";
0270                 delete apd.arrayType;
0271                 apd.arrayType = nullptr;
0272                 return nullptr;
0273             }
0274         }
0275     }
0276     return DataInformationFactory::newArray(apd);
0277 }
0278 
0279 DataInformation* OsdParser::parseChildElement(const QDomElement& xmlElem, const OsdParserInfo& info, const QString& name)
0280 {
0281     OsdParserInfo newInfo(info);
0282     // instantiate a dummy so that a property chain up to the root element exists
0283     DummyDataInformation dummy(info.parent, info.name);
0284     newInfo.parent = &dummy;
0285     newInfo.name = name;
0286     return parseElement(xmlElem, newInfo);
0287 }
0288 
0289 DataInformation* OsdParser::parseType(const QDomElement& xmlElem, const OsdParserInfo& info, const QString& name)
0290 {
0291     const QString typeAttribute = xmlElem.attribute(PROPERTY_TYPE());
0292     if (!typeAttribute.isEmpty()) {
0293         // type was specified as a primitive string
0294         LoggerWithContext lwc(info.logger, info.context() + name);
0295         DataInformation* ret = PrimitiveFactory::newInstance(name, typeAttribute, lwc);
0296         if (!ret) {
0297             info.error() << typeAttribute << "is not a valid type identifier";
0298         }
0299         return ret;
0300     }
0301     // we have to parse the first child element of the <type> element
0302     const QDomElement toParse = xmlElem.firstChildElement(PROPERTY_TYPE()).firstChildElement();
0303     if (toParse.isNull()) {
0304         // don't log an error here, it may be okay (i.e. in arrays <type> can be omitted)
0305         return nullptr;
0306     }
0307     if (!toParse.nextSiblingElement().isNull()) {
0308         info.warn() << "<type> element has more than one child!";
0309     }
0310     // TODO have this newInfo code only in one location
0311     DataInformation* ret = parseChildElement(toParse, info, name);
0312     if (!ret) {
0313         info.error() << "Failed to parse element defined in <type>";
0314     }
0315     return ret;
0316 }
0317 
0318 PointerDataInformation* OsdParser::pointerFromXML(const QDomElement& xmlElem, const OsdParserInfo& info)
0319 {
0320     PointerParsedData ppd(info);
0321 
0322     ppd.valueType = parseType(xmlElem, info, NAME_POINTER_VALUE_TYPE());
0323 
0324     // first check whether there is a <target> element and use the inner element
0325     // if that doesn't exist use the first child element as the type, but only if there is only one child
0326     QDomElement childElement = xmlElem.firstChildElement(PROPERTY_TARGET()).firstChildElement();
0327     if (childElement.isNull()) {
0328         childElement = xmlElem.firstChildElement();
0329         if (childElement.isNull()) {
0330             info.error() << "Pointer target is missing! Please add a <target> child element.";
0331             return nullptr;
0332         }
0333         if (childElement != xmlElem.lastChildElement()) {
0334             // there is more than one child element
0335             info.error() << "There is more than one child element, cannot determine which one "
0336                 "is the pointer target. Wrap the correct one in a <target> element.";
0337             return nullptr;
0338         }
0339     }
0340     ppd.pointerTarget = parseChildElement(childElement, info, NAME_POINTER_TARGET());
0341     return DataInformationFactory::newPointer(ppd);
0342 }
0343 
0344 PrimitiveDataInformation* OsdParser::primitiveFromXML(const QDomElement& xmlElem, const OsdParserInfo& info)
0345 {
0346     PrimitiveParsedData ppd(info);
0347     ppd.type = readProperty(xmlElem, PROPERTY_TYPE());
0348     return DataInformationFactory::newPrimitive(ppd);
0349 }
0350 
0351 AbstractBitfieldDataInformation* OsdParser::bitfieldFromXML(const QDomElement& xmlElem,
0352                                                             const OsdParserInfo& info)
0353 {
0354     BitfieldParsedData bpd(info);
0355     bpd.type = readProperty(xmlElem, PROPERTY_TYPE());
0356     QString width = readProperty(xmlElem, PROPERTY_WIDTH());
0357     bpd.width = ParserUtils::intFromString(width);
0358     return DataInformationFactory::newBitfield(bpd);
0359 }
0360 
0361 UnionDataInformation* OsdParser::unionFromXML(const QDomElement& xmlElem, const OsdParserInfo& info)
0362 {
0363     StructOrUnionParsedData supd(info);
0364     supd.children.reset(new OsdChildrenParser(info, xmlElem.firstChildElement()));
0365     return DataInformationFactory::newUnion(supd);
0366 }
0367 
0368 StructureDataInformation* OsdParser::structFromXML(const QDomElement& xmlElem, const OsdParserInfo& info)
0369 {
0370     StructOrUnionParsedData supd(info);
0371     supd.children.reset(new OsdChildrenParser(info, xmlElem.firstChildElement()));
0372     return DataInformationFactory::newStruct(supd);
0373 }
0374 
0375 EnumDataInformation* OsdParser::enumFromXML(const QDomElement& xmlElem, bool isFlags,
0376                                             const OsdParserInfo& info)
0377 {
0378     EnumParsedData epd(info);
0379     epd.type = readProperty(xmlElem, PROPERTY_TYPE());
0380     epd.enumName = readProperty(xmlElem, PROPERTY_ENUM_NAME());
0381     if (epd.enumName.isEmpty()) {
0382         epd.enumName = readProperty(xmlElem, TYPE_ENUM()); // used again here as property
0383     }
0384     epd.enumDef = findEnum(epd.enumName, info);
0385     if (!epd.enumDef) {
0386         info.error().nospace() << "Enum definition '" << epd.enumName << "' does not exist!";
0387         return nullptr;
0388     }
0389 
0390     if (isFlags) {
0391         return DataInformationFactory::newFlags(epd);
0392     }
0393 
0394     return DataInformationFactory::newEnum(epd);
0395 }
0396 
0397 StringDataInformation* OsdParser::stringFromXML(const QDomElement& xmlElem,
0398                                                 const OsdParserInfo& info)
0399 {
0400     StringParsedData spd(info);
0401     spd.encoding = readProperty(xmlElem, PROPERTY_ENCODING());
0402     spd.termination = ParserUtils::uintFromString(readProperty(xmlElem, PROPERTY_TERMINATED_BY()));
0403     spd.maxByteCount = ParserUtils::uintFromString(readProperty(xmlElem, PROPERTY_MAX_BYTE_COUNT()));
0404     spd.maxCharCount = ParserUtils::uintFromString(readProperty(xmlElem, PROPERTY_MAX_CHAR_COUNT()));
0405     return DataInformationFactory::newString(spd);
0406 }
0407 
0408 DataInformation* OsdParser::parseElement(const QDomElement& elem, const OsdParserInfo& oldInfo)
0409 {
0410     Q_ASSERT(!elem.isNull());
0411     DataInformation* data = nullptr;
0412     const QString tag = elem.tagName();
0413     OsdParserInfo info(oldInfo);
0414     info.name = readProperty(elem, PROPERTY_NAME(), QStringLiteral("<anonymous>"));
0415     if (tag == TYPE_STRUCT()) {
0416         data = structFromXML(elem, info);
0417     } else if (tag == TYPE_ARRAY()) {
0418         data = arrayFromXML(elem, info);
0419     } else if (tag == TYPE_BITFIELD()) {
0420         data = bitfieldFromXML(elem, info);
0421     } else if (tag == TYPE_PRIMITIVE()) {
0422         data = primitiveFromXML(elem, info);
0423     } else if (tag == TYPE_UNION()) {
0424         data = unionFromXML(elem, info);
0425     } else if (tag == TYPE_ENUM()) {
0426         data = enumFromXML(elem, false, info);
0427     } else if (tag == TYPE_FLAGS()) {
0428         data = enumFromXML(elem, true, info);
0429     } else if (tag == TYPE_STRING()) {
0430         data = stringFromXML(elem, info);
0431     } else if (tag == TYPE_POINTER()) {
0432         data = pointerFromXML(elem, info);
0433     } else if (tag == TYPE_TAGGED_UNION()) {
0434         data = taggedUnionFromXML(elem, info);
0435     } else {
0436         LoggerWithContext lwc(info.logger, info.context());
0437         // use the type tag as a primitive type
0438         data = PrimitiveFactory::newInstance(info.name, tag, lwc);
0439         if (!data) {
0440             info.error() << "Cannot parse unknown tag: " << tag;
0441         }
0442     }
0443 
0444     if (data) {
0445         CommonParsedData cpd(info);
0446         QString byteOrderStr = readProperty(elem, PROPERTY_BYTEORDER());
0447         if (!byteOrderStr.isEmpty()) {
0448             cpd.endianess = ParserUtils::byteOrderFromString(byteOrderStr,
0449                                                              LoggerWithContext(info.logger, info.context()));
0450         }
0451         cpd.updateFunc = ParserUtils::functionSafeEval(info.engine, readProperty(elem, PROPERTY_UPDATE_FUNC()));
0452         cpd.validationFunc = ParserUtils::functionSafeEval(info.engine, readProperty(elem, PROPERTY_VALIDATION_FUNC()));
0453         cpd.toStringFunc = ParserUtils::functionSafeEval(info.engine, readProperty(elem, PROPERTY_TO_STRING_FUNC()));
0454         cpd.customTypeName = readProperty(elem, PROPERTY_CUSTOM_TYPE_NAME());
0455         if (!DataInformationFactory::commonInitialization(data, cpd)) {
0456             delete data; // error message has already been logged
0457             return nullptr;
0458         }
0459     }
0460     return data;
0461 }
0462 
0463 EnumDefinition::Ptr OsdParser::findEnum(const QString& defName, const OsdParserInfo& info)
0464 {
0465     for (const auto& def : info.enums) {
0466         if (def->name() == defName) {
0467             return def;
0468         }
0469     }
0470 
0471     info.error() << "Could not find enum definition with name" << defName;
0472     return EnumDefinition::Ptr(nullptr);
0473 }
0474 
0475 QString OsdParser::readProperty(const QDomElement& elem, const QString& property, const QString& defaultVal)
0476 {
0477     const QString attrib = elem.attribute(property);
0478     if (!attrib.isEmpty()) {
0479         return attrib;
0480     }
0481     // check for element now
0482     const QDomElement childElem = elem.firstChildElement(property);
0483     if (!elem.isNull()) {
0484         return elem.text();
0485     }
0486 
0487     return defaultVal;
0488 }
0489 
0490 TaggedUnionDataInformation* OsdParser::taggedUnionFromXML(const QDomElement& xmlElem,
0491                                                           const OsdParserInfo& info)
0492 {
0493     TaggedUnionParsedData tpd(info);
0494     // can be null
0495     QDomElement defaultChildren = xmlElem.firstChildElement(PROPERTY_DEFAULT_CHILDREN()).firstChildElement();
0496     if (defaultChildren.isNull()) {
0497         info.info() << "No default fields specified, defaulting to none.";
0498     }
0499     tpd.defaultFields.reset(new OsdChildrenParser(info, defaultChildren));
0500     tpd.children.reset(new OsdChildrenParser(info, xmlElem.firstChildElement()));
0501     // now handle alternatives
0502     QDomElement alternatives = xmlElem.firstChildElement(PROPERTY_ALTERNATIVES());
0503     if (alternatives.isNull()) {
0504         info.error() << "Missing <alternatives> element, tagged union cannot exist without at least one alternative";
0505         return nullptr;
0506     }
0507     for (QDomElement elem = alternatives.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement()) {
0508         TaggedUnionParsedData::Alternatives alt;
0509         alt.name = readProperty(elem, PROPERTY_STRUCT_NAME());
0510         QString selectIfStr = readProperty(elem, PROPERTY_SELECT_IF());
0511         QScriptValue selectIf = ParserUtils::functionSafeEval(info.engine, selectIfStr);
0512         if (!selectIf.isValid()) {
0513             selectIf = selectIfStr;
0514         }
0515         alt.selectIf = selectIf;
0516         if (elem.tagName() == TYPE_GROUP()) {
0517             alt.fields = QSharedPointer<ChildrenParser>(new OsdChildrenParser(info, elem.firstChildElement()));
0518         } else {
0519             alt.fields = QSharedPointer<ChildrenParser>(new SingleElementOsdChildrenParser(info, elem));
0520         }
0521         tpd.alternatives.append(alt);
0522     }
0523 
0524     return DataInformationFactory::newTaggedUnion(tpd);
0525 }
0526 
0527 OsdChildrenParser::OsdChildrenParser(const OsdParserInfo& info, const QDomElement& firstChild)
0528     : mInfo(info)
0529     , mElem(firstChild)
0530 {
0531 }
0532 
0533 OsdChildrenParser::~OsdChildrenParser() = default;
0534 
0535 DataInformation* OsdChildrenParser::next()
0536 {
0537     Q_ASSERT(!mElem.isNull());
0538     // skip all known properties
0539     const QStringList allProperties = ALL_PROPERTIES();
0540     while (allProperties.contains(mElem.tagName())) {
0541         mElem = mElem.nextSiblingElement();
0542     }
0543     if (mElem.isNull()) {
0544         mInfo.warn() << "Reached end of fields, but next() was requested!";
0545         return nullptr;
0546     }
0547     DataInformation* ret = OsdParser::parseElement(mElem, mInfo);
0548     mElem = mElem.nextSiblingElement();
0549     return ret;
0550 }
0551 
0552 bool OsdChildrenParser::hasNext()
0553 {
0554     if (mElem.isNull()) {
0555         return false;
0556     }
0557     const QStringList allProperties = ALL_PROPERTIES();
0558     while (allProperties.contains(mElem.tagName())) {
0559         mElem = mElem.nextSiblingElement(); // skip known properties
0560     }
0561     return !mElem.isNull();
0562 }
0563 
0564 void OsdChildrenParser::setParent(DataInformation* newParent)
0565 {
0566     mInfo.parent = newParent;
0567 }
0568 
0569 SingleElementOsdChildrenParser::SingleElementOsdChildrenParser(const OsdParserInfo& info, const QDomElement& element)
0570     : OsdChildrenParser(info, element)
0571     , mParsed(false)
0572 {
0573     if (mElem.isNull()) {
0574         info.warn() << "Null Element passed to child parser!";
0575     }
0576 }
0577 
0578 SingleElementOsdChildrenParser::~SingleElementOsdChildrenParser() = default;
0579 
0580 DataInformation* SingleElementOsdChildrenParser::next()
0581 {
0582     Q_ASSERT(!mParsed);
0583     mParsed = true;
0584     return OsdParser::parseElement(mElem, mInfo);
0585 }
0586 
0587 bool SingleElementOsdChildrenParser::hasNext()
0588 {
0589     return !mParsed && !mElem.isNull();
0590 }