bump product version to 5.0.4.1
[LibreOffice.git] / configmgr / source / xcsparser.cxx
blob46e04dc10ca5315bfbf087c321f1acfc16352787
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 static_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 (static_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), ignoring_()
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(valueParser_.getLayer(), false, ""),
173 componentName_));
174 return true;
176 break;
177 case STATE_TEMPLATES:
178 if (elements_.empty()) {
179 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
180 name.equals("group"))
182 handleGroup(reader, true);
183 return true;
185 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
186 name.equals("set"))
188 handleSet(reader, true);
189 return true;
191 break;
193 // fall through
194 case STATE_COMPONENT:
195 assert(!elements_.empty());
196 switch (elements_.top().node->kind()) {
197 case Node::KIND_PROPERTY:
198 case Node::KIND_LOCALIZED_PROPERTY:
199 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
200 name.equals("value"))
202 handlePropValue(reader, elements_.top().node);
203 return true;
205 break;
206 case Node::KIND_GROUP:
207 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
208 name.equals("prop"))
210 handleProp(reader);
211 return true;
213 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
214 name.equals("node-ref"))
216 handleNodeRef(reader);
217 return true;
219 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
220 name.equals("group"))
222 handleGroup(reader, false);
223 return true;
225 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
226 name.equals("set"))
228 handleSet(reader, false);
229 return true;
231 break;
232 case Node::KIND_SET:
233 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
234 name.equals("item"))
236 handleSetItem(
237 reader,
238 static_cast< SetNode * >(elements_.top().node.get()));
239 return true;
241 break;
242 default: // Node::KIND_LOCALIZED_VALUE
243 assert(false); // this cannot happen
244 break;
246 break;
247 case STATE_COMPONENT_DONE:
248 break;
249 default: // STATE_START
250 assert(false); // this cannot happen
251 break;
254 throw css::uno::RuntimeException(
255 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
258 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
259 if (valueParser_.endElement()) {
260 return;
262 if (ignoring_ > 0) {
263 --ignoring_;
264 } else if (!elements_.empty()) {
265 Element top(elements_.top());
266 elements_.pop();
267 if (top.node.is()) {
268 if (elements_.empty()) {
269 switch (state_) {
270 case STATE_TEMPLATES:
272 NodeMap::iterator i(data_.templates.find(top.name));
273 if (i == data_.templates.end()) {
274 data_.templates.insert(
275 NodeMap::value_type(top.name, top.node));
276 } else {
277 merge(i->second, top.node);
280 break;
281 case STATE_COMPONENT:
283 NodeMap & components = data_.getComponents();
284 NodeMap::iterator i(components.find(top.name));
285 if (i == components.end()) {
286 components.insert(
287 NodeMap::value_type(top.name, top.node));
288 } else {
289 merge(i->second, top.node);
291 state_ = STATE_COMPONENT_DONE;
293 break;
294 default:
295 assert(false);
296 throw css::uno::RuntimeException(
297 "this cannot happen");
299 } else {
300 if (!elements_.top().node->getMembers().insert(
301 NodeMap::value_type(top.name, top.node)).second)
303 throw css::uno::RuntimeException(
304 "duplicate " + top.name + " in " + reader.getUrl());
308 } else {
309 switch (state_) {
310 case STATE_COMPONENT_SCHEMA:
311 // To support old, broken extensions with .xcs files that contain
312 // empty <component-schema> elements:
313 state_ = STATE_COMPONENT_DONE;
314 break;
315 case STATE_TEMPLATES:
316 state_ = STATE_TEMPLATES_DONE;
317 break;
318 case STATE_TEMPLATES_DONE:
319 throw css::uno::RuntimeException(
320 "no component element in " + reader.getUrl());
321 case STATE_COMPONENT_DONE:
322 break;
323 default:
324 assert(false); // this cannot happen
329 void XcsParser::characters(xmlreader::Span const & text) {
330 valueParser_.characters(text);
333 void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
334 //TODO: oor:version, xml:lang attributes
335 OStringBuffer buf;
336 buf.append('.');
337 bool hasPackage = false;
338 bool hasName = false;
339 for (;;) {
340 int attrNsId;
341 xmlreader::Span attrLn;
342 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
343 break;
345 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
347 if (hasPackage) {
348 throw css::uno::RuntimeException(
349 "multiple component-schema package attributes in " +
350 reader.getUrl());
352 hasPackage = true;
353 xmlreader::Span s(reader.getAttributeValue(false));
354 buf.insert(0, s.begin, s.length);
355 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
356 attrLn.equals("name"))
358 if (hasName) {
359 throw css::uno::RuntimeException(
360 "multiple component-schema name attributes in " +
361 reader.getUrl());
363 hasName = true;
364 xmlreader::Span s(reader.getAttributeValue(false));
365 buf.append(s.begin, s.length);
368 if (!hasPackage) {
369 throw css::uno::RuntimeException(
370 "no component-schema package attribute in " + reader.getUrl());
372 if (!hasName) {
373 throw css::uno::RuntimeException(
374 "no component-schema name attribute in " + reader.getUrl());
376 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
377 convertFromUtf8();
380 void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
381 bool hasName = false;
382 OUString name;
383 OUString component(componentName_);
384 bool hasNodeType = false;
385 OUString nodeType;
386 for (;;) {
387 int attrNsId;
388 xmlreader::Span attrLn;
389 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
390 break;
392 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
393 hasName = true;
394 name = reader.getAttributeValue(false).convertFromUtf8();
395 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
396 attrLn.equals("component"))
398 component = reader.getAttributeValue(false).convertFromUtf8();
399 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
400 attrLn.equals("node-type"))
402 hasNodeType = true;
403 nodeType = reader.getAttributeValue(false).convertFromUtf8();
406 if (!hasName) {
407 throw css::uno::RuntimeException(
408 "no node-ref name attribute in " + reader.getUrl());
410 rtl::Reference< Node > tmpl(
411 data_.getTemplate(
412 valueParser_.getLayer(),
413 xmldata::parseTemplateReference(
414 component, hasNodeType, nodeType, 0)));
415 if (!tmpl.is()) {
416 //TODO: this can erroneously happen as long as import/uses attributes
417 // are not correctly processed
418 throw css::uno::RuntimeException(
419 "unknown node-ref " + name + " in " + reader.getUrl());
421 rtl::Reference< Node > node(tmpl->clone(false));
422 node->setLayer(valueParser_.getLayer());
423 elements_.push(Element(node, name));
426 void XcsParser::handleProp(xmlreader::XmlReader & reader) {
427 bool hasName = false;
428 OUString name;
429 valueParser_.type_ = TYPE_ERROR;
430 bool localized = false;
431 bool nillable = true;
432 for (;;) {
433 int attrNsId;
434 xmlreader::Span attrLn;
435 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
436 break;
438 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
439 hasName = true;
440 name = reader.getAttributeValue(false).convertFromUtf8();
441 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
442 attrLn.equals("type"))
444 valueParser_.type_ = xmldata::parseType(
445 reader, reader.getAttributeValue(true));
446 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
447 attrLn.equals("localized"))
449 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
450 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
451 attrLn.equals("nillable"))
453 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
456 if (!hasName) {
457 throw css::uno::RuntimeException(
458 "no prop name attribute in " + reader.getUrl());
460 if (valueParser_.type_ == TYPE_ERROR) {
461 throw css::uno::RuntimeException(
462 "no prop type attribute in " + reader.getUrl());
464 elements_.push(
465 Element(
466 (localized
467 ? rtl::Reference< Node >(
468 new LocalizedPropertyNode(
469 valueParser_.getLayer(), valueParser_.type_, nillable))
470 : rtl::Reference< Node >(
471 new PropertyNode(
472 valueParser_.getLayer(), valueParser_.type_, nillable,
473 css::uno::Any(), false))),
474 name));
477 void XcsParser::handlePropValue(
478 xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
480 xmlreader::Span attrSeparator;
481 for (;;) {
482 int attrNsId;
483 xmlreader::Span attrLn;
484 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
485 break;
487 if (attrNsId == ParseManager::NAMESPACE_OOR &&
488 attrLn.equals("separator"))
490 attrSeparator = reader.getAttributeValue(false);
491 if (attrSeparator.length == 0) {
492 throw css::uno::RuntimeException(
493 "bad oor:separator attribute in " + reader.getUrl());
497 valueParser_.separator_ = OString(
498 attrSeparator.begin, attrSeparator.length);
499 valueParser_.start(property);
502 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
503 bool hasName = false;
504 OUString name;
505 bool extensible = false;
506 for (;;) {
507 int attrNsId;
508 xmlreader::Span attrLn;
509 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
510 break;
512 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
513 hasName = true;
514 name = reader.getAttributeValue(false).convertFromUtf8();
515 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
516 attrLn.equals("extensible"))
518 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
521 if (!hasName) {
522 throw css::uno::RuntimeException(
523 "no group name attribute in " + reader.getUrl());
525 if (isTemplate) {
526 name = Data::fullTemplateName(componentName_, name);
528 elements_.push(
529 Element(
530 new GroupNode(
531 valueParser_.getLayer(), extensible,
532 isTemplate ? name : OUString()),
533 name));
536 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
537 bool hasName = false;
538 OUString name;
539 OUString component(componentName_);
540 bool hasNodeType = false;
541 OUString nodeType;
542 for (;;) {
543 int attrNsId;
544 xmlreader::Span attrLn;
545 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
546 break;
548 if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
549 hasName = true;
550 name = reader.getAttributeValue(false).convertFromUtf8();
551 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
552 attrLn.equals("component"))
554 component = reader.getAttributeValue(false).convertFromUtf8();
555 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
556 attrLn.equals("node-type"))
558 hasNodeType = true;
559 nodeType = reader.getAttributeValue(false).convertFromUtf8();
562 if (!hasName) {
563 throw css::uno::RuntimeException(
564 "no set name attribute in " + reader.getUrl());
566 if (isTemplate) {
567 name = Data::fullTemplateName(componentName_, name);
569 elements_.push(
570 Element(
571 new SetNode(
572 valueParser_.getLayer(),
573 xmldata::parseTemplateReference(
574 component, hasNodeType, nodeType, 0),
575 isTemplate ? name : OUString()),
576 name));
579 void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
580 OUString component(componentName_);
581 bool hasNodeType = false;
582 OUString nodeType;
583 for (;;) {
584 int attrNsId;
585 xmlreader::Span attrLn;
586 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
587 break;
589 if (attrNsId == ParseManager::NAMESPACE_OOR &&
590 attrLn.equals("component"))
592 component = reader.getAttributeValue(false).convertFromUtf8();
593 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
594 attrLn.equals("node-type"))
596 hasNodeType = true;
597 nodeType = reader.getAttributeValue(false).convertFromUtf8();
600 set->getAdditionalTemplateNames().push_back(
601 xmldata::parseTemplateReference(component, hasNodeType, nodeType, 0));
602 elements_.push(Element(rtl::Reference< Node >(), ""));
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */