sw a11y: clang-format SidebarWinAccessible code
[LibreOffice.git] / configmgr / source / xcuparser.cxx
blob15e218f55fc05269a79e36ddbd9d62667e23abd3
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] = std::move(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 bool XcuParser::isAlreadyFinalized(int finalizedLayer) const {
223 return finalizedLayer != Data::NO_LAYER && finalizedLayer <= valueParser_.getLayer();
226 void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
227 OStringBuffer buf(256);
228 buf.append('.');
229 bool hasPackage = false;
230 bool hasName = false;
231 Operation op = OPERATION_MODIFY;
232 bool finalized = false;
233 for (;;) {
234 int attrNsId;
235 xmlreader::Span attrLn;
236 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
237 break;
239 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
241 if (hasPackage) {
242 throw css::uno::RuntimeException(
243 "multiple component-update package attributes in " +
244 reader.getUrl());
246 hasPackage = true;
247 xmlreader::Span s(reader.getAttributeValue(false));
248 buf.insert(0, s.begin, s.length);
249 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
250 attrLn == "name")
252 if (hasName) {
253 throw css::uno::RuntimeException(
254 "multiple component-update name attributes in " +
255 reader.getUrl());
257 hasName = true;
258 xmlreader::Span s(reader.getAttributeValue(false));
259 buf.append(s.begin, s.length);
260 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
261 attrLn == "op")
263 op = parseOperation(reader.getAttributeValue(true));
264 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
265 attrLn == "finalized")
267 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
270 if (!hasPackage) {
271 throw css::uno::RuntimeException(
272 "no component-data package attribute in " + reader.getUrl());
274 if (!hasName) {
275 throw css::uno::RuntimeException(
276 "no component-data name attribute in " + reader.getUrl());
278 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
279 convertFromUtf8();
280 if (trackPath_) {
281 assert(path_.empty());
282 path_.push_back(componentName_);
283 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
285 state_.push(State::Ignore(true));
286 return;
289 rtl::Reference< Node > node(
290 data_.getComponents().findNode(valueParser_.getLayer(),
291 componentName_));
292 if (!node.is()) {
293 SAL_WARN(
294 "configmgr",
295 "unknown component \"" << componentName_ << "\" in \""
296 << reader.getUrl() << '"');
297 state_.push(State::Ignore(true));
298 return;
300 switch (op) {
301 case OPERATION_MODIFY:
302 case OPERATION_FUSE:
303 break;
304 default:
305 throw css::uno::RuntimeException(
306 "invalid operation on root node in " + reader.getUrl());
308 if (isAlreadyFinalized(node->getFinalized())) {
309 state_.push(State::Ignore(true));
310 return;
312 if (finalized) {
313 node->setFinalized(valueParser_.getLayer());
315 state_.push(State::Modify(node));
318 void XcuParser::handleItem(xmlreader::XmlReader & reader) {
319 xmlreader::Span attrPath;
320 for (;;) {
321 int attrNsId;
322 xmlreader::Span attrLn;
323 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
324 break;
326 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "path") {
327 attrPath = reader.getAttributeValue(false);
330 if (!attrPath.is()) {
331 throw css::uno::RuntimeException(
332 "missing path attribute in " + reader.getUrl());
334 OUString path(attrPath.convertFromUtf8());
335 int finalizedLayer;
336 rtl::Reference< Node > node(
337 data_.resolvePathRepresentation(
338 path, nullptr, &path_, &finalizedLayer));
339 if (!node.is()) {
340 SAL_WARN(
341 "configmgr",
342 "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
343 state_.push(State::Ignore(true));
344 return;
346 assert(!path_.empty());
347 componentName_ = path_.front();
348 if (trackPath_) {
349 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
351 state_.push(State::Ignore(true));
352 return;
354 } else {
355 path_.clear();
357 switch (node->kind()) {
358 case Node::KIND_PROPERTY:
359 case Node::KIND_LOCALIZED_VALUE:
360 SAL_WARN(
361 "configmgr",
362 "item of bad type \"" << path << "\" in \"" << reader.getUrl()
363 << '"');
364 state_.push(State::Ignore(true));
365 return;
366 case Node::KIND_LOCALIZED_PROPERTY:
367 valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
368 node.get())->getStaticType();
369 break;
370 default:
371 break;
373 if (isAlreadyFinalized(finalizedLayer)) {
374 state_.push(State::Ignore(true));
375 return;
377 state_.push(State::Modify(node));
380 void XcuParser::handlePropValue(
381 xmlreader::XmlReader & reader, PropertyNode * prop)
383 bool nil = false;
384 OString separator;
385 OUString external;
386 for (;;) {
387 int attrNsId;
388 xmlreader::Span attrLn;
389 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
390 break;
392 if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn == "nil") {
393 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
394 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
395 attrLn == "type")
397 Type type = xmldata::parseType(
398 reader, reader.getAttributeValue(true));
399 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
400 throw css::uno::RuntimeException(
401 "invalid value type in " + reader.getUrl());
403 valueParser_.type_ = type;
404 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
405 attrLn == "separator")
407 xmlreader::Span s(reader.getAttributeValue(false));
408 if (s.length == 0) {
409 throw css::uno::RuntimeException(
410 "bad oor:separator attribute in " + reader.getUrl());
412 separator = OString(s.begin, s.length);
413 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
414 attrLn == "external")
416 external = reader.getAttributeValue(true).convertFromUtf8();
417 if (external.isEmpty()) {
418 throw css::uno::RuntimeException(
419 "bad oor:external attribute value in " + reader.getUrl());
423 if (nil) {
424 if (!prop->isNillable()) {
425 throw css::uno::RuntimeException(
426 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
428 if (!external.isEmpty()) {
429 throw css::uno::RuntimeException(
430 "xsi:nil and oor:external attributes for prop in " +
431 reader.getUrl());
433 prop->setValue(valueParser_.getLayer(), css::uno::Any(), valueParser_.getLayer() == Data::NO_LAYER);
434 state_.push(State::Ignore(false));
435 } else if (external.isEmpty()) {
436 valueParser_.separator_ = separator;
437 valueParser_.start(prop);
438 } else {
439 prop->setExternal(valueParser_.getLayer(), external);
440 state_.push(State::Ignore(false));
444 void XcuParser::handleLocpropValue(
445 xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
447 OUString name;
448 bool nil = false;
449 OString separator;
450 Operation op = OPERATION_FUSE;
451 for (;;) {
452 int attrNsId;
453 xmlreader::Span attrLn;
454 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
455 break;
457 if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
458 attrLn == "lang")
460 name = reader.getAttributeValue(false).convertFromUtf8();
461 } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
462 attrLn == "nil")
464 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
465 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
466 attrLn == "type")
468 Type type = xmldata::parseType(
469 reader, reader.getAttributeValue(true));
470 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
471 throw css::uno::RuntimeException(
472 "invalid value type in " + reader.getUrl());
474 valueParser_.type_ = type;
475 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
476 attrLn == "separator")
478 xmlreader::Span s(reader.getAttributeValue(false));
479 if (s.length == 0) {
480 throw css::uno::RuntimeException(
481 "bad oor:separator attribute in " + reader.getUrl());
483 separator = OString(s.begin, s.length);
484 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
485 attrLn == "op")
487 op = parseOperation(reader.getAttributeValue(true));
490 if (trackPath_) {
491 path_.push_back(name);
492 if (partial_ != nullptr &&
493 partial_->contains(path_) != Partial::CONTAINS_NODE)
495 state_.push(State::Ignore(true));
496 return;
499 NodeMap & members = locprop->getMembers();
500 NodeMap::iterator i(members.find(name));
501 if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
502 state_.push(State::Ignore(true));
503 return;
505 if (nil && !locprop->isNillable()) {
506 throw css::uno::RuntimeException(
507 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
509 switch (op) {
510 case OPERATION_FUSE:
512 bool pop = false;
513 if (nil) {
514 if (i == members.end()) {
515 members[name] = new LocalizedValueNode(
516 valueParser_.getLayer(), css::uno::Any());
517 } else {
518 static_cast< LocalizedValueNode * >(
519 i->second.get())->setValue(
520 valueParser_.getLayer(), css::uno::Any(), valueParser_.getLayer() == Data::NO_LAYER);
522 state_.push(State::Ignore(true));
523 } else {
524 valueParser_.separator_ = separator;
525 valueParser_.start(locprop, name);
526 pop = true;
528 if (trackPath_) {
529 recordModification(false);
530 if (pop) {
531 path_.pop_back();
535 break;
536 case OPERATION_REMOVE:
537 //TODO: only allow if parent.op == OPERATION_FUSE
538 //TODO: disallow removing when e.g. lang=""?
539 if (i != members.end()) {
540 members.erase(i);
542 state_.push(State::Ignore(true));
543 recordModification(false);
544 break;
545 default:
546 throw css::uno::RuntimeException(
547 "bad op attribute for value element in " + reader.getUrl());
551 void XcuParser::handleGroupProp(
552 xmlreader::XmlReader & reader, GroupNode * group)
554 bool hasName = false;
555 OUString name;
556 Type type = TYPE_ERROR;
557 Operation op = OPERATION_MODIFY;
558 bool finalized = false;
559 for (;;) {
560 int attrNsId;
561 xmlreader::Span attrLn;
562 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
563 break;
565 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
566 hasName = true;
567 name = reader.getAttributeValue(false).convertFromUtf8();
568 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
569 attrLn == "type")
571 type = xmldata::parseType(reader, reader.getAttributeValue(true));
572 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
573 attrLn == "op")
575 op = parseOperation(reader.getAttributeValue(true));
576 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
577 attrLn == "finalized")
579 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
582 if (!hasName) {
583 throw css::uno::RuntimeException(
584 "no prop name attribute in " + reader.getUrl());
586 if (trackPath_) {
587 path_.push_back(name);
588 //TODO: This ignores locprop values for which specific include paths
589 // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
590 if (partial_ != nullptr &&
591 partial_->contains(path_) != Partial::CONTAINS_NODE)
593 state_.push(State::Ignore(true));
594 return;
597 NodeMap & members = group->getMembers();
598 NodeMap::iterator i(members.find(name));
599 if (i == members.end()) {
600 handleUnknownGroupProp(reader, group, name, type, op, finalized);
601 } else {
602 switch (i->second->kind()) {
603 case Node::KIND_PROPERTY:
604 handlePlainGroupProp(reader, group, i, name, type, op, finalized);
605 break;
606 case Node::KIND_LOCALIZED_PROPERTY:
607 handleLocalizedGroupProp(
608 reader,
609 static_cast< LocalizedPropertyNode * >(i->second.get()), name,
610 type, op, finalized);
611 break;
612 default:
613 throw css::uno::RuntimeException(
614 "inappropriate prop " + name + " in " + reader.getUrl());
619 void XcuParser::handleUnknownGroupProp(
620 xmlreader::XmlReader const & reader, GroupNode const * group,
621 OUString const & name, Type type, Operation operation, bool finalized)
623 switch (operation) {
624 case OPERATION_REPLACE:
625 case OPERATION_FUSE:
626 if (group->isExtensible()) {
627 if (type == TYPE_ERROR) {
628 throw css::uno::RuntimeException(
629 "missing type attribute for prop " + name + " in " +
630 reader.getUrl());
632 valueParser_.type_ = type;
633 rtl::Reference< Node > prop(
634 new PropertyNode(
635 valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
636 true));
637 if (finalized) {
638 prop->setFinalized(valueParser_.getLayer());
640 state_.push(State::Insert(prop, name));
641 recordModification(false);
642 break;
644 [[fallthrough]];
645 default:
646 SAL_WARN(
647 "configmgr",
648 "unknown property \"" << name << "\" in \"" << reader.getUrl()
649 << '"');
650 state_.push(State::Ignore(true));
651 break;
655 void XcuParser::handlePlainGroupProp(
656 xmlreader::XmlReader const & reader, GroupNode * group,
657 NodeMap::iterator const & propertyIndex, std::u16string_view name,
658 Type type, Operation operation, bool finalized)
660 PropertyNode * property = static_cast< PropertyNode * >(
661 propertyIndex->second.get());
662 if (property->getLayer() > valueParser_.getLayer()) {
663 state_.push(State::Ignore(true));
664 return;
666 if (isAlreadyFinalized(property->getFinalized())) {
667 state_.push(State::Ignore(true));
668 return;
670 if (finalized) {
671 property->setFinalized(valueParser_.getLayer());
673 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
674 type != property->getStaticType())
676 throw css::uno::RuntimeException(
677 OUString::Concat("invalid type for prop ") + name + " in " + reader.getUrl());
679 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
680 switch (operation) {
681 case OPERATION_MODIFY:
682 case OPERATION_REPLACE:
683 case OPERATION_FUSE:
684 state_.push(State::Modify(property));
685 recordModification(false);
686 break;
687 case OPERATION_REMOVE:
688 if (!property->isExtension()) {
689 throw css::uno::RuntimeException(
690 OUString::Concat("invalid remove of non-extension prop ") + name + " in " +
691 reader.getUrl());
693 group->getMembers().erase(propertyIndex);
694 state_.push(State::Ignore(true));
695 recordModification(false);
696 break;
700 void XcuParser::handleLocalizedGroupProp(
701 xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
702 OUString const & name, Type type, Operation operation, bool finalized)
704 if (property->getLayer() > valueParser_.getLayer()) {
705 state_.push(State::Ignore(true));
706 return;
708 if (isAlreadyFinalized(property->getFinalized())) {
709 state_.push(State::Ignore(true));
710 return;
712 if (finalized) {
713 property->setFinalized(valueParser_.getLayer());
715 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
716 type != property->getStaticType())
718 throw css::uno::RuntimeException(
719 "invalid type for prop " + name + " in " + reader.getUrl());
721 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
722 switch (operation) {
723 case OPERATION_MODIFY:
724 case OPERATION_FUSE:
725 state_.push(State::Modify(property));
726 break;
727 case OPERATION_REPLACE:
729 rtl::Reference< Node > replacement(
730 new LocalizedPropertyNode(
731 valueParser_.getLayer(), property->getStaticType(),
732 property->isNillable()));
733 replacement->setFinalized(property->getFinalized());
734 state_.push(State::Insert(replacement, name));
735 recordModification(false);
737 break;
738 case OPERATION_REMOVE:
739 throw css::uno::RuntimeException(
740 "invalid remove of non-extension prop " + name + " in " +
741 reader.getUrl());
745 void XcuParser::handleGroupNode(
746 xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
748 bool hasName = false;
749 OUString name;
750 Operation op = OPERATION_MODIFY;
751 bool finalized = false;
752 for (;;) {
753 int attrNsId;
754 xmlreader::Span attrLn;
755 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
756 break;
758 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
759 hasName = true;
760 name = reader.getAttributeValue(false).convertFromUtf8();
761 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
762 attrLn == "op")
764 op = parseOperation(reader.getAttributeValue(true));
765 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
766 attrLn == "finalized")
768 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
771 if (!hasName) {
772 throw css::uno::RuntimeException(
773 "no node name attribute in " + reader.getUrl());
775 if (trackPath_) {
776 path_.push_back(name);
777 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
779 state_.push(State::Ignore(true));
780 return;
783 rtl::Reference< Node > child(
784 group->getMembers().findNode(valueParser_.getLayer(), name));
785 if (!child.is()) {
786 SAL_WARN(
787 "configmgr",
788 "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
789 state_.push(State::Ignore(true));
790 return;
792 Node::Kind kind = child->kind();
793 if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
794 throw css::uno::RuntimeException(
795 "bad <node> \"" + name + "\" of non group/set kind in " +
796 reader.getUrl());
798 if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
799 throw css::uno::RuntimeException(
800 "invalid operation on group node in " + reader.getUrl());
802 if (isAlreadyFinalized(child->getFinalized())) {
803 state_.push(State::Ignore(true));
804 return;
806 if (finalized) {
807 child->setFinalized(valueParser_.getLayer());
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 bool alreadyFinalized = false;
881 int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
882 int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
883 NodeMap & members = set->getMembers();
884 NodeMap::iterator i(members.find(name));
885 if (i != members.end()) {
886 auto const fin = i->second->getFinalized();
887 alreadyFinalized = isAlreadyFinalized(fin);
888 finalizedLayer = std::min(finalizedLayer, fin);
889 i->second->setFinalized(finalizedLayer);
890 mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
891 i->second->setMandatory(mandatoryLayer);
892 if (i->second->getLayer() > valueParser_.getLayer()) {
893 state_.push(State::Ignore(true));
894 return;
897 if (alreadyFinalized) {
898 state_.push(State::Ignore(true));
899 return;
901 switch (op) {
902 case OPERATION_MODIFY:
903 if (i == members.end()) {
904 SAL_WARN(
905 "configmgr",
906 "ignoring modify of unknown set member node \"" << name
907 << "\" in \"" << reader.getUrl() << '"');
908 state_.push(State::Ignore(true));
909 } else {
910 state_.push(State::Modify(i->second));
912 break;
913 case OPERATION_REPLACE:
915 rtl::Reference< Node > member(tmpl->clone(true));
916 member->setLayer(valueParser_.getLayer());
917 member->setFinalized(finalizedLayer);
918 member->setMandatory(mandatoryLayer);
919 state_.push(State::Insert(member, name));
920 recordModification(i == members.end());
922 break;
923 case OPERATION_FUSE:
924 if (i == members.end()) {
925 rtl::Reference< Node > member(tmpl->clone(true));
926 member->setLayer(valueParser_.getLayer());
927 member->setFinalized(finalizedLayer);
928 member->setMandatory(mandatoryLayer);
929 state_.push(State::Insert(member, name));
930 recordModification(true);
931 } else {
932 state_.push(State::Modify(i->second));
934 break;
935 case OPERATION_REMOVE:
937 // Ignore removal of unknown members and members made mandatory in
938 // this or a lower layer; forget about user-layer removals that no
939 // longer remove anything (so that paired additions/removals in the
940 // user layer do not grow registrymodifications.xcu unbounded):
941 bool known = i != members.end();
942 if (known &&
943 (mandatoryLayer == Data::NO_LAYER ||
944 mandatoryLayer > valueParser_.getLayer()))
946 members.erase(i);
948 state_.push(State::Ignore(true));
949 if (known) {
950 recordModification(false);
952 break;
957 void XcuParser::recordModification(bool addition) {
958 if (broadcastModifications_ != nullptr) {
959 broadcastModifications_->add(path_);
961 if (addition && additions_ != nullptr) {
962 additions_->push_back(path_);
964 if (recordModifications_) {
965 data_.modifications.add(path_);
971 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */