lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / configmgr / source / xcsparser.cxx
blob271d617e5019e2f3af3fd22fe1a0f6d85f9cfd0e
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 <cassert>
23 #include <cstddef>
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.hxx>
33 #include <rtl/ustring.hxx>
34 #include <xmlreader/span.hxx>
35 #include <xmlreader/xmlreader.hxx>
37 #include "data.hxx"
38 #include "localizedpropertynode.hxx"
39 #include "groupnode.hxx"
40 #include "node.hxx"
41 #include "nodemap.hxx"
42 #include "parsemanager.hxx"
43 #include "propertynode.hxx"
44 #include "setnode.hxx"
45 #include "xcsparser.hxx"
46 #include "xmldata.hxx"
48 namespace configmgr {
50 namespace {
52 // Conservatively merge a template or component (and its recursive parts) into
53 // an existing instance:
54 void merge(
55 rtl::Reference< Node > const & original,
56 rtl::Reference< Node > const & update)
58 assert(
59 original.is() && update.is() && original->kind() == update->kind() &&
60 update->getFinalized() == Data::NO_LAYER);
61 if (update->getLayer() >= original->getLayer() &&
62 update->getLayer() <= original->getFinalized())
64 switch (original->kind()) {
65 case Node::KIND_PROPERTY:
66 case Node::KIND_LOCALIZED_PROPERTY:
67 case Node::KIND_LOCALIZED_VALUE:
68 break; //TODO: merge certain parts?
69 case Node::KIND_GROUP:
70 for (auto const& updateMember : update->getMembers())
72 NodeMap & members = original->getMembers();
73 NodeMap::iterator i1(members.find(updateMember.first));
74 if (i1 == members.end()) {
75 if (updateMember.second->kind() == Node::KIND_PROPERTY &&
76 static_cast< GroupNode * >(
77 original.get())->isExtensible())
79 members.insert(updateMember);
81 } else if (updateMember.second->kind() == i1->second->kind()) {
82 merge(i1->second, updateMember.second);
85 break;
86 case Node::KIND_SET:
87 for (auto const& updateMember : update->getMembers())
89 NodeMap & members = original->getMembers();
90 NodeMap::iterator i1(members.find(updateMember.first));
91 if (i1 == members.end()) {
92 if (static_cast< SetNode * >(original.get())->
93 isValidTemplate(updateMember.second->getTemplateName()))
95 members.insert(updateMember);
97 } else if (updateMember.second->kind() == i1->second->kind() &&
98 (updateMember.second->getTemplateName() ==
99 i1->second->getTemplateName()))
101 merge(i1->second, updateMember.second);
104 break;
105 case Node::KIND_ROOT:
106 assert(false); // this cannot happen
107 break;
114 XcsParser::XcsParser(int layer, Data & data):
115 valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
118 XcsParser::~XcsParser() {}
120 xmlreader::XmlReader::Text XcsParser::getTextMode() {
121 return valueParser_.getTextMode();
124 bool XcsParser::startElement(
125 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
126 std::set< OUString > const * /*existingDependencies*/)
128 if (valueParser_.startElement(reader, nsId, name)) {
129 return true;
131 if (state_ == STATE_START) {
132 if (nsId == ParseManager::NAMESPACE_OOR &&
133 name.equals("component-schema"))
135 handleComponentSchema(reader);
136 state_ = STATE_COMPONENT_SCHEMA;
137 ignoring_ = 0;
138 return true;
140 } else {
141 //TODO: ignoring component-schema import, component-schema uses, and
142 // prop constraints; accepting all four at illegal places (and with
143 // illegal content):
144 if (ignoring_ > 0 ||
145 (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
146 (name.equals("info") || name.equals("import") ||
147 name.equals("uses") || name.equals("constraints"))))
149 assert(ignoring_ < LONG_MAX);
150 ++ignoring_;
151 return true;
153 switch (state_) {
154 case STATE_COMPONENT_SCHEMA:
155 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
156 name.equals("templates"))
158 state_ = STATE_TEMPLATES;
159 return true;
161 SAL_FALLTHROUGH;
162 case STATE_TEMPLATES_DONE:
163 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
164 name.equals("component"))
166 state_ = STATE_COMPONENT;
167 assert(elements_.empty());
168 elements_.push(
169 Element(
170 new GroupNode(valueParser_.getLayer(), false, ""),
171 componentName_));
172 return true;
174 break;
175 case STATE_TEMPLATES:
176 if (elements_.empty()) {
177 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
178 name.equals("group"))
180 handleGroup(reader, true);
181 return true;
183 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
184 name.equals("set"))
186 handleSet(reader, true);
187 return true;
189 break;
191 SAL_FALLTHROUGH;
192 case STATE_COMPONENT:
193 assert(!elements_.empty());
194 switch (elements_.top().node->kind()) {
195 case Node::KIND_PROPERTY:
196 case Node::KIND_LOCALIZED_PROPERTY:
197 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
198 name.equals("value"))
200 handlePropValue(reader, elements_.top().node);
201 return true;
203 break;
204 case Node::KIND_GROUP:
205 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
206 name.equals("prop"))
208 handleProp(reader);
209 return true;
211 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
212 name.equals("node-ref"))
214 handleNodeRef(reader);
215 return true;
217 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
218 name.equals("group"))
220 handleGroup(reader, false);
221 return true;
223 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
224 name.equals("set"))
226 handleSet(reader, false);
227 return true;
229 break;
230 case Node::KIND_SET:
231 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
232 name.equals("item"))
234 handleSetItem(
235 reader,
236 static_cast< SetNode * >(elements_.top().node.get()));
237 return true;
239 break;
240 default: // Node::KIND_LOCALIZED_VALUE
241 assert(false); // this cannot happen
242 break;
244 break;
245 case STATE_COMPONENT_DONE:
246 break;
247 default: // STATE_START
248 assert(false); // this cannot happen
249 break;
252 throw css::uno::RuntimeException(
253 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
256 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
257 if (valueParser_.endElement()) {
258 return;
260 if (ignoring_ > 0) {
261 --ignoring_;
262 } else if (!elements_.empty()) {
263 Element top(elements_.top());
264 elements_.pop();
265 if (top.node.is()) {
266 if (elements_.empty()) {
267 switch (state_) {
268 case STATE_TEMPLATES:
270 NodeMap::iterator i(data_.templates.find(top.name));
271 if (i == data_.templates.end()) {
272 data_.templates.insert(
273 NodeMap::value_type(top.name, top.node));
274 } else {
275 merge(i->second, top.node);
278 break;
279 case STATE_COMPONENT:
281 NodeMap & components = data_.getComponents();
282 NodeMap::iterator i(components.find(top.name));
283 if (i == components.end()) {
284 components.insert(
285 NodeMap::value_type(top.name, top.node));
286 } else {
287 merge(i->second, top.node);
289 state_ = STATE_COMPONENT_DONE;
291 break;
292 default:
293 assert(false);
294 throw css::uno::RuntimeException(
295 "this cannot happen");
297 } else {
298 if (!elements_.top().node->getMembers().insert(
299 NodeMap::value_type(top.name, top.node)).second)
301 throw css::uno::RuntimeException(
302 "duplicate " + top.name + " in " + reader.getUrl());
306 } else {
307 switch (state_) {
308 case STATE_COMPONENT_SCHEMA:
309 // To support old, broken extensions with .xcs files that contain
310 // empty <component-schema> elements:
311 state_ = STATE_COMPONENT_DONE;
312 break;
313 case STATE_TEMPLATES:
314 state_ = STATE_TEMPLATES_DONE;
315 break;
316 case STATE_TEMPLATES_DONE:
317 throw css::uno::RuntimeException(
318 "no component element in " + reader.getUrl());
319 case STATE_COMPONENT_DONE:
320 break;
321 default:
322 assert(false); // this cannot happen
327 void XcsParser::characters(xmlreader::Span const & text) {
328 valueParser_.characters(text);
331 void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
332 //TODO: oor:version, xml:lang attributes
333 OStringBuffer buf;
334 buf.append('.');
335 bool hasPackage = false;
336 bool hasName = false;
337 for (;;) {
338 int attrNsId;
339 xmlreader::Span attrLn;
340 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
341 break;
343 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
345 if (hasPackage) {
346 throw css::uno::RuntimeException(
347 "multiple component-schema package attributes in " +
348 reader.getUrl());
350 hasPackage = true;
351 xmlreader::Span s(reader.getAttributeValue(false));
352 buf.insert(0, s.begin, s.length);
353 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
354 attrLn.equals("name"))
356 if (hasName) {
357 throw css::uno::RuntimeException(
358 "multiple component-schema name attributes in " +
359 reader.getUrl());
361 hasName = true;
362 xmlreader::Span s(reader.getAttributeValue(false));
363 buf.append(s.begin, s.length);
366 if (!hasPackage) {
367 throw css::uno::RuntimeException(
368 "no component-schema package attribute in " + reader.getUrl());
370 if (!hasName) {
371 throw css::uno::RuntimeException(
372 "no component-schema name attribute in " + reader.getUrl());
374 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
375 convertFromUtf8();
378 void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
379 bool hasName = false;
380 OUString name;
381 OUString component(componentName_);
382 bool hasNodeType = false;
383 OUString nodeType;
384 for (;;) {
385 int attrNsId;
386 xmlreader::Span attrLn;
387 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
388 break;
390 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
391 hasName = true;
392 name = reader.getAttributeValue(false).convertFromUtf8();
393 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
394 attrLn.equals("component"))
396 component = reader.getAttributeValue(false).convertFromUtf8();
397 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
398 attrLn.equals("node-type"))
400 hasNodeType = true;
401 nodeType = reader.getAttributeValue(false).convertFromUtf8();
404 if (!hasName) {
405 throw css::uno::RuntimeException(
406 "no node-ref name attribute in " + reader.getUrl());
408 rtl::Reference< Node > tmpl(
409 data_.getTemplate(
410 valueParser_.getLayer(),
411 xmldata::parseTemplateReference(
412 component, hasNodeType, nodeType, nullptr)));
413 if (!tmpl.is()) {
414 //TODO: this can erroneously happen as long as import/uses attributes
415 // are not correctly processed
416 throw css::uno::RuntimeException(
417 "unknown node-ref " + name + " in " + reader.getUrl());
419 rtl::Reference< Node > node(tmpl->clone(false));
420 node->setLayer(valueParser_.getLayer());
421 elements_.push(Element(node, name));
424 void XcsParser::handleProp(xmlreader::XmlReader & reader) {
425 bool hasName = false;
426 OUString name;
427 valueParser_.type_ = TYPE_ERROR;
428 bool localized = false;
429 bool nillable = true;
430 for (;;) {
431 int attrNsId;
432 xmlreader::Span attrLn;
433 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
434 break;
436 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
437 hasName = true;
438 name = reader.getAttributeValue(false).convertFromUtf8();
439 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
440 attrLn.equals("type"))
442 valueParser_.type_ = xmldata::parseType(
443 reader, reader.getAttributeValue(true));
444 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
445 attrLn.equals("localized"))
447 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
448 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
449 attrLn.equals("nillable"))
451 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
454 if (!hasName) {
455 throw css::uno::RuntimeException(
456 "no prop name attribute in " + reader.getUrl());
458 if (valueParser_.type_ == TYPE_ERROR) {
459 throw css::uno::RuntimeException(
460 "no prop type attribute in " + reader.getUrl());
462 elements_.push(
463 Element(
464 (localized
465 ? rtl::Reference< Node >(
466 new LocalizedPropertyNode(
467 valueParser_.getLayer(), valueParser_.type_, nillable))
468 : rtl::Reference< Node >(
469 new PropertyNode(
470 valueParser_.getLayer(), valueParser_.type_, nillable,
471 css::uno::Any(), false))),
472 name));
475 void XcsParser::handlePropValue(
476 xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
478 xmlreader::Span attrSeparator;
479 for (;;) {
480 int attrNsId;
481 xmlreader::Span attrLn;
482 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
483 break;
485 if (attrNsId == ParseManager::NAMESPACE_OOR &&
486 attrLn.equals("separator"))
488 attrSeparator = reader.getAttributeValue(false);
489 if (attrSeparator.length == 0) {
490 throw css::uno::RuntimeException(
491 "bad oor:separator attribute in " + reader.getUrl());
495 valueParser_.separator_ = OString(
496 attrSeparator.begin, attrSeparator.length);
497 valueParser_.start(property);
500 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
501 bool hasName = false;
502 OUString name;
503 bool extensible = false;
504 for (;;) {
505 int attrNsId;
506 xmlreader::Span attrLn;
507 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
508 break;
510 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
511 hasName = true;
512 name = reader.getAttributeValue(false).convertFromUtf8();
513 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
514 attrLn.equals("extensible"))
516 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
519 if (!hasName) {
520 throw css::uno::RuntimeException(
521 "no group name attribute in " + reader.getUrl());
523 if (isTemplate) {
524 name = Data::fullTemplateName(componentName_, name);
526 elements_.push(
527 Element(
528 new GroupNode(
529 valueParser_.getLayer(), extensible,
530 isTemplate ? name : OUString()),
531 name));
534 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
535 bool hasName = false;
536 OUString name;
537 OUString component(componentName_);
538 bool hasNodeType = false;
539 OUString nodeType;
540 for (;;) {
541 int attrNsId;
542 xmlreader::Span attrLn;
543 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
544 break;
546 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
547 hasName = true;
548 name = reader.getAttributeValue(false).convertFromUtf8();
549 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
550 attrLn.equals("component"))
552 component = reader.getAttributeValue(false).convertFromUtf8();
553 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
554 attrLn.equals("node-type"))
556 hasNodeType = true;
557 nodeType = reader.getAttributeValue(false).convertFromUtf8();
560 if (!hasName) {
561 throw css::uno::RuntimeException(
562 "no set name attribute in " + reader.getUrl());
564 if (isTemplate) {
565 name = Data::fullTemplateName(componentName_, name);
567 elements_.push(
568 Element(
569 new SetNode(
570 valueParser_.getLayer(),
571 xmldata::parseTemplateReference(
572 component, hasNodeType, nodeType, nullptr),
573 isTemplate ? name : OUString()),
574 name));
577 void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
578 OUString component(componentName_);
579 bool hasNodeType = false;
580 OUString nodeType;
581 for (;;) {
582 int attrNsId;
583 xmlreader::Span attrLn;
584 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
585 break;
587 if (attrNsId == ParseManager::NAMESPACE_OOR &&
588 attrLn.equals("component"))
590 component = reader.getAttributeValue(false).convertFromUtf8();
591 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
592 attrLn.equals("node-type"))
594 hasNodeType = true;
595 nodeType = reader.getAttributeValue(false).convertFromUtf8();
598 set->getAdditionalTemplateNames().push_back(
599 xmldata::parseTemplateReference(component, hasNodeType, nodeType, nullptr));
600 elements_.push(Element(rtl::Reference< Node >(), ""));
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */