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 }