Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / configmgr / source / xcuparser.cxx
blob03e3c60ab0678cbdf002ecd522d1880ca3cc5743
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <set>
26 #include <com/sun/star/uno/Any.hxx>
27 #include <com/sun/star/uno/RuntimeException.hpp>
28 #include <rtl/ref.hxx>
29 #include <rtl/strbuf.hxx>
30 #include <rtl/string.hxx>
31 #include <rtl/ustring.hxx>
32 #include <sal/log.hxx>
33 #include <xmlreader/span.hxx>
34 #include <xmlreader/xmlreader.hxx>
36 #include "data.hxx"
37 #include "localizedpropertynode.hxx"
38 #include "localizedvaluenode.hxx"
39 #include "groupnode.hxx"
40 #include "modifications.hxx"
41 #include "node.hxx"
42 #include "nodemap.hxx"
43 #include "parsemanager.hxx"
44 #include "partial.hxx"
45 #include "propertynode.hxx"
46 #include "setnode.hxx"
47 #include "xcuparser.hxx"
48 #include "xmldata.hxx"
50 namespace configmgr {
52 XcuParser::XcuParser(
53 int layer, Data & data, Partial const * partial,
54 Modifications * broadcastModifications, Additions * additions):
55 valueParser_(layer), data_(data),
56 partial_(partial), broadcastModifications_(broadcastModifications),
57 additions_(additions), recordModifications_(layer == Data::NO_LAYER),
58 trackPath_(
59 partial_ != nullptr || broadcastModifications_ != nullptr || additions_ != nullptr ||
60 recordModifications_)
63 XcuParser::~XcuParser() {}
65 xmlreader::XmlReader::Text XcuParser::getTextMode() {
66 return valueParser_.getTextMode();
69 bool XcuParser::startElement(
70 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
71 std::set< OUString > const * /*existingDependencies*/)
73 if (valueParser_.startElement(reader, nsId, name)) {
74 return true;
76 if (state_.empty()) {
77 if (nsId == ParseManager::NAMESPACE_OOR &&
78 name == "component-data")
80 handleComponentData(reader);
81 } else if (nsId == ParseManager::NAMESPACE_OOR && name == "items")
83 state_.push(State::Modify(rtl::Reference< Node >()));
84 } else {
85 throw css::uno::RuntimeException(
86 "bad root element <" + name.convertFromUtf8() + "> in " +
87 reader.getUrl());
89 } else if (state_.top().ignore) {
90 state_.push(State::Ignore(false));
91 } else if (!state_.top().node.is()) {
92 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE || name != "item")
94 throw css::uno::RuntimeException(
95 "bad items node member <" + name.convertFromUtf8() + "> in " +
96 reader.getUrl());
98 handleItem(reader);
99 } else {
100 switch (state_.top().node->kind()) {
101 case Node::KIND_PROPERTY:
102 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
103 name != "value")
105 throw css::uno::RuntimeException(
106 "bad property node member <" + name.convertFromUtf8() +
107 "> in " + reader.getUrl());
109 handlePropValue(
110 reader,
111 static_cast< PropertyNode * >(state_.top().node.get()));
112 break;
113 case Node::KIND_LOCALIZED_PROPERTY:
114 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
115 name != "value")
117 throw css::uno::RuntimeException(
118 "bad localized property node member <" +
119 name.convertFromUtf8() + "> in " + reader.getUrl());
121 handleLocpropValue(
122 reader,
123 static_cast< LocalizedPropertyNode * >(
124 state_.top().node.get()));
125 break;
126 case Node::KIND_LOCALIZED_VALUE:
127 throw css::uno::RuntimeException(
128 "bad member <" + name.convertFromUtf8() + "> in " +
129 reader.getUrl());
130 case Node::KIND_GROUP:
131 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
132 name == "prop")
134 handleGroupProp(
135 reader,
136 static_cast< GroupNode * >(state_.top().node.get()));
137 } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
138 name == "node")
140 handleGroupNode(reader, state_.top().node);
141 } else {
142 throw css::uno::RuntimeException(
143 "bad group node member <" + name.convertFromUtf8() +
144 "> in " + reader.getUrl());
146 break;
147 case Node::KIND_SET:
148 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
149 name == "node")
151 handleSetNode(
152 reader, static_cast< SetNode * >(state_.top().node.get()));
153 } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
154 name == "prop")
156 SAL_WARN(
157 "configmgr",
158 "bad set node <prop> member in \"" << reader.getUrl()
159 << '"');
160 state_.push(State::Ignore(false));
161 } else {
162 throw css::uno::RuntimeException(
163 "bad set node member <" + name.convertFromUtf8() +
164 "> in " + reader.getUrl());
166 break;
167 case Node::KIND_ROOT:
168 assert(false); // this cannot happen
169 break;
172 return true;
175 void XcuParser::endElement(xmlreader::XmlReader const &) {
176 if (valueParser_.endElement()) {
177 return;
179 assert(!state_.empty());
180 bool pop = state_.top().pop;
181 rtl::Reference< Node > insert;
182 OUString name;
183 if (state_.top().insert) {
184 insert = state_.top().node;
185 assert(insert.is());
186 name = state_.top().name;
188 state_.pop();
189 if (insert.is()) {
190 assert(!state_.empty() && state_.top().node.is());
191 state_.top().node->getMembers()[name] = insert;
193 if (pop && !path_.empty()) {
194 path_.pop_back();
195 // </item> will pop less than <item> pushed, but that is harmless,
196 // as the next <item> will reset path_
200 void XcuParser::characters(xmlreader::Span const & text) {
201 valueParser_.characters(text);
204 XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
205 assert(text.is());
206 if (text == "modify") {
207 return OPERATION_MODIFY;
209 if (text == "replace") {
210 return OPERATION_REPLACE;
212 if (text == "fuse") {
213 return OPERATION_FUSE;
215 if (text == "remove") {
216 return OPERATION_REMOVE;
218 throw css::uno::RuntimeException(
219 "invalid op " + text.convertFromUtf8());
222 void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
223 OStringBuffer buf(256);
224 buf.append('.');
225 bool hasPackage = false;
226 bool hasName = false;
227 Operation op = OPERATION_MODIFY;
228 bool finalized = false;
229 for (;;) {
230 int attrNsId;
231 xmlreader::Span attrLn;
232 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
233 break;
235 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
237 if (hasPackage) {
238 throw css::uno::RuntimeException(
239 "multiple component-update package attributes in " +
240 reader.getUrl());
242 hasPackage = true;
243 xmlreader::Span s(reader.getAttributeValue(false));
244 buf.insert(0, s.begin, s.length);
245 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
246 attrLn == "name")
248 if (hasName) {
249 throw css::uno::RuntimeException(
250 "multiple component-update name attributes in " +
251 reader.getUrl());
253 hasName = true;
254 xmlreader::Span s(reader.getAttributeValue(false));
255 buf.append(s.begin, s.length);
256 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
257 attrLn == "op")
259 op = parseOperation(reader.getAttributeValue(true));
260 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
261 attrLn == "finalized")
263 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
266 if (!hasPackage) {
267 throw css::uno::RuntimeException(
268 "no component-data package attribute in " + reader.getUrl());
270 if (!hasName) {
271 throw css::uno::RuntimeException(
272 "no component-data name attribute in " + reader.getUrl());
274 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
275 convertFromUtf8();
276 if (trackPath_) {
277 assert(path_.empty());
278 path_.push_back(componentName_);
279 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
281 state_.push(State::Ignore(true));
282 return;
285 rtl::Reference< Node > node(
286 data_.getComponents().findNode(valueParser_.getLayer(),
287 componentName_));
288 if (!node.is()) {
289 SAL_WARN(
290 "configmgr",
291 "unknown component \"" << componentName_ << "\" in \""
292 << reader.getUrl() << '"');
293 state_.push(State::Ignore(true));
294 return;
296 switch (op) {
297 case OPERATION_MODIFY:
298 case OPERATION_FUSE:
299 break;
300 default:
301 throw css::uno::RuntimeException(
302 "invalid operation on root node in " + reader.getUrl());
304 int finalizedLayer = std::min(
305 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
306 node->getFinalized());
307 node->setFinalized(finalizedLayer);
308 if (finalizedLayer < valueParser_.getLayer()) {
309 state_.push(State::Ignore(true));
310 return;
312 state_.push(State::Modify(node));
315 void XcuParser::handleItem(xmlreader::XmlReader & reader) {
316 xmlreader::Span attrPath;
317 for (;;) {
318 int attrNsId;
319 xmlreader::Span attrLn;
320 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
321 break;
323 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "path") {
324 attrPath = reader.getAttributeValue(false);
327 if (!attrPath.is()) {
328 throw css::uno::RuntimeException(
329 "missing path attribute in " + reader.getUrl());
331 OUString path(attrPath.convertFromUtf8());
332 int finalizedLayer;
333 rtl::Reference< Node > node(
334 data_.resolvePathRepresentation(
335 path, nullptr, &path_, &finalizedLayer));
336 if (!node.is()) {
337 SAL_WARN(
338 "configmgr",
339 "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
340 state_.push(State::Ignore(true));
341 return;
343 assert(!path_.empty());
344 componentName_ = path_.front();
345 if (trackPath_) {
346 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
348 state_.push(State::Ignore(true));
349 return;
351 } else {
352 path_.clear();
354 switch (node->kind()) {
355 case Node::KIND_PROPERTY:
356 case Node::KIND_LOCALIZED_VALUE:
357 SAL_WARN(
358 "configmgr",
359 "item of bad type \"" << path << "\" in \"" << reader.getUrl()
360 << '"');
361 state_.push(State::Ignore(true));
362 return;
363 case Node::KIND_LOCALIZED_PROPERTY:
364 valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
365 node.get())->getStaticType();
366 break;
367 default:
368 break;
370 if (finalizedLayer < valueParser_.getLayer()) {
371 state_.push(State::Ignore(true));
372 return;
374 state_.push(State::Modify(node));
377 void XcuParser::handlePropValue(
378 xmlreader::XmlReader & reader, PropertyNode * prop)
380 bool nil = false;
381 OString separator;
382 OUString external;
383 for (;;) {
384 int attrNsId;
385 xmlreader::Span attrLn;
386 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
387 break;
389 if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn == "nil") {
390 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
391 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
392 attrLn == "type")
394 Type type = xmldata::parseType(
395 reader, reader.getAttributeValue(true));
396 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
397 throw css::uno::RuntimeException(
398 "invalid value type in " + reader.getUrl());
400 valueParser_.type_ = type;
401 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
402 attrLn == "separator")
404 xmlreader::Span s(reader.getAttributeValue(false));
405 if (s.length == 0) {
406 throw css::uno::RuntimeException(
407 "bad oor:separator attribute in " + reader.getUrl());
409 separator = OString(s.begin, s.length);
410 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
411 attrLn == "external")
413 external = reader.getAttributeValue(true).convertFromUtf8();
414 if (external.isEmpty()) {
415 throw css::uno::RuntimeException(
416 "bad oor:external attribute value in " + reader.getUrl());
420 if (nil) {
421 if (!prop->isNillable()) {
422 throw css::uno::RuntimeException(
423 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
425 if (!external.isEmpty()) {
426 throw css::uno::RuntimeException(
427 "xsi:nil and oor:external attributes for prop in " +
428 reader.getUrl());
430 prop->setValue(valueParser_.getLayer(), css::uno::Any());
431 state_.push(State::Ignore(false));
432 } else if (external.isEmpty()) {
433 valueParser_.separator_ = separator;
434 valueParser_.start(prop);
435 } else {
436 prop->setExternal(valueParser_.getLayer(), external);
437 state_.push(State::Ignore(false));
441 void XcuParser::handleLocpropValue(
442 xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
444 OUString name;
445 bool nil = false;
446 OString separator;
447 Operation op = OPERATION_FUSE;
448 for (;;) {
449 int attrNsId;
450 xmlreader::Span attrLn;
451 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
452 break;
454 if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
455 attrLn == "lang")
457 name = reader.getAttributeValue(false).convertFromUtf8();
458 } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
459 attrLn == "nil")
461 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
462 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
463 attrLn == "type")
465 Type type = xmldata::parseType(
466 reader, reader.getAttributeValue(true));
467 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
468 throw css::uno::RuntimeException(
469 "invalid value type in " + reader.getUrl());
471 valueParser_.type_ = type;
472 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
473 attrLn == "separator")
475 xmlreader::Span s(reader.getAttributeValue(false));
476 if (s.length == 0) {
477 throw css::uno::RuntimeException(
478 "bad oor:separator attribute in " + reader.getUrl());
480 separator = OString(s.begin, s.length);
481 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
482 attrLn == "op")
484 op = parseOperation(reader.getAttributeValue(true));
487 if (trackPath_) {
488 path_.push_back(name);
489 if (partial_ != nullptr &&
490 partial_->contains(path_) != Partial::CONTAINS_NODE)
492 state_.push(State::Ignore(true));
493 return;
496 NodeMap & members = locprop->getMembers();
497 NodeMap::iterator i(members.find(name));
498 if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
499 state_.push(State::Ignore(true));
500 return;
502 if (nil && !locprop->isNillable()) {
503 throw css::uno::RuntimeException(
504 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
506 switch (op) {
507 case OPERATION_FUSE:
509 bool pop = false;
510 if (nil) {
511 if (i == members.end()) {
512 members[name] = new LocalizedValueNode(
513 valueParser_.getLayer(), css::uno::Any());
514 } else {
515 static_cast< LocalizedValueNode * >(
516 i->second.get())->setValue(
517 valueParser_.getLayer(), css::uno::Any());
519 state_.push(State::Ignore(true));
520 } else {
521 valueParser_.separator_ = separator;
522 valueParser_.start(locprop, name);
523 pop = true;
525 if (trackPath_) {
526 recordModification(false);
527 if (pop) {
528 path_.pop_back();
532 break;
533 case OPERATION_REMOVE:
534 //TODO: only allow if parent.op == OPERATION_FUSE
535 //TODO: disallow removing when e.g. lang=""?
536 if (i != members.end()) {
537 members.erase(i);
539 state_.push(State::Ignore(true));
540 recordModification(false);
541 break;
542 default:
543 throw css::uno::RuntimeException(
544 "bad op attribute for value element in " + reader.getUrl());
548 void XcuParser::handleGroupProp(
549 xmlreader::XmlReader & reader, GroupNode * group)
551 bool hasName = false;
552 OUString name;
553 Type type = TYPE_ERROR;
554 Operation op = OPERATION_MODIFY;
555 bool finalized = false;
556 for (;;) {
557 int attrNsId;
558 xmlreader::Span attrLn;
559 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
560 break;
562 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
563 hasName = true;
564 name = reader.getAttributeValue(false).convertFromUtf8();
565 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
566 attrLn == "type")
568 type = xmldata::parseType(reader, reader.getAttributeValue(true));
569 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
570 attrLn == "op")
572 op = parseOperation(reader.getAttributeValue(true));
573 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
574 attrLn == "finalized")
576 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
579 if (!hasName) {
580 throw css::uno::RuntimeException(
581 "no prop name attribute in " + reader.getUrl());
583 if (trackPath_) {
584 path_.push_back(name);
585 //TODO: This ignores locprop values for which specific include paths
586 // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
587 if (partial_ != nullptr &&
588 partial_->contains(path_) != Partial::CONTAINS_NODE)
590 state_.push(State::Ignore(true));
591 return;
594 NodeMap & members = group->getMembers();
595 NodeMap::iterator i(members.find(name));
596 if (i == members.end()) {
597 handleUnknownGroupProp(reader, group, name, type, op, finalized);
598 } else {
599 switch (i->second->kind()) {
600 case Node::KIND_PROPERTY:
601 handlePlainGroupProp(reader, group, i, name, type, op, finalized);
602 break;
603 case Node::KIND_LOCALIZED_PROPERTY:
604 handleLocalizedGroupProp(
605 reader,
606 static_cast< LocalizedPropertyNode * >(i->second.get()), name,
607 type, op, finalized);
608 break;
609 default:
610 throw css::uno::RuntimeException(
611 "inappropriate prop " + name + " in " + reader.getUrl());
616 void XcuParser::handleUnknownGroupProp(
617 xmlreader::XmlReader const & reader, GroupNode const * group,
618 OUString const & name, Type type, Operation operation, bool finalized)
620 switch (operation) {
621 case OPERATION_REPLACE:
622 case OPERATION_FUSE:
623 if (group->isExtensible()) {
624 if (type == TYPE_ERROR) {
625 throw css::uno::RuntimeException(
626 "missing type attribute for prop " + name + " in " +
627 reader.getUrl());
629 valueParser_.type_ = type;
630 rtl::Reference< Node > prop(
631 new PropertyNode(
632 valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
633 true));
634 if (finalized) {
635 prop->setFinalized(valueParser_.getLayer());
637 state_.push(State::Insert(prop, name));
638 recordModification(false);
639 break;
641 [[fallthrough]];
642 default:
643 SAL_WARN(
644 "configmgr",
645 "unknown property \"" << name << "\" in \"" << reader.getUrl()
646 << '"');
647 state_.push(State::Ignore(true));
648 break;
652 void XcuParser::handlePlainGroupProp(
653 xmlreader::XmlReader const & reader, GroupNode * group,
654 NodeMap::iterator const & propertyIndex, OUString const & name,
655 Type type, Operation operation, bool finalized)
657 PropertyNode * property = static_cast< PropertyNode * >(
658 propertyIndex->second.get());
659 if (property->getLayer() > valueParser_.getLayer()) {
660 state_.push(State::Ignore(true));
661 return;
663 int finalizedLayer = std::min(
664 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
665 property->getFinalized());
666 property->setFinalized(finalizedLayer);
667 if (finalizedLayer < valueParser_.getLayer()) {
668 state_.push(State::Ignore(true));
669 return;
671 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
672 type != property->getStaticType())
674 throw css::uno::RuntimeException(
675 "invalid type for prop " + name + " in " + reader.getUrl());
677 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
678 switch (operation) {
679 case OPERATION_MODIFY:
680 case OPERATION_REPLACE:
681 case OPERATION_FUSE:
682 state_.push(State::Modify(property));
683 recordModification(false);
684 break;
685 case OPERATION_REMOVE:
686 if (!property->isExtension()) {
687 throw css::uno::RuntimeException(
688 "invalid remove of non-extension prop " + name + " in " +
689 reader.getUrl());
691 group->getMembers().erase(propertyIndex);
692 state_.push(State::Ignore(true));
693 recordModification(false);
694 break;
698 void XcuParser::handleLocalizedGroupProp(
699 xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
700 OUString const & name, Type type, Operation operation, bool finalized)
702 if (property->getLayer() > valueParser_.getLayer()) {
703 state_.push(State::Ignore(true));
704 return;
706 int finalizedLayer = std::min(
707 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
708 property->getFinalized());
709 property->setFinalized(finalizedLayer);
710 if (finalizedLayer < valueParser_.getLayer()) {
711 state_.push(State::Ignore(true));
712 return;
714 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
715 type != property->getStaticType())
717 throw css::uno::RuntimeException(
718 "invalid type for prop " + name + " in " + reader.getUrl());
720 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
721 switch (operation) {
722 case OPERATION_MODIFY:
723 case OPERATION_FUSE:
724 state_.push(State::Modify(property));
725 break;
726 case OPERATION_REPLACE:
728 rtl::Reference< Node > replacement(
729 new LocalizedPropertyNode(
730 valueParser_.getLayer(), property->getStaticType(),
731 property->isNillable()));
732 replacement->setFinalized(property->getFinalized());
733 state_.push(State::Insert(replacement, name));
734 recordModification(false);
736 break;
737 case OPERATION_REMOVE:
738 throw css::uno::RuntimeException(
739 "invalid remove of non-extension prop " + name + " in " +
740 reader.getUrl());
744 void XcuParser::handleGroupNode(
745 xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
747 bool hasName = false;
748 OUString name;
749 Operation op = OPERATION_MODIFY;
750 bool finalized = false;
751 for (;;) {
752 int attrNsId;
753 xmlreader::Span attrLn;
754 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
755 break;
757 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
758 hasName = true;
759 name = reader.getAttributeValue(false).convertFromUtf8();
760 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
761 attrLn == "op")
763 op = parseOperation(reader.getAttributeValue(true));
764 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
765 attrLn == "finalized")
767 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
770 if (!hasName) {
771 throw css::uno::RuntimeException(
772 "no node name attribute in " + reader.getUrl());
774 if (trackPath_) {
775 path_.push_back(name);
776 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
778 state_.push(State::Ignore(true));
779 return;
782 rtl::Reference< Node > child(
783 group->getMembers().findNode(valueParser_.getLayer(), name));
784 if (!child.is()) {
785 SAL_WARN(
786 "configmgr",
787 "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
788 state_.push(State::Ignore(true));
789 return;
791 Node::Kind kind = child->kind();
792 if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
793 throw css::uno::RuntimeException(
794 "bad <node> \"" + name + "\" of non group/set kind in " +
795 reader.getUrl());
797 if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
798 throw css::uno::RuntimeException(
799 "invalid operation on group node in " + reader.getUrl());
801 int finalizedLayer = std::min(
802 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
803 child->getFinalized());
804 child->setFinalized(finalizedLayer);
805 if (finalizedLayer < valueParser_.getLayer()) {
806 state_.push(State::Ignore(true));
807 return;
809 state_.push(State::Modify(child));
812 void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
813 bool hasName = false;
814 OUString name;
815 OUString component(componentName_);
816 bool hasNodeType = false;
817 OUString nodeType;
818 Operation op = OPERATION_MODIFY;
819 bool finalized = false;
820 bool mandatory = false;
821 for (;;) {
822 int attrNsId;
823 xmlreader::Span attrLn;
824 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
825 break;
827 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
828 hasName = true;
829 name = reader.getAttributeValue(false).convertFromUtf8();
830 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
831 attrLn == "component")
833 component = reader.getAttributeValue(false).convertFromUtf8();
834 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
835 attrLn == "node-type")
837 hasNodeType = true;
838 nodeType = reader.getAttributeValue(false).convertFromUtf8();
839 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
840 attrLn == "op")
842 op = parseOperation(reader.getAttributeValue(true));
843 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
844 attrLn == "finalized")
846 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
847 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
848 attrLn == "mandatory")
850 mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
853 if (!hasName) {
854 throw css::uno::RuntimeException(
855 "no node name attribute in " + reader.getUrl());
857 if (trackPath_) {
858 path_.push_back(name);
859 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
861 state_.push(State::Ignore(true));
862 return;
865 OUString templateName(
866 xmldata::parseTemplateReference(
867 component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
868 if (!set->isValidTemplate(templateName)) {
869 throw css::uno::RuntimeException(
870 "set member node " + name + " references invalid template " +
871 templateName + " in " + reader.getUrl());
873 rtl::Reference< Node > tmpl(
874 data_.getTemplate(valueParser_.getLayer(), templateName));
875 if (!tmpl.is()) {
876 throw css::uno::RuntimeException(
877 "set member node " + name + " references undefined template " +
878 templateName + " in " + reader.getUrl());
880 int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
881 int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
882 NodeMap & members = set->getMembers();
883 NodeMap::iterator i(members.find(name));
884 if (i != members.end()) {
885 finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
886 i->second->setFinalized(finalizedLayer);
887 mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
888 i->second->setMandatory(mandatoryLayer);
889 if (i->second->getLayer() > valueParser_.getLayer()) {
890 state_.push(State::Ignore(true));
891 return;
894 if (finalizedLayer < valueParser_.getLayer()) {
895 state_.push(State::Ignore(true));
896 return;
898 switch (op) {
899 case OPERATION_MODIFY:
900 if (i == members.end()) {
901 SAL_WARN(
902 "configmgr",
903 "ignoring modify of unknown set member node \"" << name
904 << "\" in \"" << reader.getUrl() << '"');
905 state_.push(State::Ignore(true));
906 } else {
907 state_.push(State::Modify(i->second));
909 break;
910 case OPERATION_REPLACE:
912 rtl::Reference< Node > member(tmpl->clone(true));
913 member->setLayer(valueParser_.getLayer());
914 member->setFinalized(finalizedLayer);
915 member->setMandatory(mandatoryLayer);
916 state_.push(State::Insert(member, name));
917 recordModification(i == members.end());
919 break;
920 case OPERATION_FUSE:
921 if (i == members.end()) {
922 rtl::Reference< Node > member(tmpl->clone(true));
923 member->setLayer(valueParser_.getLayer());
924 member->setFinalized(finalizedLayer);
925 member->setMandatory(mandatoryLayer);
926 state_.push(State::Insert(member, name));
927 recordModification(true);
928 } else {
929 state_.push(State::Modify(i->second));
931 break;
932 case OPERATION_REMOVE:
934 // Ignore removal of unknown members and members made mandatory in
935 // this or a lower layer; forget about user-layer removals that no
936 // longer remove anything (so that paired additions/removals in the
937 // user layer do not grow registrymodifications.xcu unbounded):
938 bool known = i != members.end();
939 if (known &&
940 (mandatoryLayer == Data::NO_LAYER ||
941 mandatoryLayer > valueParser_.getLayer()))
943 members.erase(i);
945 state_.push(State::Ignore(true));
946 if (known) {
947 recordModification(false);
949 break;
954 void XcuParser::recordModification(bool addition) {
955 if (broadcastModifications_ != nullptr) {
956 broadcastModifications_->add(path_);
958 if (addition && additions_ != nullptr) {
959 additions_->push_back(path_);
961 if (recordModifications_) {
962 data_.modifications.add(path_);
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */