bump product version to 7.2.5.1
[LibreOffice.git] / configmgr / source / xcsparser.cxx
blob947792c0a62e39251c3dcfcf58ef9c53b408ad25
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 <set>
25 #include <com/sun/star/uno/Any.hxx>
26 #include <com/sun/star/uno/RuntimeException.hpp>
27 #include <rtl/ref.hxx>
28 #include <rtl/strbuf.hxx>
29 #include <rtl/string.hxx>
30 #include <rtl/ustring.hxx>
31 #include <xmlreader/span.hxx>
32 #include <xmlreader/xmlreader.hxx>
34 #include "data.hxx"
35 #include "localizedpropertynode.hxx"
36 #include "groupnode.hxx"
37 #include "node.hxx"
38 #include "nodemap.hxx"
39 #include "parsemanager.hxx"
40 #include "propertynode.hxx"
41 #include "setnode.hxx"
42 #include "xcsparser.hxx"
43 #include "xmldata.hxx"
45 namespace configmgr {
47 namespace {
49 // Conservatively merge a template or component (and its recursive parts) into
50 // an existing instance:
51 void merge(
52 rtl::Reference< Node > const & original,
53 rtl::Reference< Node > const & update)
55 assert(
56 original.is() && update.is() && original->kind() == update->kind() &&
57 update->getFinalized() == Data::NO_LAYER);
58 if (update->getLayer() < original->getLayer() ||
59 update->getLayer() > original->getFinalized())
60 return;
62 switch (original->kind()) {
63 case Node::KIND_PROPERTY:
64 case Node::KIND_LOCALIZED_PROPERTY:
65 case Node::KIND_LOCALIZED_VALUE:
66 break; //TODO: merge certain parts?
67 case Node::KIND_GROUP:
68 for (auto const& updateMember : update->getMembers())
70 NodeMap & members = original->getMembers();
71 NodeMap::iterator i1(members.find(updateMember.first));
72 if (i1 == members.end()) {
73 if (updateMember.second->kind() == Node::KIND_PROPERTY &&
74 static_cast< GroupNode * >(
75 original.get())->isExtensible())
77 members.insert(updateMember);
79 } else if (updateMember.second->kind() == i1->second->kind()) {
80 merge(i1->second, updateMember.second);
83 break;
84 case Node::KIND_SET:
85 for (auto const& updateMember : update->getMembers())
87 NodeMap & members = original->getMembers();
88 NodeMap::iterator i1(members.find(updateMember.first));
89 if (i1 == members.end()) {
90 if (static_cast< SetNode * >(original.get())->
91 isValidTemplate(updateMember.second->getTemplateName()))
93 members.insert(updateMember);
95 } else if (updateMember.second->kind() == i1->second->kind() &&
96 (updateMember.second->getTemplateName() ==
97 i1->second->getTemplateName()))
99 merge(i1->second, updateMember.second);
102 break;
103 case Node::KIND_ROOT:
104 assert(false); // this cannot happen
105 break;
111 XcsParser::XcsParser(int layer, Data & data):
112 valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
115 XcsParser::~XcsParser() {}
117 xmlreader::XmlReader::Text XcsParser::getTextMode() {
118 return valueParser_.getTextMode();
121 bool XcsParser::startElement(
122 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
123 std::set< OUString > const * /*existingDependencies*/)
125 if (valueParser_.startElement(reader, nsId, name)) {
126 return true;
128 if (state_ == STATE_START) {
129 if (nsId == ParseManager::NAMESPACE_OOR &&
130 name == "component-schema")
132 handleComponentSchema(reader);
133 state_ = STATE_COMPONENT_SCHEMA;
134 ignoring_ = 0;
135 return true;
137 } else {
138 //TODO: ignoring component-schema import, component-schema uses, and
139 // prop constraints; accepting all four at illegal places (and with
140 // illegal content):
141 if (ignoring_ > 0 ||
142 (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
143 (name == "info" || name == "import" ||
144 name == "uses" || name == "constraints")))
146 assert(ignoring_ < LONG_MAX);
147 ++ignoring_;
148 return true;
150 switch (state_) {
151 case STATE_COMPONENT_SCHEMA:
152 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
153 name == "templates")
155 state_ = STATE_TEMPLATES;
156 return true;
158 [[fallthrough]];
159 case STATE_TEMPLATES_DONE:
160 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
161 name == "component")
163 state_ = STATE_COMPONENT;
164 assert(elements_.empty());
165 elements_.push(
166 Element(
167 new GroupNode(valueParser_.getLayer(), false, ""),
168 componentName_));
169 return true;
171 break;
172 case STATE_TEMPLATES:
173 if (elements_.empty()) {
174 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
175 name == "group")
177 handleGroup(reader, true);
178 return true;
180 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
181 name == "set")
183 handleSet(reader, true);
184 return true;
186 break;
188 [[fallthrough]];
189 case STATE_COMPONENT:
190 assert(!elements_.empty());
191 switch (elements_.top().node->kind()) {
192 case Node::KIND_PROPERTY:
193 case Node::KIND_LOCALIZED_PROPERTY:
194 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
195 name == "value")
197 handlePropValue(reader, elements_.top().node);
198 return true;
200 break;
201 case Node::KIND_GROUP:
202 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
203 name == "prop")
205 handleProp(reader);
206 return true;
208 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
209 name == "node-ref")
211 handleNodeRef(reader);
212 return true;
214 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
215 name == "group")
217 handleGroup(reader, false);
218 return true;
220 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
221 name == "set")
223 handleSet(reader, false);
224 return true;
226 break;
227 case Node::KIND_SET:
228 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
229 name == "item")
231 handleSetItem(
232 reader,
233 static_cast< SetNode * >(elements_.top().node.get()));
234 return true;
236 break;
237 default: // Node::KIND_LOCALIZED_VALUE
238 assert(false); // this cannot happen
239 break;
241 break;
242 case STATE_COMPONENT_DONE:
243 break;
244 default: // STATE_START
245 assert(false); // this cannot happen
246 break;
249 throw css::uno::RuntimeException(
250 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
253 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
254 if (valueParser_.endElement()) {
255 return;
257 if (ignoring_ > 0) {
258 --ignoring_;
259 } else if (!elements_.empty()) {
260 Element top(std::move(elements_.top()));
261 elements_.pop();
262 if (top.node.is()) {
263 if (elements_.empty()) {
264 switch (state_) {
265 case STATE_TEMPLATES:
267 auto itPair = data_.templates.insert({top.name, top.node});
268 if (!itPair.second) {
269 merge(itPair.first->second, top.node);
272 break;
273 case STATE_COMPONENT:
275 NodeMap & components = data_.getComponents();
276 auto itPair = components.insert({top.name, top.node});
277 if (!itPair.second) {
278 merge(itPair.first->second, top.node);
280 state_ = STATE_COMPONENT_DONE;
282 break;
283 default:
284 assert(false);
285 throw css::uno::RuntimeException(
286 "this cannot happen");
288 } else {
289 if (!elements_.top().node->getMembers().insert(
290 NodeMap::value_type(top.name, top.node)).second)
292 throw css::uno::RuntimeException(
293 "duplicate " + top.name + " in " + reader.getUrl());
297 } else {
298 switch (state_) {
299 case STATE_COMPONENT_SCHEMA:
300 // To support old, broken extensions with .xcs files that contain
301 // empty <component-schema> elements:
302 state_ = STATE_COMPONENT_DONE;
303 break;
304 case STATE_TEMPLATES:
305 state_ = STATE_TEMPLATES_DONE;
306 break;
307 case STATE_TEMPLATES_DONE:
308 throw css::uno::RuntimeException(
309 "no component element in " + reader.getUrl());
310 case STATE_COMPONENT_DONE:
311 break;
312 default:
313 assert(false); // this cannot happen
318 void XcsParser::characters(xmlreader::Span const & text) {
319 valueParser_.characters(text);
322 void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
323 //TODO: oor:version, xml:lang attributes
324 OStringBuffer buf(256);
325 buf.append('.');
326 bool hasPackage = false;
327 bool hasName = false;
328 for (;;) {
329 int attrNsId;
330 xmlreader::Span attrLn;
331 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
332 break;
334 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
336 if (hasPackage) {
337 throw css::uno::RuntimeException(
338 "multiple component-schema package attributes in " +
339 reader.getUrl());
341 hasPackage = true;
342 xmlreader::Span s(reader.getAttributeValue(false));
343 buf.insert(0, s.begin, s.length);
344 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
345 attrLn == "name")
347 if (hasName) {
348 throw css::uno::RuntimeException(
349 "multiple component-schema name attributes in " +
350 reader.getUrl());
352 hasName = true;
353 xmlreader::Span s(reader.getAttributeValue(false));
354 buf.append(s.begin, s.length);
357 if (!hasPackage) {
358 throw css::uno::RuntimeException(
359 "no component-schema package attribute in " + reader.getUrl());
361 if (!hasName) {
362 throw css::uno::RuntimeException(
363 "no component-schema name attribute in " + reader.getUrl());
365 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
366 convertFromUtf8();
369 void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
370 bool hasName = false;
371 OUString name;
372 OUString component(componentName_);
373 bool hasNodeType = false;
374 OUString nodeType;
375 for (;;) {
376 int attrNsId;
377 xmlreader::Span attrLn;
378 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
379 break;
381 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
382 hasName = true;
383 name = reader.getAttributeValue(false).convertFromUtf8();
384 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
385 attrLn == "component")
387 component = reader.getAttributeValue(false).convertFromUtf8();
388 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
389 attrLn == "node-type")
391 hasNodeType = true;
392 nodeType = reader.getAttributeValue(false).convertFromUtf8();
395 if (!hasName) {
396 throw css::uno::RuntimeException(
397 "no node-ref name attribute in " + reader.getUrl());
399 rtl::Reference< Node > tmpl(
400 data_.getTemplate(
401 valueParser_.getLayer(),
402 xmldata::parseTemplateReference(
403 component, hasNodeType, nodeType, nullptr)));
404 if (!tmpl.is()) {
405 //TODO: this can erroneously happen as long as import/uses attributes
406 // are not correctly processed
407 throw css::uno::RuntimeException(
408 "unknown node-ref " + name + " in " + reader.getUrl());
410 rtl::Reference< Node > node(tmpl->clone(false));
411 node->setLayer(valueParser_.getLayer());
412 elements_.push(Element(node, name));
415 void XcsParser::handleProp(xmlreader::XmlReader & reader) {
416 bool hasName = false;
417 OUString name;
418 valueParser_.type_ = TYPE_ERROR;
419 bool localized = false;
420 bool nillable = true;
421 for (;;) {
422 int attrNsId;
423 xmlreader::Span attrLn;
424 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
425 break;
427 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
428 hasName = true;
429 name = reader.getAttributeValue(false).convertFromUtf8();
430 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
431 attrLn == "type")
433 valueParser_.type_ = xmldata::parseType(
434 reader, reader.getAttributeValue(true));
435 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
436 attrLn == "localized")
438 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
439 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
440 attrLn == "nillable")
442 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
445 if (!hasName) {
446 throw css::uno::RuntimeException(
447 "no prop name attribute in " + reader.getUrl());
449 if (valueParser_.type_ == TYPE_ERROR) {
450 throw css::uno::RuntimeException(
451 "no prop type attribute in " + reader.getUrl());
453 elements_.push(
454 Element(
455 (localized
456 ? rtl::Reference< Node >(
457 new LocalizedPropertyNode(
458 valueParser_.getLayer(), valueParser_.type_, nillable))
459 : rtl::Reference< Node >(
460 new PropertyNode(
461 valueParser_.getLayer(), valueParser_.type_, nillable,
462 css::uno::Any(), false))),
463 name));
466 void XcsParser::handlePropValue(
467 xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
469 xmlreader::Span attrSeparator;
470 for (;;) {
471 int attrNsId;
472 xmlreader::Span attrLn;
473 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
474 break;
476 if (attrNsId == ParseManager::NAMESPACE_OOR &&
477 attrLn == "separator")
479 attrSeparator = reader.getAttributeValue(false);
480 if (attrSeparator.length == 0) {
481 throw css::uno::RuntimeException(
482 "bad oor:separator attribute in " + reader.getUrl());
486 valueParser_.separator_ = OString(
487 attrSeparator.begin, attrSeparator.length);
488 valueParser_.start(property);
491 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
492 bool hasName = false;
493 OUString name;
494 bool extensible = false;
495 for (;;) {
496 int attrNsId;
497 xmlreader::Span attrLn;
498 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
499 break;
501 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
502 hasName = true;
503 name = reader.getAttributeValue(false).convertFromUtf8();
504 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
505 attrLn == "extensible")
507 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
510 if (!hasName) {
511 throw css::uno::RuntimeException(
512 "no group name attribute in " + reader.getUrl());
514 if (isTemplate) {
515 name = Data::fullTemplateName(componentName_, name);
517 elements_.push(
518 Element(
519 new GroupNode(
520 valueParser_.getLayer(), extensible,
521 isTemplate ? name : OUString()),
522 name));
525 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
526 bool hasName = false;
527 OUString name;
528 OUString component(componentName_);
529 bool hasNodeType = false;
530 OUString nodeType;
531 for (;;) {
532 int attrNsId;
533 xmlreader::Span attrLn;
534 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
535 break;
537 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
538 hasName = true;
539 name = reader.getAttributeValue(false).convertFromUtf8();
540 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
541 attrLn == "component")
543 component = reader.getAttributeValue(false).convertFromUtf8();
544 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
545 attrLn == "node-type")
547 hasNodeType = true;
548 nodeType = reader.getAttributeValue(false).convertFromUtf8();
551 if (!hasName) {
552 throw css::uno::RuntimeException(
553 "no set name attribute in " + reader.getUrl());
555 if (isTemplate) {
556 name = Data::fullTemplateName(componentName_, name);
558 elements_.push(
559 Element(
560 new SetNode(
561 valueParser_.getLayer(),
562 xmldata::parseTemplateReference(
563 component, hasNodeType, nodeType, nullptr),
564 isTemplate ? name : OUString()),
565 name));
568 void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
569 OUString component(componentName_);
570 bool hasNodeType = false;
571 OUString nodeType;
572 for (;;) {
573 int attrNsId;
574 xmlreader::Span attrLn;
575 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
576 break;
578 if (attrNsId == ParseManager::NAMESPACE_OOR &&
579 attrLn == "component")
581 component = reader.getAttributeValue(false).convertFromUtf8();
582 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
583 attrLn == "node-type")
585 hasNodeType = true;
586 nodeType = reader.getAttributeValue(false).convertFromUtf8();
589 set->getAdditionalTemplateNames().push_back(
590 xmldata::parseTemplateReference(component, hasNodeType, nodeType, nullptr));
591 elements_.push(Element(rtl::Reference< Node >(), ""));
596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */