#include "settingsconfigimpl_p.h" #include #include #include namespace { using SourceType = SettingsConfigBase::variant; template void convertElement(QXmlStreamReader &reader, TVariantTarget &, SourceType &&) { throw SettingsConfigBase::XmlException{reader, QStringLiteral("Unexpected root element in included file")}; } template void convertElement(QXmlStreamReader &reader, TVariantTarget &target, SourceType &&source) { if(nonstd::holds_alternative(source)) target = nonstd::get(std::move(source)); else convertElement(reader, target, std::move(source)); } template struct VariantInfo {}; template struct VariantInfo>> { using TargetType = SettingsConfigBase::variant; static void convert(QXmlStreamReader &reader, TargetType &target, SourceType &&source) { convertElement(reader, target, std::move(source)); } }; template struct ListInfo; template struct ListInfo> { using TElement = T; }; template struct visitor { using IterType = typename std::decay::type; using ListType = typename std::decay::type; using VariantType = typename ListInfo::TElement; SettingsConfigImpl &_reader; IterType &_iter; QList &_list; visitor(SettingsConfigImpl &reader, IterType &iter, QList &list) : _reader{reader}, _iter{iter}, _list{list} {} void process(const SettingsConfigBase::SelectableContrainerInfo &info) const { if(_reader.isUsable(info)) ++_iter; else _iter = _list.erase(_iter); } VariantType operator()(const SettingsConfigBase::EntryType &info) const { process(info); return {}; } VariantType operator()(const SettingsConfigBase::GroupType &info) const { process(info); return {}; } VariantType operator()(const SettingsConfigBase::SectionType &info) const { process(info); return {}; } VariantType operator()(const SettingsConfigBase::CategoryType &info) const { process(info); return {}; } template VariantType operator()(const T &) const { Q_UNREACHABLE(); return {}; } }; } void SettingsConfigImpl::setFilters(QString frontend, const QFileSelector *selector) { _frontend = std::move(frontend); _selector = selector; } void SettingsConfigImpl::resetFilters() { _frontend.clear(); _selector = nullptr; } void SettingsConfigImpl::setIncludesOptional(bool alwaysOptional) { _alwaysOptional = alwaysOptional; } bool SettingsConfigImpl::finish_group_content(QXmlStreamReader &reader, GroupContentGroup &data, bool hasNext) { hasNext = read_GroupContentGroup(reader, data, hasNext); finishContents(reader, data.content); return hasNext; } bool SettingsConfigImpl::finish_section_content(QXmlStreamReader &reader, SectionContentGroup &data, bool hasNext) { hasNext = read_SectionContentGroup(reader, data, hasNext); finishContents(reader, data.content); return hasNext; } bool SettingsConfigImpl::finish_category_content(QXmlStreamReader &reader, CategoryContentGroup &data, bool hasNext) { hasNext = read_CategoryContentGroup(reader, data, hasNext); finishContents(reader, data.content); return hasNext; } bool SettingsConfigImpl::finish_settings_config_content(QXmlStreamReader &reader, SettingsConfigContentGroup &data, bool hasNext) { hasNext = read_SettingsConfigContentGroup(reader, data, hasNext); finishContents(reader, data.content); return hasNext; } QStringList SettingsConfigImpl::allSelectors() const { return {}; } QString SettingsConfigImpl::select(const QString &path) const { return path; } const QFileSelector *SettingsConfigImpl::selector() const { return _selector; } template void SettingsConfigImpl::finishContents(QXmlStreamReader &reader, TGroup &group) { optional index; for(auto it = group.begin(); it != group.end();) { // convert includes to the actual data if(nonstd::holds_alternative(*it)) { if(!isUsable(nonstd::get(*it))) { it = group.erase(it); continue; } if(!readGeneralInclude(reader, nonstd::get(*it), it, group)) continue; } // verify that the contents are all of the same type if(index) { if(index.value() != it->index()) throw XmlException{reader, QStringLiteral("Detected mixture of different child elements. Only includes and a single other type are allowed")}; } else index = it->index(); //erase if not usable due to selectors nonstd::visit(visitor{*this, it, group}, *it); } } template bool SettingsConfigImpl::readGeneralInclude(QXmlStreamReader &reader, IncludeType include, TIter &it, TList &list) { try { if(_selector) include.includePath = select(include.includePath); //make the path relative if possbile if(dynamic_cast(reader.device())) { QFileInfo docInfo{static_cast(reader.device())->fileName()}; include.includePath = docInfo.dir().absoluteFilePath(include.includePath); } // read the document VariantInfo::convert(reader, *it, readDocument(include.includePath)); return true; } catch(FileException &e) { if(include.optional || _alwaysOptional) { qWarning() << e.what(); it = list.erase(it); return false; } else throw; } } bool SettingsConfigImpl::isUsable(const SelectableContrainerInfo &element) const { if(!_frontend.isNull() && element.frontends) { auto fronts = element.frontends.value().split(QLatin1Char('|'), QString::SkipEmptyParts); if(!fronts.contains(_frontend)) return false; } if(_selector && element.selectors) { auto selectors = allSelectors(); auto selects = element.selectors.value().split(QLatin1Char('|'), QString::SkipEmptyParts); for(const auto &select : selects) { auto sels = select.split(QLatin1Char('&'), QString::SkipEmptyParts); auto allSelected = true; for(const auto &sel : sels) { if(!selectors.contains(sel)) { allSelected = false; break; } } if(allSelected) return true; } return false; } return true; }