LibreOffice Module configmgr (master) 1
xcsparser.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <cassert>
23#include <set>
24
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>
33
34#include "data.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"
44
45namespace configmgr {
46
47namespace {
48
49// Conservatively merge a template or component (and its recursive parts) into
50// an existing instance:
51void merge(
52 rtl::Reference< Node > const & original,
53 rtl::Reference< Node > const & update)
54{
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;
61
62 switch (original->kind()) {
66 break; //TODO: merge certain parts?
68 for (auto const& updateMember : update->getMembers())
69 {
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())
76 {
77 members.insert(updateMember);
78 }
79 } else if (updateMember.second->kind() == i1->second->kind()) {
80 merge(i1->second, updateMember.second);
81 }
82 }
83 break;
84 case Node::KIND_SET:
85 for (auto const& updateMember : update->getMembers())
86 {
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()))
92 {
93 members.insert(updateMember);
94 }
95 } else if (updateMember.second->kind() == i1->second->kind() &&
96 (updateMember.second->getTemplateName() ==
97 i1->second->getTemplateName()))
98 {
99 merge(i1->second, updateMember.second);
100 }
101 }
102 break;
103 case Node::KIND_ROOT:
104 assert(false); // this cannot happen
105 break;
106 }
107}
108
109}
110
111XcsParser::XcsParser(int layer, Data & data):
112 valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
113{}
114
116
118 return valueParser_.getTextMode();
119}
120
122 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
123 std::set< OUString > const * /*existingDependencies*/)
124{
125 if (valueParser_.startElement(reader, nsId, name)) {
126 return true;
127 }
128 if (state_ == STATE_START) {
129 if (nsId == ParseManager::NAMESPACE_OOR &&
130 name == "component-schema")
131 {
132 handleComponentSchema(reader);
134 ignoring_ = 0;
135 return true;
136 }
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 ||
143 (name == "info" || name == "import" ||
144 name == "uses" || name == "constraints")))
145 {
146 assert(ignoring_ < LONG_MAX);
147 ++ignoring_;
148 return true;
149 }
150 switch (state_) {
153 name == "templates")
154 {
156 return true;
157 }
158 [[fallthrough]];
161 name == "component")
162 {
164 assert(elements_.empty());
165 elements_.push(
166 Element(
167 new GroupNode(valueParser_.getLayer(), false, ""),
169 return true;
170 }
171 break;
172 case STATE_TEMPLATES:
173 if (elements_.empty()) {
175 name == "group")
176 {
177 handleGroup(reader, true);
178 return true;
179 }
181 name == "set")
182 {
183 handleSet(reader, true);
184 return true;
185 }
186 break;
187 }
188 [[fallthrough]];
189 case STATE_COMPONENT:
190 assert(!elements_.empty());
191 switch (elements_.top().node->kind()) {
195 name == "value")
196 {
197 handlePropValue(reader, elements_.top().node);
198 return true;
199 }
200 break;
201 case Node::KIND_GROUP:
203 name == "prop")
204 {
205 handleProp(reader);
206 return true;
207 }
209 name == "node-ref")
210 {
211 handleNodeRef(reader);
212 return true;
213 }
215 name == "group")
216 {
217 handleGroup(reader, false);
218 return true;
219 }
221 name == "set")
222 {
223 handleSet(reader, false);
224 return true;
225 }
226 break;
227 case Node::KIND_SET:
229 name == "item")
230 {
232 reader,
233 static_cast< SetNode * >(elements_.top().node.get()));
234 return true;
235 }
236 break;
237 default: // Node::KIND_LOCALIZED_VALUE
238 assert(false); // this cannot happen
239 break;
240 }
241 break;
243 break;
244 default: // STATE_START
245 assert(false); // this cannot happen
246 break;
247 }
248 }
249 throw css::uno::RuntimeException(
250 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
251}
252
254 if (valueParser_.endElement()) {
255 return;
256 }
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:
266 {
267 auto itPair = data_.templates.insert({top.name, top.node});
268 if (!itPair.second) {
269 merge(itPair.first->second, top.node);
270 }
271 }
272 break;
273 case STATE_COMPONENT:
274 {
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);
279 }
281 }
282 break;
283 default:
284 assert(false);
285 throw css::uno::RuntimeException(
286 "this cannot happen");
287 }
288 } else {
289 if (!elements_.top().node->getMembers().insert(
290 NodeMap::value_type(top.name, top.node)).second)
291 {
292 throw css::uno::RuntimeException(
293 "duplicate " + top.name + " in " + reader.getUrl());
294 }
295 }
296 }
297 } else {
298 switch (state_) {
300 // To support old, broken extensions with .xcs files that contain
301 // empty <component-schema> elements:
303 break;
304 case STATE_TEMPLATES:
306 break;
308 throw css::uno::RuntimeException(
309 "no component element in " + reader.getUrl());
311 break;
312 default:
313 assert(false); // this cannot happen
314 }
315 }
316}
317
320}
321
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;
333 }
334 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
335 {
336 if (hasPackage) {
337 throw css::uno::RuntimeException(
338 "multiple component-schema package attributes in " +
339 reader.getUrl());
340 }
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")
346 {
347 if (hasName) {
348 throw css::uno::RuntimeException(
349 "multiple component-schema name attributes in " +
350 reader.getUrl());
351 }
352 hasName = true;
353 xmlreader::Span s(reader.getAttributeValue(false));
354 buf.append(s.begin, s.length);
355 }
356 }
357 if (!hasPackage) {
358 throw css::uno::RuntimeException(
359 "no component-schema package attribute in " + reader.getUrl());
360 }
361 if (!hasName) {
362 throw css::uno::RuntimeException(
363 "no component-schema name attribute in " + reader.getUrl());
364 }
365 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
366 convertFromUtf8();
367}
368
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;
380 }
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")
386 {
387 component = reader.getAttributeValue(false).convertFromUtf8();
388 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
389 attrLn == "node-type")
390 {
391 hasNodeType = true;
392 nodeType = reader.getAttributeValue(false).convertFromUtf8();
393 }
394 }
395 if (!hasName) {
396 throw css::uno::RuntimeException(
397 "no node-ref name attribute in " + reader.getUrl());
398 }
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());
409 }
410 rtl::Reference< Node > node(tmpl->clone(false));
411 node->setLayer(valueParser_.getLayer());
412 elements_.push(Element(node, name));
413}
414
416 bool hasName = false;
417 OUString name;
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;
426 }
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")
432 {
434 reader, reader.getAttributeValue(true));
435 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
436 attrLn == "localized")
437 {
438 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
439 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
440 attrLn == "nillable")
441 {
442 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
443 }
444 }
445 if (!hasName) {
446 throw css::uno::RuntimeException(
447 "no prop name attribute in " + reader.getUrl());
448 }
450 throw css::uno::RuntimeException(
451 "no prop type attribute in " + reader.getUrl());
452 }
453 elements_.push(
454 Element(
455 (localized
460 new PropertyNode(
462 css::uno::Any(), false))),
463 name));
464}
465
468{
469 xmlreader::Span attrSeparator;
470 for (;;) {
471 int attrNsId;
472 xmlreader::Span attrLn;
473 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
474 break;
475 }
476 if (attrNsId == ParseManager::NAMESPACE_OOR &&
477 attrLn == "separator")
478 {
479 attrSeparator = reader.getAttributeValue(false);
480 if (attrSeparator.length == 0) {
481 throw css::uno::RuntimeException(
482 "bad oor:separator attribute in " + reader.getUrl());
483 }
484 }
485 }
486 valueParser_.separator_ = OString(
487 attrSeparator.begin, attrSeparator.length);
489}
490
491void 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;
500 }
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")
506 {
507 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
508 }
509 }
510 if (!hasName) {
511 throw css::uno::RuntimeException(
512 "no group name attribute in " + reader.getUrl());
513 }
514 if (isTemplate) {
516 }
517 elements_.push(
518 Element(
519 new GroupNode(
520 valueParser_.getLayer(), extensible,
521 isTemplate ? name : OUString()),
522 name));
523}
524
525void 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;
536 }
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")
542 {
543 component = reader.getAttributeValue(false).convertFromUtf8();
544 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
545 attrLn == "node-type")
546 {
547 hasNodeType = true;
548 nodeType = reader.getAttributeValue(false).convertFromUtf8();
549 }
550 }
551 if (!hasName) {
552 throw css::uno::RuntimeException(
553 "no set name attribute in " + reader.getUrl());
554 }
555 if (isTemplate) {
557 }
558 elements_.push(
559 Element(
560 new SetNode(
563 component, hasNodeType, nodeType, nullptr),
564 isTemplate ? name : OUString()),
565 name));
566}
567
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;
577 }
578 if (attrNsId == ParseManager::NAMESPACE_OOR &&
579 attrLn == "component")
580 {
581 component = reader.getAttributeValue(false).convertFromUtf8();
582 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
583 attrLn == "node-type")
584 {
585 hasNodeType = true;
586 nodeType = reader.getAttributeValue(false).convertFromUtf8();
587 }
588 }
589 set->getAdditionalTemplateNames().push_back(
590 xmldata::parseTemplateReference(component, hasNodeType, nodeType, nullptr));
592}
593
594}
595
596/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
NodeMapImpl::iterator iterator
Definition: nodemap.hxx:37
std::pair< iterator, bool > insert(const value_type &vt)
Definition: nodemap.hxx:53
NodeMapImpl::value_type value_type
Definition: nodemap.hxx:39
@ KIND_LOCALIZED_PROPERTY
Definition: node.hxx:35
@ KIND_LOCALIZED_VALUE
Definition: node.hxx:35
void characters(xmlreader::Span const &text)
bool startElement(xmlreader::XmlReader &reader, int nsId, xmlreader::Span const &name)
xmlreader::XmlReader::Text getTextMode() const
void start(rtl::Reference< Node > const &property, OUString const &localizedName=OUString())
OUString componentName_
Definition: xcsparser.hxx:93
virtual xmlreader::XmlReader::Text getTextMode() override
Definition: xcsparser.cxx:117
XcsParser(int layer, Data &data)
Definition: xcsparser.cxx:111
void handleProp(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:415
virtual bool startElement(xmlreader::XmlReader &reader, int nsId, xmlreader::Span const &name, std::set< OUString > const *existingDependencies) override
Definition: xcsparser.cxx:121
ValueParser valueParser_
Definition: xcsparser.hxx:91
void handleComponentSchema(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:322
void handleNodeRef(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:369
void handlePropValue(xmlreader::XmlReader &reader, rtl::Reference< Node > const &property)
Definition: xcsparser.cxx:466
void handleGroup(xmlreader::XmlReader &reader, bool isTemplate)
Definition: xcsparser.cxx:491
void handleSetItem(xmlreader::XmlReader &reader, SetNode *set)
Definition: xcsparser.cxx:568
void handleSet(xmlreader::XmlReader &reader, bool isTemplate)
Definition: xcsparser.cxx:525
virtual void endElement(xmlreader::XmlReader const &reader) override
Definition: xcsparser.cxx:253
virtual void characters(xmlreader::Span const &text) override
Definition: xcsparser.cxx:318
virtual ~XcsParser() override
Definition: xcsparser.cxx:115
ElementStack elements_
Definition: xcsparser.hxx:96
const OUString & getUrl() const
bool nextAttribute(int *nsId, Span *localName)
Span getAttributeValue(bool fullyNormalize)
OUString name
Definition: components.cxx:85
OString top
const long LONG_MAX
std::map< const SwTextNode *, const sal_uInt32 > NodeMap
def text(shape, orig_st)
void set(css::uno::UnoInterfaceReference const &value)
Type parseType(xmlreader::XmlReader const &reader, xmlreader::Span const &text)
Definition: xmldata.cxx:38
bool parseBoolean(xmlreader::Span const &text)
Definition: xmldata.cxx:105
OUString parseTemplateReference(std::u16string_view component, bool hasNodeType, std::u16string_view nodeType, OUString const *defaultTemplateName)
Definition: xmldata.cxx:117
@ TYPE_ERROR
Definition: type.hxx:33
PyObject_HEAD PyUNO_callable_Internals * members
NodeMap templates
Definition: data.hxx:49
static OUString fullTemplateName(std::u16string_view component, std::u16string_view name)
Definition: data.cxx:154
NodeMap & getComponents() const
Definition: data.cxx:294
rtl::Reference< Node > getTemplate(int layer, OUString const &fullName) const
Definition: data.cxx:288
rtl::OUString convertFromUtf8() const
char const * begin
sal_Int32 length
bool update()