Bump version to 4.1-6
[LibreOffice.git] / configmgr / source / xcsparser.cxx
blobb60cb1d5145e98f96f0ad8a11bf85452393fab6d
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 (NodeMap::const_iterator i2(update->getMembers().begin());
71 i2 != update->getMembers().end(); ++i2)
73 NodeMap & members = original->getMembers();
74 NodeMap::iterator i1(members.find(i2->first));
75 if (i1 == members.end()) {
76 if (i2->second->kind() == Node::KIND_PROPERTY &&
77 dynamic_cast< GroupNode * >(
78 original.get())->isExtensible())
80 members.insert(*i2);
82 } else if (i2->second->kind() == i1->second->kind()) {
83 merge(i1->second, i2->second);
86 break;
87 case Node::KIND_SET:
88 for (NodeMap::const_iterator i2(update->getMembers().begin());
89 i2 != update->getMembers().end(); ++i2)
91 NodeMap & members = original->getMembers();
92 NodeMap::iterator i1(members.find(i2->first));
93 if (i1 == members.end()) {
94 if (dynamic_cast< SetNode * >(original.get())->
95 isValidTemplate(i2->second->getTemplateName()))
97 members.insert(*i2);
99 } else if (i2->second->kind() == i1->second->kind() &&
100 (i2->second->getTemplateName() ==
101 i1->second->getTemplateName()))
103 merge(i1->second, i2->second);
106 break;
107 case Node::KIND_ROOT:
108 assert(false); // this cannot happen
109 break;
116 XcsParser::XcsParser(int layer, Data & data):
117 valueParser_(layer), data_(data), state_(STATE_START)
120 XcsParser::~XcsParser() {}
122 xmlreader::XmlReader::Text XcsParser::getTextMode() {
123 return valueParser_.getTextMode();
126 bool XcsParser::startElement(
127 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
128 std::set< OUString > const * existingDependencies)
130 if (valueParser_.startElement(reader, nsId, name, existingDependencies)) {
131 return true;
133 if (state_ == STATE_START) {
134 if (nsId == ParseManager::NAMESPACE_OOR &&
135 name.equals("component-schema"))
137 handleComponentSchema(reader);
138 state_ = STATE_COMPONENT_SCHEMA;
139 ignoring_ = 0;
140 return true;
142 } else {
143 //TODO: ignoring component-schema import, component-schema uses, and
144 // prop constraints; accepting all four at illegal places (and with
145 // illegal content):
146 if (ignoring_ > 0 ||
147 (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
148 (name.equals("info") || name.equals("import") ||
149 name.equals("uses") || name.equals("constraints"))))
151 assert(ignoring_ < LONG_MAX);
152 ++ignoring_;
153 return true;
155 switch (state_) {
156 case STATE_COMPONENT_SCHEMA:
157 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
158 name.equals("templates"))
160 state_ = STATE_TEMPLATES;
161 return true;
163 // fall through
164 case STATE_TEMPLATES_DONE:
165 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
166 name.equals("component"))
168 state_ = STATE_COMPONENT;
169 assert(elements_.empty());
170 elements_.push(
171 Element(
172 new GroupNode(
173 valueParser_.getLayer(), false, OUString()),
174 componentName_));
175 return true;
177 break;
178 case STATE_TEMPLATES:
179 if (elements_.empty()) {
180 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
181 name.equals("group"))
183 handleGroup(reader, true);
184 return true;
186 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
187 name.equals("set"))
189 handleSet(reader, true);
190 return true;
192 break;
194 // fall through
195 case STATE_COMPONENT:
196 assert(!elements_.empty());
197 switch (elements_.top().node->kind()) {
198 case Node::KIND_PROPERTY:
199 case Node::KIND_LOCALIZED_PROPERTY:
200 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
201 name.equals("value"))
203 handlePropValue(reader, elements_.top().node);
204 return true;
206 break;
207 case Node::KIND_GROUP:
208 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
209 name.equals("prop"))
211 handleProp(reader);
212 return true;
214 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
215 name.equals("node-ref"))
217 handleNodeRef(reader);
218 return true;
220 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
221 name.equals("group"))
223 handleGroup(reader, false);
224 return true;
226 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
227 name.equals("set"))
229 handleSet(reader, false);
230 return true;
232 break;
233 case Node::KIND_SET:
234 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
235 name.equals("item"))
237 handleSetItem(
238 reader,
239 dynamic_cast< SetNode * >(elements_.top().node.get()));
240 return true;
242 break;
243 default: // Node::KIND_LOCALIZED_VALUE
244 assert(false); // this cannot happen
245 break;
247 break;
248 case STATE_COMPONENT_DONE:
249 break;
250 default: // STATE_START
251 assert(false); // this cannot happen
252 break;
255 throw css::uno::RuntimeException(
256 (OUString("bad member <") +
257 name.convertFromUtf8() +
258 OUString("> in ") + reader.getUrl()),
259 css::uno::Reference< css::uno::XInterface >());
262 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
263 if (valueParser_.endElement()) {
264 return;
266 if (ignoring_ > 0) {
267 --ignoring_;
268 } else if (!elements_.empty()) {
269 Element top(elements_.top());
270 elements_.pop();
271 if (top.node.is()) {
272 if (elements_.empty()) {
273 switch (state_) {
274 case STATE_TEMPLATES:
276 NodeMap::iterator i(data_.templates.find(top.name));
277 if (i == data_.templates.end()) {
278 data_.templates.insert(
279 NodeMap::value_type(top.name, top.node));
280 } else {
281 merge(i->second, top.node);
284 break;
285 case STATE_COMPONENT:
287 NodeMap & components = data_.getComponents();
288 NodeMap::iterator i(components.find(top.name));
289 if (i == components.end()) {
290 components.insert(
291 NodeMap::value_type(top.name, top.node));
292 } else {
293 merge(i->second, top.node);
295 state_ = STATE_COMPONENT_DONE;
297 break;
298 default:
299 assert(false);
300 throw css::uno::RuntimeException(
301 OUString("this cannot happen"),
302 css::uno::Reference< css::uno::XInterface >());
304 } else {
305 if (!elements_.top().node->getMembers().insert(
306 NodeMap::value_type(top.name, top.node)).second)
308 throw css::uno::RuntimeException(
309 (OUString("duplicate ") +
310 top.name +
311 OUString(" in ") +
312 reader.getUrl()),
313 css::uno::Reference< css::uno::XInterface >());
317 } else {
318 switch (state_) {
319 case STATE_COMPONENT_SCHEMA:
320 // To support old, broken extensions with .xcs files that contain
321 // empty <component-schema> elements:
322 state_ = STATE_COMPONENT_DONE;
323 break;
324 case STATE_TEMPLATES:
325 state_ = STATE_TEMPLATES_DONE;
326 break;
327 case STATE_TEMPLATES_DONE:
328 throw css::uno::RuntimeException(
329 (OUString("no component element in ") +
330 reader.getUrl()),
331 css::uno::Reference< css::uno::XInterface >());
332 case STATE_COMPONENT_DONE:
333 break;
334 default:
335 assert(false); // this cannot happen
340 void XcsParser::characters(xmlreader::Span const & text) {
341 valueParser_.characters(text);
344 void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
345 //TODO: oor:version, xml:lang attributes
346 OStringBuffer buf;
347 buf.append('.');
348 bool hasPackage = false;
349 bool hasName = false;
350 for (;;) {
351 int attrNsId;
352 xmlreader::Span attrLn;
353 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
354 break;
356 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
358 if (hasPackage) {
359 throw css::uno::RuntimeException(
360 (OUString("multiple component-schema package attributes"
361 " in ") +
362 reader.getUrl()),
363 css::uno::Reference< css::uno::XInterface >());
365 hasPackage = true;
366 xmlreader::Span s(reader.getAttributeValue(false));
367 buf.insert(0, s.begin, s.length);
368 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
369 attrLn.equals("name"))
371 if (hasName) {
372 throw css::uno::RuntimeException(
373 (OUString("multiple component-schema name attributes in ") +
374 reader.getUrl()),
375 css::uno::Reference< css::uno::XInterface >());
377 hasName = true;
378 xmlreader::Span s(reader.getAttributeValue(false));
379 buf.append(s.begin, s.length);
382 if (!hasPackage) {
383 throw css::uno::RuntimeException(
384 (OUString("no component-schema package attribute in ") +
385 reader.getUrl()),
386 css::uno::Reference< css::uno::XInterface >());
388 if (!hasName) {
389 throw css::uno::RuntimeException(
390 (OUString("no component-schema name attribute in ") +
391 reader.getUrl()),
392 css::uno::Reference< css::uno::XInterface >());
394 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
395 convertFromUtf8();
398 void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
399 bool hasName = false;
400 OUString name;
401 OUString component(componentName_);
402 bool hasNodeType = false;
403 OUString nodeType;
404 for (;;) {
405 int attrNsId;
406 xmlreader::Span attrLn;
407 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
408 break;
410 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
411 hasName = true;
412 name = reader.getAttributeValue(false).convertFromUtf8();
413 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
414 attrLn.equals("component"))
416 component = reader.getAttributeValue(false).convertFromUtf8();
417 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
418 attrLn.equals("node-type"))
420 hasNodeType = true;
421 nodeType = reader.getAttributeValue(false).convertFromUtf8();
424 if (!hasName) {
425 throw css::uno::RuntimeException(
426 (OUString("no node-ref name attribute in ") +
427 reader.getUrl()),
428 css::uno::Reference< css::uno::XInterface >());
430 rtl::Reference< Node > tmpl(
431 data_.getTemplate(
432 valueParser_.getLayer(),
433 xmldata::parseTemplateReference(
434 component, hasNodeType, nodeType, 0)));
435 if (!tmpl.is()) {
436 //TODO: this can erroneously happen as long as import/uses attributes
437 // are not correctly processed
438 throw css::uno::RuntimeException(
439 (OUString("unknown node-ref ") +
440 name + OUString(" in ") +
441 reader.getUrl()),
442 css::uno::Reference< css::uno::XInterface >());
444 rtl::Reference< Node > node(tmpl->clone(false));
445 node->setLayer(valueParser_.getLayer());
446 elements_.push(Element(node, name));
449 void XcsParser::handleProp(xmlreader::XmlReader & reader) {
450 bool hasName = false;
451 OUString name;
452 valueParser_.type_ = TYPE_ERROR;
453 bool localized = false;
454 bool nillable = true;
455 for (;;) {
456 int attrNsId;
457 xmlreader::Span attrLn;
458 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
459 break;
461 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
462 hasName = true;
463 name = reader.getAttributeValue(false).convertFromUtf8();
464 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
465 attrLn.equals("type"))
467 valueParser_.type_ = xmldata::parseType(
468 reader, reader.getAttributeValue(true));
469 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
470 attrLn.equals("localized"))
472 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
473 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
474 attrLn.equals("nillable"))
476 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
479 if (!hasName) {
480 throw css::uno::RuntimeException(
481 (OUString("no prop name attribute in ") +
482 reader.getUrl()),
483 css::uno::Reference< css::uno::XInterface >());
485 if (valueParser_.type_ == TYPE_ERROR) {
486 throw css::uno::RuntimeException(
487 (OUString("no prop type attribute in ") +
488 reader.getUrl()),
489 css::uno::Reference< css::uno::XInterface >());
491 elements_.push(
492 Element(
493 (localized
494 ? rtl::Reference< Node >(
495 new LocalizedPropertyNode(
496 valueParser_.getLayer(), valueParser_.type_, nillable))
497 : rtl::Reference< Node >(
498 new PropertyNode(
499 valueParser_.getLayer(), valueParser_.type_, nillable,
500 css::uno::Any(), false))),
501 name));
504 void XcsParser::handlePropValue(
505 xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
507 xmlreader::Span attrSeparator;
508 for (;;) {
509 int attrNsId;
510 xmlreader::Span attrLn;
511 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
512 break;
514 if (attrNsId == ParseManager::NAMESPACE_OOR &&
515 attrLn.equals("separator"))
517 attrSeparator = reader.getAttributeValue(false);
518 if (attrSeparator.length == 0) {
519 throw css::uno::RuntimeException(
520 (OUString("bad oor:separator attribute in ") +
521 reader.getUrl()),
522 css::uno::Reference< css::uno::XInterface >());
526 valueParser_.separator_ = OString(
527 attrSeparator.begin, attrSeparator.length);
528 valueParser_.start(property);
531 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
532 bool hasName = false;
533 OUString name;
534 bool extensible = false;
535 for (;;) {
536 int attrNsId;
537 xmlreader::Span attrLn;
538 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
539 break;
541 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
542 hasName = true;
543 name = reader.getAttributeValue(false).convertFromUtf8();
544 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
545 attrLn.equals("extensible"))
547 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
550 if (!hasName) {
551 throw css::uno::RuntimeException(
552 (OUString("no group name attribute in ") +
553 reader.getUrl()),
554 css::uno::Reference< css::uno::XInterface >());
556 if (isTemplate) {
557 name = Data::fullTemplateName(componentName_, name);
559 elements_.push(
560 Element(
561 new GroupNode(
562 valueParser_.getLayer(), extensible,
563 isTemplate ? name : OUString()),
564 name));
567 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
568 bool hasName = false;
569 OUString name;
570 OUString component(componentName_);
571 bool hasNodeType = false;
572 OUString nodeType;
573 for (;;) {
574 int attrNsId;
575 xmlreader::Span attrLn;
576 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
577 break;
579 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
580 hasName = true;
581 name = reader.getAttributeValue(false).convertFromUtf8();
582 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
583 attrLn.equals("component"))
585 component = reader.getAttributeValue(false).convertFromUtf8();
586 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
587 attrLn.equals("node-type"))
589 hasNodeType = true;
590 nodeType = reader.getAttributeValue(false).convertFromUtf8();
593 if (!hasName) {
594 throw css::uno::RuntimeException(
595 (OUString("no set name attribute in ") +
596 reader.getUrl()),
597 css::uno::Reference< css::uno::XInterface >());
599 if (isTemplate) {
600 name = Data::fullTemplateName(componentName_, name);
602 elements_.push(
603 Element(
604 new SetNode(
605 valueParser_.getLayer(),
606 xmldata::parseTemplateReference(
607 component, hasNodeType, nodeType, 0),
608 isTemplate ? name : OUString()),
609 name));
612 void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
613 OUString component(componentName_);
614 bool hasNodeType = false;
615 OUString nodeType;
616 for (;;) {
617 int attrNsId;
618 xmlreader::Span attrLn;
619 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
620 break;
622 if (attrNsId == ParseManager::NAMESPACE_OOR &&
623 attrLn.equals("component"))
625 component = reader.getAttributeValue(false).convertFromUtf8();
626 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
627 attrLn.equals("node-type"))
629 hasNodeType = true;
630 nodeType = reader.getAttributeValue(false).convertFromUtf8();
633 set->getAdditionalTemplateNames().push_back(
634 xmldata::parseTemplateReference(component, hasNodeType, nodeType, 0));
635 elements_.push(Element(rtl::Reference< Node >(), OUString()));
640 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */