summaryrefslogtreecommitdiffstats
path: root/configmgr/source/xcuparser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'configmgr/source/xcuparser.cxx')
-rw-r--r--configmgr/source/xcuparser.cxx968
1 files changed, 968 insertions, 0 deletions
diff --git a/configmgr/source/xcuparser.cxx b/configmgr/source/xcuparser.cxx
new file mode 100644
index 000000000..af21518ab
--- /dev/null
+++ b/configmgr/source/xcuparser.cxx
@@ -0,0 +1,968 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <set>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "data.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "groupnode.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parsemanager.hxx"
+#include "partial.hxx"
+#include "propertynode.hxx"
+#include "setnode.hxx"
+#include "xcuparser.hxx"
+#include "xmldata.hxx"
+
+namespace configmgr {
+
+XcuParser::XcuParser(
+ int layer, Data & data, Partial const * partial,
+ Modifications * broadcastModifications, Additions * additions):
+ valueParser_(layer), data_(data),
+ partial_(partial), broadcastModifications_(broadcastModifications),
+ additions_(additions), recordModifications_(layer == Data::NO_LAYER),
+ trackPath_(
+ partial_ != nullptr || broadcastModifications_ != nullptr || additions_ != nullptr ||
+ recordModifications_)
+{}
+
+XcuParser::~XcuParser() {}
+
+xmlreader::XmlReader::Text XcuParser::getTextMode() {
+ return valueParser_.getTextMode();
+}
+
+bool XcuParser::startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * /*existingDependencies*/)
+{
+ if (valueParser_.startElement(reader, nsId, name)) {
+ return true;
+ }
+ if (state_.empty()) {
+ if (nsId == ParseManager::NAMESPACE_OOR &&
+ name == "component-data")
+ {
+ handleComponentData(reader);
+ } else if (nsId == ParseManager::NAMESPACE_OOR && name == "items")
+ {
+ state_.push(State::Modify(rtl::Reference< Node >()));
+ } else {
+ throw css::uno::RuntimeException(
+ "bad root element <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ }
+ } else if (state_.top().ignore) {
+ state_.push(State::Ignore(false));
+ } else if (!state_.top().node.is()) {
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE || name != "item")
+ {
+ throw css::uno::RuntimeException(
+ "bad items node member <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ }
+ handleItem(reader);
+ } else {
+ switch (state_.top().node->kind()) {
+ case Node::KIND_PROPERTY:
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
+ name != "value")
+ {
+ throw css::uno::RuntimeException(
+ "bad property node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ handlePropValue(
+ reader,
+ static_cast< PropertyNode * >(state_.top().node.get()));
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
+ name != "value")
+ {
+ throw css::uno::RuntimeException(
+ "bad localized property node member <" +
+ name.convertFromUtf8() + "> in " + reader.getUrl());
+ }
+ handleLocpropValue(
+ reader,
+ static_cast< LocalizedPropertyNode * >(
+ state_.top().node.get()));
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ throw css::uno::RuntimeException(
+ "bad member <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ case Node::KIND_GROUP:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "prop")
+ {
+ handleGroupProp(
+ reader,
+ static_cast< GroupNode * >(state_.top().node.get()));
+ } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "node")
+ {
+ handleGroupNode(reader, state_.top().node);
+ } else {
+ throw css::uno::RuntimeException(
+ "bad group node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ break;
+ case Node::KIND_SET:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "node")
+ {
+ handleSetNode(
+ reader, static_cast< SetNode * >(state_.top().node.get()));
+ } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "prop")
+ {
+ SAL_WARN(
+ "configmgr",
+ "bad set node <prop> member in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(false));
+ } else {
+ throw css::uno::RuntimeException(
+ "bad set node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ return true;
+}
+
+void XcuParser::endElement(xmlreader::XmlReader const &) {
+ if (valueParser_.endElement()) {
+ return;
+ }
+ assert(!state_.empty());
+ bool pop = state_.top().pop;
+ rtl::Reference< Node > insert;
+ OUString name;
+ if (state_.top().insert) {
+ insert = state_.top().node;
+ assert(insert.is());
+ name = state_.top().name;
+ }
+ state_.pop();
+ if (insert.is()) {
+ assert(!state_.empty() && state_.top().node.is());
+ state_.top().node->getMembers()[name] = insert;
+ }
+ if (pop && !path_.empty()) {
+ path_.pop_back();
+ // </item> will pop less than <item> pushed, but that is harmless,
+ // as the next <item> will reset path_
+ }
+}
+
+void XcuParser::characters(xmlreader::Span const & text) {
+ valueParser_.characters(text);
+}
+
+XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
+ assert(text.is());
+ if (text == "modify") {
+ return OPERATION_MODIFY;
+ }
+ if (text == "replace") {
+ return OPERATION_REPLACE;
+ }
+ if (text == "fuse") {
+ return OPERATION_FUSE;
+ }
+ if (text == "remove") {
+ return OPERATION_REMOVE;
+ }
+ throw css::uno::RuntimeException(
+ "invalid op " + text.convertFromUtf8());
+}
+
+void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
+ OStringBuffer buf(256);
+ buf.append('.');
+ bool hasPackage = false;
+ bool hasName = false;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
+ {
+ if (hasPackage) {
+ throw css::uno::RuntimeException(
+ "multiple component-update package attributes in " +
+ reader.getUrl());
+ }
+ hasPackage = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.insert(0, s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "name")
+ {
+ if (hasName) {
+ throw css::uno::RuntimeException(
+ "multiple component-update name attributes in " +
+ reader.getUrl());
+ }
+ hasName = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.append(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasPackage) {
+ throw css::uno::RuntimeException(
+ "no component-data package attribute in " + reader.getUrl());
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no component-data name attribute in " + reader.getUrl());
+ }
+ componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
+ convertFromUtf8();
+ if (trackPath_) {
+ assert(path_.empty());
+ path_.push_back(componentName_);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ rtl::Reference< Node > node(
+ data_.getComponents().findNode(valueParser_.getLayer(),
+ componentName_));
+ if (!node.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown component \"" << componentName_ << "\" in \""
+ << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ switch (op) {
+ case OPERATION_MODIFY:
+ case OPERATION_FUSE:
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "invalid operation on root node in " + reader.getUrl());
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ node->getFinalized());
+ node->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(node));
+}
+
+void XcuParser::handleItem(xmlreader::XmlReader & reader) {
+ xmlreader::Span attrPath;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "path") {
+ attrPath = reader.getAttributeValue(false);
+ }
+ }
+ if (!attrPath.is()) {
+ throw css::uno::RuntimeException(
+ "missing path attribute in " + reader.getUrl());
+ }
+ OUString path(attrPath.convertFromUtf8());
+ int finalizedLayer;
+ rtl::Reference< Node > node(
+ data_.resolvePathRepresentation(
+ path, nullptr, &path_, &finalizedLayer));
+ if (!node.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ assert(!path_.empty());
+ componentName_ = path_.front();
+ if (trackPath_) {
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ } else {
+ path_.clear();
+ }
+ switch (node->kind()) {
+ case Node::KIND_PROPERTY:
+ case Node::KIND_LOCALIZED_VALUE:
+ SAL_WARN(
+ "configmgr",
+ "item of bad type \"" << path << "\" in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(true));
+ return;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
+ node.get())->getStaticType();
+ break;
+ default:
+ break;
+ }
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(node));
+}
+
+void XcuParser::handlePropValue(
+ xmlreader::XmlReader & reader, PropertyNode * prop)
+ {
+ bool nil = false;
+ OString separator;
+ OUString external;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn == "nil") {
+ nil = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ Type type = xmldata::parseType(
+ reader, reader.getAttributeValue(true));
+ if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
+ throw css::uno::RuntimeException(
+ "invalid value type in " + reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "separator")
+ {
+ xmlreader::Span s(reader.getAttributeValue(false));
+ if (s.length == 0) {
+ throw css::uno::RuntimeException(
+ "bad oor:separator attribute in " + reader.getUrl());
+ }
+ separator = OString(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "external")
+ {
+ external = reader.getAttributeValue(true).convertFromUtf8();
+ if (external.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "bad oor:external attribute value in " + reader.getUrl());
+ }
+ }
+ }
+ if (nil) {
+ if (!prop->isNillable()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
+ }
+ if (!external.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil and oor:external attributes for prop in " +
+ reader.getUrl());
+ }
+ prop->setValue(valueParser_.getLayer(), css::uno::Any());
+ state_.push(State::Ignore(false));
+ } else if (external.isEmpty()) {
+ valueParser_.separator_ = separator;
+ valueParser_.start(prop);
+ } else {
+ prop->setExternal(valueParser_.getLayer(), external);
+ state_.push(State::Ignore(false));
+ }
+}
+
+void XcuParser::handleLocpropValue(
+ xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
+{
+ OUString name;
+ bool nil = false;
+ OString separator;
+ Operation op = OPERATION_FUSE;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
+ attrLn == "lang")
+ {
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
+ attrLn == "nil")
+ {
+ nil = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ Type type = xmldata::parseType(
+ reader, reader.getAttributeValue(true));
+ if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
+ throw css::uno::RuntimeException(
+ "invalid value type in " + reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "separator")
+ {
+ xmlreader::Span s(reader.getAttributeValue(false));
+ if (s.length == 0) {
+ throw css::uno::RuntimeException(
+ "bad oor:separator attribute in " + reader.getUrl());
+ }
+ separator = OString(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ }
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr &&
+ partial_->contains(path_) != Partial::CONTAINS_NODE)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ NodeMap & members = locprop->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (nil && !locprop->isNillable()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
+ }
+ switch (op) {
+ case OPERATION_FUSE:
+ {
+ bool pop = false;
+ if (nil) {
+ if (i == members.end()) {
+ members[name] = new LocalizedValueNode(
+ valueParser_.getLayer(), css::uno::Any());
+ } else {
+ static_cast< LocalizedValueNode * >(
+ i->second.get())->setValue(
+ valueParser_.getLayer(), css::uno::Any());
+ }
+ state_.push(State::Ignore(true));
+ } else {
+ valueParser_.separator_ = separator;
+ valueParser_.start(locprop, name);
+ pop = true;
+ }
+ if (trackPath_) {
+ recordModification(false);
+ if (pop) {
+ path_.pop_back();
+ }
+ }
+ }
+ break;
+ case OPERATION_REMOVE:
+ //TODO: only allow if parent.op == OPERATION_FUSE
+ //TODO: disallow removing when e.g. lang=""?
+ if (i != members.end()) {
+ members.erase(i);
+ }
+ state_.push(State::Ignore(true));
+ recordModification(false);
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "bad op attribute for value element in " + reader.getUrl());
+ }
+}
+
+void XcuParser::handleGroupProp(
+ xmlreader::XmlReader & reader, GroupNode * group)
+{
+ bool hasName = false;
+ OUString name;
+ Type type = TYPE_ERROR;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ type = xmldata::parseType(reader, reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no prop name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ //TODO: This ignores locprop values for which specific include paths
+ // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
+ if (partial_ != nullptr &&
+ partial_->contains(path_) != Partial::CONTAINS_NODE)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ NodeMap & members = group->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i == members.end()) {
+ handleUnknownGroupProp(reader, group, name, type, op, finalized);
+ } else {
+ switch (i->second->kind()) {
+ case Node::KIND_PROPERTY:
+ handlePlainGroupProp(reader, group, i, name, type, op, finalized);
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ handleLocalizedGroupProp(
+ reader,
+ static_cast< LocalizedPropertyNode * >(i->second.get()), name,
+ type, op, finalized);
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "inappropriate prop " + name + " in " + reader.getUrl());
+ }
+ }
+}
+
+void XcuParser::handleUnknownGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode const * group,
+ OUString const & name, Type type, Operation operation, bool finalized)
+{
+ switch (operation) {
+ case OPERATION_REPLACE:
+ case OPERATION_FUSE:
+ if (group->isExtensible()) {
+ if (type == TYPE_ERROR) {
+ throw css::uno::RuntimeException(
+ "missing type attribute for prop " + name + " in " +
+ reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ rtl::Reference< Node > prop(
+ new PropertyNode(
+ valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
+ true));
+ if (finalized) {
+ prop->setFinalized(valueParser_.getLayer());
+ }
+ state_.push(State::Insert(prop, name));
+ recordModification(false);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ SAL_WARN(
+ "configmgr",
+ "unknown property \"" << name << "\" in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(true));
+ break;
+ }
+}
+
+void XcuParser::handlePlainGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode * group,
+ NodeMap::iterator const & propertyIndex, std::u16string_view name,
+ Type type, Operation operation, bool finalized)
+{
+ PropertyNode * property = static_cast< PropertyNode * >(
+ propertyIndex->second.get());
+ if (property->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ property->getFinalized());
+ property->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
+ type != property->getStaticType())
+ {
+ throw css::uno::RuntimeException(
+ OUString::Concat("invalid type for prop ") + name + " in " + reader.getUrl());
+ }
+ valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
+ switch (operation) {
+ case OPERATION_MODIFY:
+ case OPERATION_REPLACE:
+ case OPERATION_FUSE:
+ state_.push(State::Modify(property));
+ recordModification(false);
+ break;
+ case OPERATION_REMOVE:
+ if (!property->isExtension()) {
+ throw css::uno::RuntimeException(
+ OUString::Concat("invalid remove of non-extension prop ") + name + " in " +
+ reader.getUrl());
+ }
+ group->getMembers().erase(propertyIndex);
+ state_.push(State::Ignore(true));
+ recordModification(false);
+ break;
+ }
+}
+
+void XcuParser::handleLocalizedGroupProp(
+ xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
+ OUString const & name, Type type, Operation operation, bool finalized)
+{
+ if (property->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ property->getFinalized());
+ property->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
+ type != property->getStaticType())
+ {
+ throw css::uno::RuntimeException(
+ "invalid type for prop " + name + " in " + reader.getUrl());
+ }
+ valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
+ switch (operation) {
+ case OPERATION_MODIFY:
+ case OPERATION_FUSE:
+ state_.push(State::Modify(property));
+ break;
+ case OPERATION_REPLACE:
+ {
+ rtl::Reference< Node > replacement(
+ new LocalizedPropertyNode(
+ valueParser_.getLayer(), property->getStaticType(),
+ property->isNillable()));
+ replacement->setFinalized(property->getFinalized());
+ state_.push(State::Insert(replacement, name));
+ recordModification(false);
+ }
+ break;
+ case OPERATION_REMOVE:
+ throw css::uno::RuntimeException(
+ "invalid remove of non-extension prop " + name + " in " +
+ reader.getUrl());
+ }
+}
+
+void XcuParser::handleGroupNode(
+ xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
+{
+ bool hasName = false;
+ OUString name;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no node name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ rtl::Reference< Node > child(
+ group->getMembers().findNode(valueParser_.getLayer(), name));
+ if (!child.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ Node::Kind kind = child->kind();
+ if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
+ throw css::uno::RuntimeException(
+ "bad <node> \"" + name + "\" of non group/set kind in " +
+ reader.getUrl());
+ }
+ if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
+ throw css::uno::RuntimeException(
+ "invalid operation on group node in " + reader.getUrl());
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ child->getFinalized());
+ child->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(child));
+}
+
+void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
+ bool hasName = false;
+ OUString name;
+ OUString component(componentName_);
+ bool hasNodeType = false;
+ OUString nodeType;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ bool mandatory = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "component")
+ {
+ component = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "node-type")
+ {
+ hasNodeType = true;
+ nodeType = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "mandatory")
+ {
+ mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no node name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ OUString templateName(
+ xmldata::parseTemplateReference(
+ component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
+ if (!set->isValidTemplate(templateName)) {
+ throw css::uno::RuntimeException(
+ "set member node " + name + " references invalid template " +
+ templateName + " in " + reader.getUrl());
+ }
+ rtl::Reference< Node > tmpl(
+ data_.getTemplate(valueParser_.getLayer(), templateName));
+ if (!tmpl.is()) {
+ throw css::uno::RuntimeException(
+ "set member node " + name + " references undefined template " +
+ templateName + " in " + reader.getUrl());
+ }
+ int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
+ int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
+ NodeMap & members = set->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i != members.end()) {
+ finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
+ i->second->setFinalized(finalizedLayer);
+ mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
+ i->second->setMandatory(mandatoryLayer);
+ if (i->second->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ switch (op) {
+ case OPERATION_MODIFY:
+ if (i == members.end()) {
+ SAL_WARN(
+ "configmgr",
+ "ignoring modify of unknown set member node \"" << name
+ << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ } else {
+ state_.push(State::Modify(i->second));
+ }
+ break;
+ case OPERATION_REPLACE:
+ {
+ rtl::Reference< Node > member(tmpl->clone(true));
+ member->setLayer(valueParser_.getLayer());
+ member->setFinalized(finalizedLayer);
+ member->setMandatory(mandatoryLayer);
+ state_.push(State::Insert(member, name));
+ recordModification(i == members.end());
+ }
+ break;
+ case OPERATION_FUSE:
+ if (i == members.end()) {
+ rtl::Reference< Node > member(tmpl->clone(true));
+ member->setLayer(valueParser_.getLayer());
+ member->setFinalized(finalizedLayer);
+ member->setMandatory(mandatoryLayer);
+ state_.push(State::Insert(member, name));
+ recordModification(true);
+ } else {
+ state_.push(State::Modify(i->second));
+ }
+ break;
+ case OPERATION_REMOVE:
+ {
+ // Ignore removal of unknown members and members made mandatory in
+ // this or a lower layer; forget about user-layer removals that no
+ // longer remove anything (so that paired additions/removals in the
+ // user layer do not grow registrymodifications.xcu unbounded):
+ bool known = i != members.end();
+ if (known &&
+ (mandatoryLayer == Data::NO_LAYER ||
+ mandatoryLayer > valueParser_.getLayer()))
+ {
+ members.erase(i);
+ }
+ state_.push(State::Ignore(true));
+ if (known) {
+ recordModification(false);
+ }
+ break;
+ }
+ }
+}
+
+void XcuParser::recordModification(bool addition) {
+ if (broadcastModifications_ != nullptr) {
+ broadcastModifications_->add(path_);
+ }
+ if (addition && additions_ != nullptr) {
+ additions_->push_back(path_);
+ }
+ if (recordModifications_) {
+ data_.modifications.add(path_);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */