blob: 924162fab4cbd261215efa0157342fa7ede50bdb [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_configmgr.hxx"
#include "sal/config.h"
#include <algorithm>
#include "com/sun/star/uno/Any.hxx"
#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/uno/RuntimeException.hpp"
#include "com/sun/star/uno/XInterface.hpp"
#include "osl/diagnose.h"
#include "rtl/ref.hxx"
#include "rtl/strbuf.hxx"
#include "rtl/string.h"
#include "rtl/string.hxx"
#include "rtl/ustring.h"
#include "rtl/ustring.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 "path.hxx"
#include "propertynode.hxx"
#include "setnode.hxx"
#include "xcuparser.hxx"
#include "xmldata.hxx"
namespace configmgr {
namespace {
namespace css = com::sun::star;
}
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_ != 0 || broadcastModifications_ != 0 || additions_ != 0 ||
recordModifications_)
{}
XcuParser::~XcuParser() {}
xmlreader::XmlReader::Text XcuParser::getTextMode() {
return valueParser_.getTextMode();
}
bool XcuParser::startElement(
xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
{
if (valueParser_.startElement(reader, nsId, name)) {
return true;
}
if (state_.empty()) {
if (nsId == ParseManager::NAMESPACE_OOR &&
name.equals(RTL_CONSTASCII_STRINGPARAM("component-data")))
{
handleComponentData(reader);
} else if (nsId == ParseManager::NAMESPACE_OOR &&
name.equals(RTL_CONSTASCII_STRINGPARAM("items")))
{
state_.push(State(rtl::Reference< Node >(), false));
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("bad root element <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
} else if (state_.top().ignore) {
state_.push(State(false));
} else if (!state_.top().node.is()) {
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("item")))
{
handleItem(reader);
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("bad items node member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
} else {
switch (state_.top().node->kind()) {
case Node::KIND_PROPERTY:
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("value")))
{
handlePropValue(
reader,
dynamic_cast< PropertyNode * >(state_.top().node.get()));
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad property node member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
break;
case Node::KIND_LOCALIZED_PROPERTY:
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("value")))
{
handleLocpropValue(
reader,
dynamic_cast< LocalizedPropertyNode * >(
state_.top().node.get()));
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad localized property node member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
break;
case Node::KIND_LOCALIZED_VALUE:
throw css::uno::RuntimeException(
(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
case Node::KIND_GROUP:
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
{
handleGroupProp(
reader,
dynamic_cast< GroupNode * >(state_.top().node.get()));
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("node")))
{
handleGroupNode(reader, state_.top().node);
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad group node member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
break;
case Node::KIND_SET:
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("node")))
{
handleSetNode(
reader, dynamic_cast< SetNode * >(state_.top().node.get()));
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
name.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
{
OSL_TRACE(
"configmgr bad set node <prop> member in %s",
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
} else {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("bad set node member <")) +
name.convertFromUtf8() +
rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
break;
}
}
return true;
}
void XcuParser::endElement(xmlreader::XmlReader const &) {
if (valueParser_.endElement()) {
return;
}
OSL_ASSERT(!state_.empty());
bool pop = state_.top().pop;
rtl::Reference< Node > insert;
rtl::OUString name;
if (state_.top().insert) {
insert = state_.top().node;
OSL_ASSERT(insert.is());
name = state_.top().name;
}
state_.pop();
if (insert.is()) {
OSL_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) {
OSL_ASSERT(text.is());
if (text.equals(RTL_CONSTASCII_STRINGPARAM("modify"))) {
return OPERATION_MODIFY;
}
if (text.equals(RTL_CONSTASCII_STRINGPARAM("replace"))) {
return OPERATION_REPLACE;
}
if (text.equals(RTL_CONSTASCII_STRINGPARAM("fuse"))) {
return OPERATION_FUSE;
}
if (text.equals(RTL_CONSTASCII_STRINGPARAM("remove"))) {
return OPERATION_REMOVE;
}
throw css::uno::RuntimeException(
(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("invalid op ")) +
text.convertFromUtf8()),
css::uno::Reference< css::uno::XInterface >());
}
void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
rtl::OStringBuffer buf;
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.equals(RTL_CONSTASCII_STRINGPARAM("package")))
{
if (hasPackage) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"multiple component-update package attributes"
" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
hasPackage = true;
xmlreader::Span s(reader.getAttributeValue(false));
buf.insert(0, s.begin, s.length);
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
{
if (hasName) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"multiple component-update name attributes in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
hasName = true;
xmlreader::Span s(reader.getAttributeValue(false));
buf.append(s.begin, s.length);
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
{
op = parseOperation(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
{
finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
}
}
if (!hasPackage) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"no component-data package attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
if (!hasName) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"no component-data name attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
convertFromUtf8();
if (trackPath_) {
OSL_ASSERT(path_.empty());
path_.push_back(componentName_);
if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
{
state_.push(State(true)); // ignored
return;
}
}
rtl::Reference< Node > node(
Data::findNode(
valueParser_.getLayer(), data_.components, componentName_));
if (!node.is()) {
OSL_TRACE(
"configmgr unknown component %s in %s",
rtl::OUStringToOString(
componentName_, RTL_TEXTENCODING_UTF8).getStr(),
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
return;
}
switch (op) {
case OPERATION_MODIFY:
case OPERATION_FUSE:
break;
default:
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"invalid operation on root node in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
int finalizedLayer = std::min(
finalized ? valueParser_.getLayer() : Data::NO_LAYER,
node->getFinalized());
node->setFinalized(finalizedLayer);
state_.push(State(node, finalizedLayer < valueParser_.getLayer()));
}
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.equals(RTL_CONSTASCII_STRINGPARAM("path")))
{
attrPath = reader.getAttributeValue(false);
}
}
if (!attrPath.is()) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("missing path attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
rtl::OUString path(attrPath.convertFromUtf8());
int finalizedLayer;
rtl::Reference< Node > node(
data_.resolvePathRepresentation(
path, 0, &path_, &finalizedLayer));
if (!node.is()) {
OSL_TRACE(
"configmgr unknown item %s in %s",
rtl::OUStringToOString(path, RTL_TEXTENCODING_UTF8).getStr(),
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
return;
}
OSL_ASSERT(!path_.empty());
componentName_ = path_.front();
if (trackPath_) {
if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
{
state_.push(State(true)); // ignored
return;
}
} else {
path_.clear();
}
switch (node->kind()) {
case Node::KIND_PROPERTY:
case Node::KIND_LOCALIZED_VALUE:
OSL_TRACE(
"configmgr item of bad type %s in %s",
rtl::OUStringToOString(path, RTL_TEXTENCODING_UTF8).getStr(),
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
return;
case Node::KIND_LOCALIZED_PROPERTY:
valueParser_.type_ = dynamic_cast< LocalizedPropertyNode * >(
node.get())->getStaticType();
break;
default:
break;
}
state_.push(State(node, finalizedLayer < valueParser_.getLayer()));
}
void XcuParser::handlePropValue(
xmlreader::XmlReader & reader, PropertyNode * prop)
{
bool nil = false;
rtl::OString separator;
rtl::OUString external;
for (;;) {
int attrNsId;
xmlreader::Span attrLn;
if (!reader.nextAttribute(&attrNsId, &attrLn)) {
break;
}
if (attrNsId == ParseManager::NAMESPACE_XSI &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("nil")))
{
nil = xmldata::parseBoolean(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
{
Type type = xmldata::parseType(
reader, reader.getAttributeValue(true));
if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("invalid value type in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
valueParser_.type_ = type;
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("separator")))
{
xmlreader::Span s(reader.getAttributeValue(false));
if (s.length == 0) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad oor:separator attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
separator = rtl::OString(s.begin, s.length);
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("external")))
{
external = reader.getAttributeValue(true).convertFromUtf8();
if (external.getLength() == 0) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad oor:external attribute value in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
}
}
if (nil) {
if (!prop->isNillable()) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"xsi:nil attribute for non-nillable prop in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
if (external.getLength() != 0) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"xsi:nil and oor:external attributes for prop in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
prop->setValue(valueParser_.getLayer(), css::uno::Any());
state_.push(State(false));
} else if (external.getLength() == 0) {
valueParser_.separator_ = separator;
valueParser_.start(prop);
} else {
prop->setExternal(valueParser_.getLayer(), external);
state_.push(State(false));
}
}
void XcuParser::handleLocpropValue(
xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
{
rtl::OUString name;
bool nil = false;
rtl::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.equals(RTL_CONSTASCII_STRINGPARAM("lang")))
{
name = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_XSI &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("nil")))
{
nil = xmldata::parseBoolean(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
{
Type type = xmldata::parseType(
reader, reader.getAttributeValue(true));
if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("invalid value type in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
valueParser_.type_ = type;
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("separator")))
{
xmlreader::Span s(reader.getAttributeValue(false));
if (s.length == 0) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad oor:separator attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
separator = rtl::OString(s.begin, s.length);
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
{
op = parseOperation(reader.getAttributeValue(true));
}
}
if (trackPath_) {
path_.push_back(name);
if (partial_ != 0 &&
partial_->contains(path_) != Partial::CONTAINS_NODE)
{
state_.push(State(true)); // ignored
return;
}
}
NodeMap::iterator i(locprop->getMembers().find(name));
if (i != locprop->getMembers().end() &&
i->second->getLayer() > valueParser_.getLayer())
{
state_.push(State(true)); // ignored
return;
}
if (nil && !locprop->isNillable()) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"xsi:nil attribute for non-nillable prop in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
switch (op) {
case OPERATION_FUSE:
{
bool pop = false;
if (nil) {
if (i == locprop->getMembers().end()) {
locprop->getMembers()[name] = new LocalizedValueNode(
valueParser_.getLayer(), css::uno::Any());
} else {
dynamic_cast< LocalizedValueNode * >(
i->second.get())->setValue(
valueParser_.getLayer(), css::uno::Any());
}
state_.push(State(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 != locprop->getMembers().end()) {
locprop->getMembers().erase(i);
}
state_.push(State(true));
recordModification(false);
break;
default:
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"bad op attribute for value element in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
}
void XcuParser::handleGroupProp(
xmlreader::XmlReader & reader, GroupNode * group)
{
bool hasName = false;
rtl::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.equals(RTL_CONSTASCII_STRINGPARAM("name")))
{
hasName = true;
name = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
{
type = xmldata::parseType(reader, reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
{
op = parseOperation(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
{
finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
}
}
if (!hasName) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("no prop name attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
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_ != 0 &&
partial_->contains(path_) != Partial::CONTAINS_NODE)
{
state_.push(State(true)); // ignored
return;
}
}
NodeMap::iterator i(group->getMembers().find(name));
if (i == group->getMembers().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,
dynamic_cast< LocalizedPropertyNode * >(i->second.get()), name,
type, op, finalized);
break;
default:
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("inappropriate prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
}
}
void XcuParser::handleUnknownGroupProp(
xmlreader::XmlReader const & reader, GroupNode * group,
rtl::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(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"missing type attribute for prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
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(prop, name, state_.top().locked));
recordModification(false);
break;
}
// fall through
default:
OSL_TRACE(
"configmgr unknown property %s in %s",
rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr(),
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
break;
}
}
void XcuParser::handlePlainGroupProp(
xmlreader::XmlReader const & reader, GroupNode * group,
NodeMap::iterator const & propertyIndex, rtl::OUString const & name,
Type type, Operation operation, bool finalized)
{
PropertyNode * property = dynamic_cast< PropertyNode * >(
propertyIndex->second.get());
if (property->getLayer() > valueParser_.getLayer()) {
state_.push(State(true)); // ignored
return;
}
int finalizedLayer = std::min(
finalized ? valueParser_.getLayer() : Data::NO_LAYER,
property->getFinalized());
property->setFinalized(finalizedLayer);
if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
type != property->getStaticType())
{
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("invalid type for prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
switch (operation) {
case OPERATION_MODIFY:
case OPERATION_REPLACE:
case OPERATION_FUSE:
state_.push(
State(
property,
(state_.top().locked ||
finalizedLayer < valueParser_.getLayer())));
recordModification(false);
break;
case OPERATION_REMOVE:
if (!property->isExtension()) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"invalid remove of non-extension prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
group->getMembers().erase(propertyIndex);
state_.push(State(true)); // ignore children
recordModification(false);
break;
}
}
void XcuParser::handleLocalizedGroupProp(
xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
rtl::OUString const & name, Type type, Operation operation, bool finalized)
{
if (property->getLayer() > valueParser_.getLayer()) {
state_.push(State(true)); // ignored
return;
}
int finalizedLayer = std::min(
finalized ? valueParser_.getLayer() : Data::NO_LAYER,
property->getFinalized());
property->setFinalized(finalizedLayer);
if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
type != property->getStaticType())
{
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("invalid type for prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
switch (operation) {
case OPERATION_MODIFY:
case OPERATION_FUSE:
state_.push(
State(
property,
(state_.top().locked ||
finalizedLayer < valueParser_.getLayer())));
break;
case OPERATION_REPLACE:
{
rtl::Reference< Node > replacement(
new LocalizedPropertyNode(
valueParser_.getLayer(), property->getStaticType(),
property->isNillable()));
replacement->setFinalized(property->getFinalized());
state_.push(
State(
replacement, name,
(state_.top().locked ||
finalizedLayer < valueParser_.getLayer())));
recordModification(false);
}
break;
case OPERATION_REMOVE:
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"invalid remove of non-extension prop ")) +
name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
}
void XcuParser::handleGroupNode(
xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
{
bool hasName = false;
rtl::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.equals(RTL_CONSTASCII_STRINGPARAM("name")))
{
hasName = true;
name = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
{
op = parseOperation(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
{
finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
}
}
if (!hasName) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("no node name attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
if (trackPath_) {
path_.push_back(name);
if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
{
state_.push(State(true)); // ignored
return;
}
}
rtl::Reference< Node > child(
Data::findNode(valueParser_.getLayer(), group->getMembers(), name));
if (!child.is()) {
OSL_TRACE(
"configmgr unknown node %s in %s",
rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr(),
rtl::OUStringToOString(
reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
state_.push(State(true)); // ignored
return;
}
if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"invalid operation on group node in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
int finalizedLayer = std::min(
finalized ? valueParser_.getLayer() : Data::NO_LAYER,
child->getFinalized());
child->setFinalized(finalizedLayer);
state_.push(
State(
child,
state_.top().locked || finalizedLayer < valueParser_.getLayer()));
}
void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
bool hasName = false;
rtl::OUString name;
rtl::OUString component(componentName_);
bool hasNodeType = false;
rtl::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.equals(RTL_CONSTASCII_STRINGPARAM("name")))
{
hasName = true;
name = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("component")))
{
component = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("node-type")))
{
hasNodeType = true;
nodeType = reader.getAttributeValue(false).convertFromUtf8();
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
{
op = parseOperation(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
{
finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
} else if (attrNsId == ParseManager::NAMESPACE_OOR &&
attrLn.equals(RTL_CONSTASCII_STRINGPARAM("mandatory")))
{
mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
}
}
if (!hasName) {
throw css::uno::RuntimeException(
(rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM("no node name attribute in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
if (trackPath_) {
path_.push_back(name);
if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
{
state_.push(State(true)); // ignored
return;
}
}
rtl::OUString templateName(
xmldata::parseTemplateReference(
component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
if (!set->isValidTemplate(templateName)) {
throw css::uno::RuntimeException(
(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("set member node ")) +
name +
rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(" references invalid template ")) +
templateName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
rtl::Reference< Node > tmpl(
data_.getTemplate(valueParser_.getLayer(), templateName));
if (!tmpl.is()) {
throw css::uno::RuntimeException(
(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("set member node ")) +
name +
rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
" references undefined template ")) +
templateName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
reader.getUrl()),
css::uno::Reference< css::uno::XInterface >());
}
int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
NodeMap::iterator i(set->getMembers().find(name));
if (i != set->getMembers().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(true)); // ignored
return;
}
}
switch (op) {
case OPERATION_MODIFY:
if (i == set->getMembers().end()) {
OSL_TRACE("ignoring modify of unknown set member node");
state_.push(State(true)); // ignored
} else {
state_.push(
State(
i->second,
(state_.top().locked ||
finalizedLayer < valueParser_.getLayer())));
}
break;
case OPERATION_REPLACE:
if (state_.top().locked || finalizedLayer < valueParser_.getLayer()) {
state_.push(State(true)); // ignored
} else {
rtl::Reference< Node > member(tmpl->clone(true));
member->setLayer(valueParser_.getLayer());
member->setFinalized(finalizedLayer);
member->setMandatory(mandatoryLayer);
state_.push(State(member, name, false));
recordModification(i == set->getMembers().end());
}
break;
case OPERATION_FUSE:
if (i == set->getMembers().end()) {
if (state_.top().locked || finalizedLayer < valueParser_.getLayer())
{
state_.push(State(true)); // ignored
} else {
rtl::Reference< Node > member(tmpl->clone(true));
member->setLayer(valueParser_.getLayer());
member->setFinalized(finalizedLayer);
member->setMandatory(mandatoryLayer);
state_.push(State(member, name, false));
recordModification(true);
}
} else {
state_.push(
State(
i->second,
(state_.top().locked ||
finalizedLayer < valueParser_.getLayer())));
}
break;
case OPERATION_REMOVE:
{
// Ignore removal of unknown members, members finalized in a lower
// layer, 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 != set->getMembers().end();
if (known && !state_.top().locked &&
finalizedLayer >= valueParser_.getLayer() &&
mandatoryLayer > valueParser_.getLayer())
{
set->getMembers().erase(i);
}
state_.push(State(true));
if (known) {
recordModification(false);
}
break;
}
}
}
void XcuParser::recordModification(bool addition) {
if (broadcastModifications_ != 0) {
broadcastModifications_->add(path_);
}
if (addition && additions_ != 0) {
additions_->push_back(path_);
}
if (recordModifications_) {
data_.modifications.add(path_);
}
}
}