lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / configmgr / source / xcuparser.cxx
blob7db53e3e77fb994a5947c97dec6f5817257d2e38
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/Reference.hxx>
28 #include <com/sun/star/uno/RuntimeException.hpp>
29 #include <com/sun/star/uno/XInterface.hpp>
30 #include <rtl/ref.hxx>
31 #include <rtl/strbuf.hxx>
32 #include <rtl/string.h>
33 #include <rtl/string.hxx>
34 #include <rtl/ustring.h>
35 #include <rtl/ustring.hxx>
36 #include <sal/log.hxx>
37 #include <xmlreader/span.hxx>
38 #include <xmlreader/xmlreader.hxx>
40 #include "data.hxx"
41 #include "localizedpropertynode.hxx"
42 #include "localizedvaluenode.hxx"
43 #include "groupnode.hxx"
44 #include "modifications.hxx"
45 #include "node.hxx"
46 #include "nodemap.hxx"
47 #include "parsemanager.hxx"
48 #include "partial.hxx"
49 #include "propertynode.hxx"
50 #include "setnode.hxx"
51 #include "xcuparser.hxx"
52 #include "xmldata.hxx"
54 namespace configmgr {
56 XcuParser::XcuParser(
57 int layer, Data & data, Partial const * partial,
58 Modifications * broadcastModifications, Additions * additions):
59 valueParser_(layer), data_(data),
60 partial_(partial), broadcastModifications_(broadcastModifications),
61 additions_(additions), recordModifications_(layer == Data::NO_LAYER),
62 trackPath_(
63 partial_ != nullptr || broadcastModifications_ != nullptr || additions_ != nullptr ||
64 recordModifications_)
67 XcuParser::~XcuParser() {}
69 xmlreader::XmlReader::Text XcuParser::getTextMode() {
70 return valueParser_.getTextMode();
73 bool XcuParser::startElement(
74 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
75 std::set< OUString > const * /*existingDependencies*/)
77 if (valueParser_.startElement(reader, nsId, name)) {
78 return true;
80 if (state_.empty()) {
81 if (nsId == ParseManager::NAMESPACE_OOR &&
82 name.equals("component-data"))
84 handleComponentData(reader);
85 } else if (nsId == ParseManager::NAMESPACE_OOR && name.equals("items"))
87 state_.push(State::Modify(rtl::Reference< Node >()));
88 } else {
89 throw css::uno::RuntimeException(
90 "bad root element <" + name.convertFromUtf8() + "> in " +
91 reader.getUrl());
93 } else if (state_.top().ignore) {
94 state_.push(State::Ignore(false));
95 } else if (!state_.top().node.is()) {
96 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE || !name.equals("item"))
98 throw css::uno::RuntimeException(
99 "bad items node member <" + name.convertFromUtf8() + "> in " +
100 reader.getUrl());
102 handleItem(reader);
103 } else {
104 switch (state_.top().node->kind()) {
105 case Node::KIND_PROPERTY:
106 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
107 !name.equals("value"))
109 throw css::uno::RuntimeException(
110 "bad property node member <" + name.convertFromUtf8() +
111 "> in " + reader.getUrl());
113 handlePropValue(
114 reader,
115 static_cast< PropertyNode * >(state_.top().node.get()));
116 break;
117 case Node::KIND_LOCALIZED_PROPERTY:
118 if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
119 !name.equals("value"))
121 throw css::uno::RuntimeException(
122 "bad localized property node member <" +
123 name.convertFromUtf8() + "> in " + reader.getUrl());
125 handleLocpropValue(
126 reader,
127 static_cast< LocalizedPropertyNode * >(
128 state_.top().node.get()));
129 break;
130 case Node::KIND_LOCALIZED_VALUE:
131 throw css::uno::RuntimeException(
132 "bad member <" + name.convertFromUtf8() + "> in " +
133 reader.getUrl());
134 case Node::KIND_GROUP:
135 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
136 name.equals("prop"))
138 handleGroupProp(
139 reader,
140 static_cast< GroupNode * >(state_.top().node.get()));
141 } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
142 name.equals("node"))
144 handleGroupNode(reader, state_.top().node);
145 } else {
146 throw css::uno::RuntimeException(
147 "bad group node member <" + name.convertFromUtf8() +
148 "> in " + reader.getUrl());
150 break;
151 case Node::KIND_SET:
152 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
153 name.equals("node"))
155 handleSetNode(
156 reader, static_cast< SetNode * >(state_.top().node.get()));
157 } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
158 name.equals("prop"))
160 SAL_WARN(
161 "configmgr",
162 "bad set node <prop> member in \"" << reader.getUrl()
163 << '"');
164 state_.push(State::Ignore(true));
165 } else {
166 throw css::uno::RuntimeException(
167 "bad set node member <" + name.convertFromUtf8() +
168 "> in " + reader.getUrl());
170 break;
171 case Node::KIND_ROOT:
172 assert(false); // this cannot happen
173 break;
176 return true;
179 void XcuParser::endElement(xmlreader::XmlReader const &) {
180 if (valueParser_.endElement()) {
181 return;
183 assert(!state_.empty());
184 bool pop = state_.top().pop;
185 rtl::Reference< Node > insert;
186 OUString name;
187 if (state_.top().insert) {
188 insert = state_.top().node;
189 assert(insert.is());
190 name = state_.top().name;
192 state_.pop();
193 if (insert.is()) {
194 assert(!state_.empty() && state_.top().node.is());
195 state_.top().node->getMembers()[name] = insert;
197 if (pop && !path_.empty()) {
198 path_.pop_back();
199 // </item> will pop less than <item> pushed, but that is harmless,
200 // as the next <item> will reset path_
204 void XcuParser::characters(xmlreader::Span const & text) {
205 valueParser_.characters(text);
208 XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
209 assert(text.is());
210 if (text.equals("modify")) {
211 return OPERATION_MODIFY;
213 if (text.equals("replace")) {
214 return OPERATION_REPLACE;
216 if (text.equals("fuse")) {
217 return OPERATION_FUSE;
219 if (text.equals("remove")) {
220 return OPERATION_REMOVE;
222 throw css::uno::RuntimeException(
223 "invalid op " + text.convertFromUtf8());
226 void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
227 OStringBuffer buf;
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.equals("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.equals("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.equals("op"))
263 op = parseOperation(reader.getAttributeValue(true));
264 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
265 attrLn.equals("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 int finalizedLayer = std::min(
309 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
310 node->getFinalized());
311 node->setFinalized(finalizedLayer);
312 if (finalizedLayer < valueParser_.getLayer()) {
313 state_.push(State::Ignore(true));
314 return;
316 state_.push(State::Modify(node));
319 void XcuParser::handleItem(xmlreader::XmlReader & reader) {
320 xmlreader::Span attrPath;
321 for (;;) {
322 int attrNsId;
323 xmlreader::Span attrLn;
324 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
325 break;
327 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("path")) {
328 attrPath = reader.getAttributeValue(false);
331 if (!attrPath.is()) {
332 throw css::uno::RuntimeException(
333 "missing path attribute in " + reader.getUrl());
335 OUString path(attrPath.convertFromUtf8());
336 int finalizedLayer;
337 rtl::Reference< Node > node(
338 data_.resolvePathRepresentation(
339 path, nullptr, &path_, &finalizedLayer));
340 if (!node.is()) {
341 SAL_WARN(
342 "configmgr",
343 "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
344 state_.push(State::Ignore(true));
345 return;
347 assert(!path_.empty());
348 componentName_ = path_.front();
349 if (trackPath_) {
350 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
352 state_.push(State::Ignore(true));
353 return;
355 } else {
356 path_.clear();
358 switch (node->kind()) {
359 case Node::KIND_PROPERTY:
360 case Node::KIND_LOCALIZED_VALUE:
361 SAL_WARN(
362 "configmgr",
363 "item of bad type \"" << path << "\" in \"" << reader.getUrl()
364 << '"');
365 state_.push(State::Ignore(true));
366 return;
367 case Node::KIND_LOCALIZED_PROPERTY:
368 valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
369 node.get())->getStaticType();
370 break;
371 default:
372 break;
374 if (finalizedLayer < valueParser_.getLayer()) {
375 state_.push(State::Ignore(true));
376 return;
378 state_.push(State::Modify(node));
381 void XcuParser::handlePropValue(
382 xmlreader::XmlReader & reader, PropertyNode * prop)
384 bool nil = false;
385 OString separator;
386 OUString external;
387 for (;;) {
388 int attrNsId;
389 xmlreader::Span attrLn;
390 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
391 break;
393 if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn.equals("nil")) {
394 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
395 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
396 attrLn.equals("type"))
398 Type type = xmldata::parseType(
399 reader, reader.getAttributeValue(true));
400 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
401 throw css::uno::RuntimeException(
402 "invalid value type in " + reader.getUrl());
404 valueParser_.type_ = type;
405 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
406 attrLn.equals("separator"))
408 xmlreader::Span s(reader.getAttributeValue(false));
409 if (s.length == 0) {
410 throw css::uno::RuntimeException(
411 "bad oor:separator attribute in " + reader.getUrl());
413 separator = OString(s.begin, s.length);
414 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
415 attrLn.equals("external"))
417 external = reader.getAttributeValue(true).convertFromUtf8();
418 if (external.isEmpty()) {
419 throw css::uno::RuntimeException(
420 "bad oor:external attribute value in " + reader.getUrl());
424 if (nil) {
425 if (!prop->isNillable()) {
426 throw css::uno::RuntimeException(
427 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
429 if (!external.isEmpty()) {
430 throw css::uno::RuntimeException(
431 "xsi:nil and oor:external attributes for prop in " +
432 reader.getUrl());
434 prop->setValue(valueParser_.getLayer(), css::uno::Any());
435 state_.push(State::Ignore(false));
436 } else if (external.isEmpty()) {
437 valueParser_.separator_ = separator;
438 valueParser_.start(prop);
439 } else {
440 prop->setExternal(valueParser_.getLayer(), external);
441 state_.push(State::Ignore(false));
445 void XcuParser::handleLocpropValue(
446 xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
448 OUString name;
449 bool nil = false;
450 OString separator;
451 Operation op = OPERATION_FUSE;
452 for (;;) {
453 int attrNsId;
454 xmlreader::Span attrLn;
455 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
456 break;
458 if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
459 attrLn.equals("lang"))
461 name = reader.getAttributeValue(false).convertFromUtf8();
462 } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
463 attrLn.equals("nil"))
465 nil = xmldata::parseBoolean(reader.getAttributeValue(true));
466 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
467 attrLn.equals("type"))
469 Type type = xmldata::parseType(
470 reader, reader.getAttributeValue(true));
471 if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
472 throw css::uno::RuntimeException(
473 "invalid value type in " + reader.getUrl());
475 valueParser_.type_ = type;
476 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
477 attrLn.equals("separator"))
479 xmlreader::Span s(reader.getAttributeValue(false));
480 if (s.length == 0) {
481 throw css::uno::RuntimeException(
482 "bad oor:separator attribute in " + reader.getUrl());
484 separator = OString(s.begin, s.length);
485 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
486 attrLn.equals("op"))
488 op = parseOperation(reader.getAttributeValue(true));
491 if (trackPath_) {
492 path_.push_back(name);
493 if (partial_ != nullptr &&
494 partial_->contains(path_) != Partial::CONTAINS_NODE)
496 state_.push(State::Ignore(true));
497 return;
500 NodeMap & members = locprop->getMembers();
501 NodeMap::iterator i(members.find(name));
502 if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
503 state_.push(State::Ignore(true));
504 return;
506 if (nil && !locprop->isNillable()) {
507 throw css::uno::RuntimeException(
508 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
510 switch (op) {
511 case OPERATION_FUSE:
513 bool pop = false;
514 if (nil) {
515 if (i == members.end()) {
516 members[name] = new LocalizedValueNode(
517 valueParser_.getLayer(), css::uno::Any());
518 } else {
519 static_cast< LocalizedValueNode * >(
520 i->second.get())->setValue(
521 valueParser_.getLayer(), css::uno::Any());
523 state_.push(State::Ignore(true));
524 } else {
525 valueParser_.separator_ = separator;
526 valueParser_.start(locprop, name);
527 pop = true;
529 if (trackPath_) {
530 recordModification(false);
531 if (pop) {
532 path_.pop_back();
536 break;
537 case OPERATION_REMOVE:
538 //TODO: only allow if parent.op == OPERATION_FUSE
539 //TODO: disallow removing when e.g. lang=""?
540 if (i != members.end()) {
541 members.erase(i);
543 state_.push(State::Ignore(true));
544 recordModification(false);
545 break;
546 default:
547 throw css::uno::RuntimeException(
548 "bad op attribute for value element in " + reader.getUrl());
552 void XcuParser::handleGroupProp(
553 xmlreader::XmlReader & reader, GroupNode * group)
555 bool hasName = false;
556 OUString name;
557 Type type = TYPE_ERROR;
558 Operation op = OPERATION_MODIFY;
559 bool finalized = false;
560 for (;;) {
561 int attrNsId;
562 xmlreader::Span attrLn;
563 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
564 break;
566 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
567 hasName = true;
568 name = reader.getAttributeValue(false).convertFromUtf8();
569 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
570 attrLn.equals("type"))
572 type = xmldata::parseType(reader, reader.getAttributeValue(true));
573 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
574 attrLn.equals("op"))
576 op = parseOperation(reader.getAttributeValue(true));
577 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
578 attrLn.equals("finalized"))
580 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
583 if (!hasName) {
584 throw css::uno::RuntimeException(
585 "no prop name attribute in " + reader.getUrl());
587 if (trackPath_) {
588 path_.push_back(name);
589 //TODO: This ignores locprop values for which specific include paths
590 // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
591 if (partial_ != nullptr &&
592 partial_->contains(path_) != Partial::CONTAINS_NODE)
594 state_.push(State::Ignore(true));
595 return;
598 NodeMap & members = group->getMembers();
599 NodeMap::iterator i(members.find(name));
600 if (i == members.end()) {
601 handleUnknownGroupProp(reader, group, name, type, op, finalized);
602 } else {
603 switch (i->second->kind()) {
604 case Node::KIND_PROPERTY:
605 handlePlainGroupProp(reader, group, i, name, type, op, finalized);
606 break;
607 case Node::KIND_LOCALIZED_PROPERTY:
608 handleLocalizedGroupProp(
609 reader,
610 static_cast< LocalizedPropertyNode * >(i->second.get()), name,
611 type, op, finalized);
612 break;
613 default:
614 throw css::uno::RuntimeException(
615 "inappropriate prop " + name + " in " + reader.getUrl());
620 void XcuParser::handleUnknownGroupProp(
621 xmlreader::XmlReader const & reader, GroupNode const * group,
622 OUString const & name, Type type, Operation operation, bool finalized)
624 switch (operation) {
625 case OPERATION_REPLACE:
626 case OPERATION_FUSE:
627 if (group->isExtensible()) {
628 if (type == TYPE_ERROR) {
629 throw css::uno::RuntimeException(
630 "missing type attribute for prop " + name + " in " +
631 reader.getUrl());
633 valueParser_.type_ = type;
634 rtl::Reference< Node > prop(
635 new PropertyNode(
636 valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
637 true));
638 if (finalized) {
639 prop->setFinalized(valueParser_.getLayer());
641 state_.push(State::Insert(prop, name));
642 recordModification(false);
643 break;
645 SAL_FALLTHROUGH;
646 default:
647 SAL_WARN(
648 "configmgr",
649 "unknown property \"" << name << "\" in \"" << reader.getUrl()
650 << '"');
651 state_.push(State::Ignore(true));
652 break;
656 void XcuParser::handlePlainGroupProp(
657 xmlreader::XmlReader const & reader, GroupNode * group,
658 NodeMap::iterator const & propertyIndex, OUString const & name,
659 Type type, Operation operation, bool finalized)
661 PropertyNode * property = static_cast< PropertyNode * >(
662 propertyIndex->second.get());
663 if (property->getLayer() > valueParser_.getLayer()) {
664 state_.push(State::Ignore(true));
665 return;
667 int finalizedLayer = std::min(
668 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
669 property->getFinalized());
670 property->setFinalized(finalizedLayer);
671 if (finalizedLayer < valueParser_.getLayer()) {
672 state_.push(State::Ignore(true));
673 return;
675 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
676 type != property->getStaticType())
678 throw css::uno::RuntimeException(
679 "invalid type for prop " + name + " in " + reader.getUrl());
681 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
682 switch (operation) {
683 case OPERATION_MODIFY:
684 case OPERATION_REPLACE:
685 case OPERATION_FUSE:
686 state_.push(State::Modify(property));
687 recordModification(false);
688 break;
689 case OPERATION_REMOVE:
690 if (!property->isExtension()) {
691 throw css::uno::RuntimeException(
692 "invalid remove of non-extension prop " + name + " in " +
693 reader.getUrl());
695 group->getMembers().erase(propertyIndex);
696 state_.push(State::Ignore(true));
697 recordModification(false);
698 break;
702 void XcuParser::handleLocalizedGroupProp(
703 xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
704 OUString const & name, Type type, Operation operation, bool finalized)
706 if (property->getLayer() > valueParser_.getLayer()) {
707 state_.push(State::Ignore(true));
708 return;
710 int finalizedLayer = std::min(
711 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
712 property->getFinalized());
713 property->setFinalized(finalizedLayer);
714 if (finalizedLayer < valueParser_.getLayer()) {
715 state_.push(State::Ignore(true));
716 return;
718 if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
719 type != property->getStaticType())
721 throw css::uno::RuntimeException(
722 "invalid type for prop " + name + " in " + reader.getUrl());
724 valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
725 switch (operation) {
726 case OPERATION_MODIFY:
727 case OPERATION_FUSE:
728 state_.push(State::Modify(property));
729 break;
730 case OPERATION_REPLACE:
732 rtl::Reference< Node > replacement(
733 new LocalizedPropertyNode(
734 valueParser_.getLayer(), property->getStaticType(),
735 property->isNillable()));
736 replacement->setFinalized(property->getFinalized());
737 state_.push(State::Insert(replacement, name));
738 recordModification(false);
740 break;
741 case OPERATION_REMOVE:
742 throw css::uno::RuntimeException(
743 "invalid remove of non-extension prop " + name + " in " +
744 reader.getUrl());
748 void XcuParser::handleGroupNode(
749 xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
751 bool hasName = false;
752 OUString name;
753 Operation op = OPERATION_MODIFY;
754 bool finalized = false;
755 for (;;) {
756 int attrNsId;
757 xmlreader::Span attrLn;
758 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
759 break;
761 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
762 hasName = true;
763 name = reader.getAttributeValue(false).convertFromUtf8();
764 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
765 attrLn.equals("op"))
767 op = parseOperation(reader.getAttributeValue(true));
768 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
769 attrLn.equals("finalized"))
771 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
774 if (!hasName) {
775 throw css::uno::RuntimeException(
776 "no node name attribute in " + reader.getUrl());
778 if (trackPath_) {
779 path_.push_back(name);
780 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
782 state_.push(State::Ignore(true));
783 return;
786 rtl::Reference< Node > child(
787 group->getMembers().findNode(valueParser_.getLayer(), name));
788 if (!child.is()) {
789 SAL_WARN(
790 "configmgr",
791 "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
792 state_.push(State::Ignore(true));
793 return;
795 Node::Kind kind = child->kind();
796 if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
797 throw css::uno::RuntimeException(
798 "bad <node> \"" + name + "\" of non group/set kind in " +
799 reader.getUrl());
801 if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
802 throw css::uno::RuntimeException(
803 "invalid operation on group node in " + reader.getUrl());
805 int finalizedLayer = std::min(
806 finalized ? valueParser_.getLayer() : Data::NO_LAYER,
807 child->getFinalized());
808 child->setFinalized(finalizedLayer);
809 if (finalizedLayer < valueParser_.getLayer()) {
810 state_.push(State::Ignore(true));
811 return;
813 state_.push(State::Modify(child));
816 void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
817 bool hasName = false;
818 OUString name;
819 OUString component(componentName_);
820 bool hasNodeType = false;
821 OUString nodeType;
822 Operation op = OPERATION_MODIFY;
823 bool finalized = false;
824 bool mandatory = false;
825 for (;;) {
826 int attrNsId;
827 xmlreader::Span attrLn;
828 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
829 break;
831 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
832 hasName = true;
833 name = reader.getAttributeValue(false).convertFromUtf8();
834 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
835 attrLn.equals("component"))
837 component = reader.getAttributeValue(false).convertFromUtf8();
838 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
839 attrLn.equals("node-type"))
841 hasNodeType = true;
842 nodeType = reader.getAttributeValue(false).convertFromUtf8();
843 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
844 attrLn.equals("op"))
846 op = parseOperation(reader.getAttributeValue(true));
847 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
848 attrLn.equals("finalized"))
850 finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
851 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
852 attrLn.equals("mandatory"))
854 mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
857 if (!hasName) {
858 throw css::uno::RuntimeException(
859 "no node name attribute in " + reader.getUrl());
861 if (trackPath_) {
862 path_.push_back(name);
863 if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
865 state_.push(State::Ignore(true));
866 return;
869 OUString templateName(
870 xmldata::parseTemplateReference(
871 component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
872 if (!set->isValidTemplate(templateName)) {
873 throw css::uno::RuntimeException(
874 "set member node " + name + " references invalid template " +
875 templateName + " in " + reader.getUrl());
877 rtl::Reference< Node > tmpl(
878 data_.getTemplate(valueParser_.getLayer(), templateName));
879 if (!tmpl.is()) {
880 throw css::uno::RuntimeException(
881 "set member node " + name + " references undefined template " +
882 templateName + " in " + reader.getUrl());
884 int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
885 int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
886 NodeMap & members = set->getMembers();
887 NodeMap::iterator i(members.find(name));
888 if (i != members.end()) {
889 finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
890 i->second->setFinalized(finalizedLayer);
891 mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
892 i->second->setMandatory(mandatoryLayer);
893 if (i->second->getLayer() > valueParser_.getLayer()) {
894 state_.push(State::Ignore(true));
895 return;
898 if (finalizedLayer < valueParser_.getLayer()) {
899 state_.push(State::Ignore(true));
900 return;
902 switch (op) {
903 case OPERATION_MODIFY:
904 if (i == members.end()) {
905 SAL_WARN(
906 "configmgr",
907 "ignoring modify of unknown set member node \"" << name
908 << "\" in \"" << reader.getUrl() << '"');
909 state_.push(State::Ignore(true));
910 } else {
911 state_.push(State::Modify(i->second));
913 break;
914 case OPERATION_REPLACE:
916 rtl::Reference< Node > member(tmpl->clone(true));
917 member->setLayer(valueParser_.getLayer());
918 member->setFinalized(finalizedLayer);
919 member->setMandatory(mandatoryLayer);
920 state_.push(State::Insert(member, name));
921 recordModification(i == members.end());
923 break;
924 case OPERATION_FUSE:
925 if (i == members.end()) {
926 rtl::Reference< Node > member(tmpl->clone(true));
927 member->setLayer(valueParser_.getLayer());
928 member->setFinalized(finalizedLayer);
929 member->setMandatory(mandatoryLayer);
930 state_.push(State::Insert(member, name));
931 recordModification(true);
932 } else {
933 state_.push(State::Modify(i->second));
935 break;
936 case OPERATION_REMOVE:
938 // Ignore removal of unknown members and members made mandatory in
939 // this or a lower layer; forget about user-layer removals that no
940 // longer remove anything (so that paired additions/removals in the
941 // user layer do not grow registrymodifications.xcu unbounded):
942 bool known = i != members.end();
943 if (known &&
944 (mandatoryLayer == Data::NO_LAYER ||
945 mandatoryLayer > valueParser_.getLayer()))
947 members.erase(i);
949 state_.push(State::Ignore(true));
950 if (known) {
951 recordModification(false);
953 break;
958 void XcuParser::recordModification(bool addition) {
959 if (broadcastModifications_ != nullptr) {
960 broadcastModifications_->add(path_);
962 if (addition && additions_ != nullptr) {
963 additions_->push_back(path_);
965 if (recordModifications_) {
966 data_.modifications.add(path_);
972 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */