Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / configmgr / source / xcsparser.cxx
blob1e6f914a3fbf6072095a39f0c23208584798f045
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.h"
33 #include "rtl/string.hxx"
34 #include "rtl/ustring.h"
35 #include "rtl/ustring.hxx"
36 #include "xmlreader/span.hxx"
37 #include "xmlreader/xmlreader.hxx"
39 #include "data.hxx"
40 #include "localizedpropertynode.hxx"
41 #include "groupnode.hxx"
42 #include "node.hxx"
43 #include "nodemap.hxx"
44 #include "parsemanager.hxx"
45 #include "propertynode.hxx"
46 #include "setnode.hxx"
47 #include "xcsparser.hxx"
48 #include "xmldata.hxx"
50 namespace configmgr {
52 namespace {
54 // Conservatively merge a template or component (and its recursive parts) into
55 // an existing instance:
56 void merge(
57 rtl::Reference< Node > const & original,
58 rtl::Reference< Node > const & update)
60 assert(
61 original.is() && update.is() && original->kind() == update->kind() &&
62 update->getFinalized() == Data::NO_LAYER);
63 if (update->getLayer() >= original->getLayer() &&
64 update->getLayer() <= original->getFinalized())
66 switch (original->kind()) {
67 case Node::KIND_PROPERTY:
68 case Node::KIND_LOCALIZED_PROPERTY:
69 case Node::KIND_LOCALIZED_VALUE:
70 break; //TODO: merge certain parts?
71 case Node::KIND_GROUP:
72 for (NodeMap::const_iterator i2(update->getMembers().begin());
73 i2 != update->getMembers().end(); ++i2)
75 NodeMap & members = original->getMembers();
76 NodeMap::iterator i1(members.find(i2->first));
77 if (i1 == members.end()) {
78 if (i2->second->kind() == Node::KIND_PROPERTY &&
79 dynamic_cast< GroupNode * >(
80 original.get())->isExtensible())
82 members.insert(*i2);
84 } else if (i2->second->kind() == i1->second->kind()) {
85 merge(i1->second, i2->second);
88 break;
89 case Node::KIND_SET:
90 for (NodeMap::const_iterator i2(update->getMembers().begin());
91 i2 != update->getMembers().end(); ++i2)
93 NodeMap & members = original->getMembers();
94 NodeMap::iterator i1(members.find(i2->first));
95 if (i1 == members.end()) {
96 if (dynamic_cast< SetNode * >(original.get())->
97 isValidTemplate(i2->second->getTemplateName()))
99 members.insert(*i2);
101 } else if (i2->second->kind() == i1->second->kind() &&
102 (i2->second->getTemplateName() ==
103 i1->second->getTemplateName()))
105 merge(i1->second, i2->second);
108 break;
109 case Node::KIND_ROOT:
110 assert(false); // this cannot happen
111 break;
118 XcsParser::XcsParser(int layer, Data & data):
119 valueParser_(layer), data_(data), state_(STATE_START)
122 XcsParser::~XcsParser() {}
124 xmlreader::XmlReader::Text XcsParser::getTextMode() {
125 return valueParser_.getTextMode();
128 bool XcsParser::startElement(
129 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
130 std::set< OUString > const * existingDependencies)
132 if (valueParser_.startElement(reader, nsId, name, existingDependencies)) {
133 return true;
135 if (state_ == STATE_START) {
136 if (nsId == ParseManager::NAMESPACE_OOR &&
137 name.equals(RTL_CONSTASCII_STRINGPARAM("component-schema"))) {
138 handleComponentSchema(reader);
139 state_ = STATE_COMPONENT_SCHEMA;
140 ignoring_ = 0;
141 return true;
143 } else {
144 //TODO: ignoring component-schema import, component-schema uses, and
145 // prop constraints; accepting all four at illegal places (and with
146 // illegal content):
147 if (ignoring_ > 0 ||
148 (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
149 (name.equals(RTL_CONSTASCII_STRINGPARAM("info")) ||
150 name.equals(RTL_CONSTASCII_STRINGPARAM("import")) ||
151 name.equals(RTL_CONSTASCII_STRINGPARAM("uses")) ||
152 name.equals(RTL_CONSTASCII_STRINGPARAM("constraints")))))
154 assert(ignoring_ < LONG_MAX);
155 ++ignoring_;
156 return true;
158 switch (state_) {
159 case STATE_COMPONENT_SCHEMA:
160 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
161 name.equals(RTL_CONSTASCII_STRINGPARAM("templates")))
163 state_ = STATE_TEMPLATES;
164 return true;
166 // fall through
167 case STATE_TEMPLATES_DONE:
168 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
169 name.equals(RTL_CONSTASCII_STRINGPARAM("component")))
171 state_ = STATE_COMPONENT;
172 assert(elements_.empty());
173 elements_.push(
174 Element(
175 new GroupNode(
176 valueParser_.getLayer(), false, rtl::OUString()),
177 componentName_));
178 return true;
180 break;
181 case STATE_TEMPLATES:
182 if (elements_.empty()) {
183 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
184 name.equals(RTL_CONSTASCII_STRINGPARAM("group")))
186 handleGroup(reader, true);
187 return true;
189 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
190 name.equals(RTL_CONSTASCII_STRINGPARAM("set")))
192 handleSet(reader, true);
193 return true;
195 break;
197 // fall through
198 case STATE_COMPONENT:
199 assert(!elements_.empty());
200 switch (elements_.top().node->kind()) {
201 case Node::KIND_PROPERTY:
202 case Node::KIND_LOCALIZED_PROPERTY:
203 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
204 name.equals(RTL_CONSTASCII_STRINGPARAM("value")))
206 handlePropValue(reader, elements_.top().node);
207 return true;
209 break;
210 case Node::KIND_GROUP:
211 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
212 name.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
214 handleProp(reader);
215 return true;
217 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
218 name.equals(RTL_CONSTASCII_STRINGPARAM("node-ref")))
220 handleNodeRef(reader);
221 return true;
223 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
224 name.equals(RTL_CONSTASCII_STRINGPARAM("group")))
226 handleGroup(reader, false);
227 return true;
229 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
230 name.equals(RTL_CONSTASCII_STRINGPARAM("set")))
232 handleSet(reader, false);
233 return true;
235 break;
236 case Node::KIND_SET:
237 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
238 name.equals(RTL_CONSTASCII_STRINGPARAM("item")))
240 handleSetItem(
241 reader,
242 dynamic_cast< SetNode * >(elements_.top().node.get()));
243 return true;
245 break;
246 default: // Node::KIND_LOCALIZED_VALUE
247 assert(false); // this cannot happen
248 break;
250 break;
251 case STATE_COMPONENT_DONE:
252 break;
253 default: // STATE_START
254 assert(false); // this cannot happen
255 break;
258 throw css::uno::RuntimeException(
259 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) +
260 name.convertFromUtf8() +
261 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) + reader.getUrl()),
262 css::uno::Reference< css::uno::XInterface >());
265 void XcsParser::endElement(xmlreader::XmlReader const & reader) {
266 if (valueParser_.endElement()) {
267 return;
269 if (ignoring_ > 0) {
270 --ignoring_;
271 } else if (!elements_.empty()) {
272 Element top(elements_.top());
273 elements_.pop();
274 if (top.node.is()) {
275 if (elements_.empty()) {
276 switch (state_) {
277 case STATE_TEMPLATES:
279 NodeMap::iterator i(data_.templates.find(top.name));
280 if (i == data_.templates.end()) {
281 data_.templates.insert(
282 NodeMap::value_type(top.name, top.node));
283 } else {
284 merge(i->second, top.node);
287 break;
288 case STATE_COMPONENT:
290 NodeMap & components = data_.getComponents();
291 NodeMap::iterator i(components.find(top.name));
292 if (i == components.end()) {
293 components.insert(
294 NodeMap::value_type(top.name, top.node));
295 } else {
296 merge(i->second, top.node);
298 state_ = STATE_COMPONENT_DONE;
300 break;
301 default:
302 assert(false);
303 throw css::uno::RuntimeException(
304 rtl::OUString(
305 RTL_CONSTASCII_USTRINGPARAM("this cannot happen")),
306 css::uno::Reference< css::uno::XInterface >());
308 } else {
309 if (!elements_.top().node->getMembers().insert(
310 NodeMap::value_type(top.name, top.node)).second)
312 throw css::uno::RuntimeException(
313 (rtl::OUString(
314 RTL_CONSTASCII_USTRINGPARAM("duplicate ")) +
315 top.name +
316 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
317 reader.getUrl()),
318 css::uno::Reference< css::uno::XInterface >());
322 } else {
323 switch (state_) {
324 case STATE_COMPONENT_SCHEMA:
325 // To support old, broken extensions with .xcs files that contain
326 // empty <component-schema> elements:
327 state_ = STATE_COMPONENT_DONE;
328 break;
329 case STATE_TEMPLATES:
330 state_ = STATE_TEMPLATES_DONE;
331 break;
332 case STATE_TEMPLATES_DONE:
333 throw css::uno::RuntimeException(
334 (rtl::OUString(
335 RTL_CONSTASCII_USTRINGPARAM("no component element in ")) +
336 reader.getUrl()),
337 css::uno::Reference< css::uno::XInterface >());
338 case STATE_COMPONENT_DONE:
339 break;
340 default:
341 assert(false); // this cannot happen
346 void XcsParser::characters(xmlreader::Span const & text) {
347 valueParser_.characters(text);
350 void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
351 //TODO: oor:version, xml:lang attributes
352 rtl::OStringBuffer buf;
353 buf.append('.');
354 bool hasPackage = false;
355 bool hasName = false;
356 for (;;) {
357 int attrNsId;
358 xmlreader::Span attrLn;
359 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
360 break;
362 if (attrNsId == ParseManager::NAMESPACE_OOR &&
363 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("package")))
365 if (hasPackage) {
366 throw css::uno::RuntimeException(
367 (rtl::OUString(
368 RTL_CONSTASCII_USTRINGPARAM(
369 "multiple component-schema package attributes"
370 " in ")) +
371 reader.getUrl()),
372 css::uno::Reference< css::uno::XInterface >());
374 hasPackage = true;
375 xmlreader::Span s(reader.getAttributeValue(false));
376 buf.insert(0, s.begin, s.length);
377 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
378 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
380 if (hasName) {
381 throw css::uno::RuntimeException(
382 (rtl::OUString(
383 RTL_CONSTASCII_USTRINGPARAM(
384 "multiple component-schema name attributes in ")) +
385 reader.getUrl()),
386 css::uno::Reference< css::uno::XInterface >());
388 hasName = true;
389 xmlreader::Span s(reader.getAttributeValue(false));
390 buf.append(s.begin, s.length);
393 if (!hasPackage) {
394 throw css::uno::RuntimeException(
395 (rtl::OUString(
396 RTL_CONSTASCII_USTRINGPARAM(
397 "no component-schema package attribute in ")) +
398 reader.getUrl()),
399 css::uno::Reference< css::uno::XInterface >());
401 if (!hasName) {
402 throw css::uno::RuntimeException(
403 (rtl::OUString(
404 RTL_CONSTASCII_USTRINGPARAM(
405 "no component-schema name attribute in ")) +
406 reader.getUrl()),
407 css::uno::Reference< css::uno::XInterface >());
409 componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
410 convertFromUtf8();
413 void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
414 bool hasName = false;
415 rtl::OUString name;
416 rtl::OUString component(componentName_);
417 bool hasNodeType = false;
418 rtl::OUString nodeType;
419 for (;;) {
420 int attrNsId;
421 xmlreader::Span attrLn;
422 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
423 break;
425 if (attrNsId == ParseManager::NAMESPACE_OOR &&
426 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
428 hasName = true;
429 name = reader.getAttributeValue(false).convertFromUtf8();
430 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
431 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("component")))
433 component = reader.getAttributeValue(false).convertFromUtf8();
434 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
435 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("node-type")))
437 hasNodeType = true;
438 nodeType = reader.getAttributeValue(false).convertFromUtf8();
441 if (!hasName) {
442 throw css::uno::RuntimeException(
443 (rtl::OUString(
444 RTL_CONSTASCII_USTRINGPARAM("no node-ref name attribute in ")) +
445 reader.getUrl()),
446 css::uno::Reference< css::uno::XInterface >());
448 rtl::Reference< Node > tmpl(
449 data_.getTemplate(
450 valueParser_.getLayer(),
451 xmldata::parseTemplateReference(
452 component, hasNodeType, nodeType, 0)));
453 if (!tmpl.is()) {
454 //TODO: this can erroneously happen as long as import/uses attributes
455 // are not correctly processed
456 throw css::uno::RuntimeException(
457 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("unknown node-ref ")) +
458 name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
459 reader.getUrl()),
460 css::uno::Reference< css::uno::XInterface >());
462 rtl::Reference< Node > node(tmpl->clone(false));
463 node->setLayer(valueParser_.getLayer());
464 elements_.push(Element(node, name));
467 void XcsParser::handleProp(xmlreader::XmlReader & reader) {
468 bool hasName = false;
469 rtl::OUString name;
470 valueParser_.type_ = TYPE_ERROR;
471 bool localized = false;
472 bool nillable = true;
473 for (;;) {
474 int attrNsId;
475 xmlreader::Span attrLn;
476 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
477 break;
479 if (attrNsId == ParseManager::NAMESPACE_OOR &&
480 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
482 hasName = true;
483 name = reader.getAttributeValue(false).convertFromUtf8();
484 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
485 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
487 valueParser_.type_ = xmldata::parseType(
488 reader, reader.getAttributeValue(true));
489 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
490 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("localized")))
492 localized = xmldata::parseBoolean(reader.getAttributeValue(true));
493 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
494 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("nillable")))
496 nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
499 if (!hasName) {
500 throw css::uno::RuntimeException(
501 (rtl::OUString(
502 RTL_CONSTASCII_USTRINGPARAM("no prop name attribute in ")) +
503 reader.getUrl()),
504 css::uno::Reference< css::uno::XInterface >());
506 if (valueParser_.type_ == TYPE_ERROR) {
507 throw css::uno::RuntimeException(
508 (rtl::OUString(
509 RTL_CONSTASCII_USTRINGPARAM("no prop type attribute in ")) +
510 reader.getUrl()),
511 css::uno::Reference< css::uno::XInterface >());
513 elements_.push(
514 Element(
515 (localized
516 ? rtl::Reference< Node >(
517 new LocalizedPropertyNode(
518 valueParser_.getLayer(), valueParser_.type_, nillable))
519 : rtl::Reference< Node >(
520 new PropertyNode(
521 valueParser_.getLayer(), valueParser_.type_, nillable,
522 css::uno::Any(), false))),
523 name));
526 void XcsParser::handlePropValue(
527 xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
529 xmlreader::Span attrSeparator;
530 for (;;) {
531 int attrNsId;
532 xmlreader::Span attrLn;
533 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
534 break;
536 if (attrNsId == ParseManager::NAMESPACE_OOR &&
537 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("separator")))
539 attrSeparator = reader.getAttributeValue(false);
540 if (attrSeparator.length == 0) {
541 throw css::uno::RuntimeException(
542 (rtl::OUString(
543 RTL_CONSTASCII_USTRINGPARAM(
544 "bad oor:separator attribute in ")) +
545 reader.getUrl()),
546 css::uno::Reference< css::uno::XInterface >());
550 valueParser_.separator_ = rtl::OString(
551 attrSeparator.begin, attrSeparator.length);
552 valueParser_.start(property);
555 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
556 bool hasName = false;
557 rtl::OUString name;
558 bool extensible = false;
559 for (;;) {
560 int attrNsId;
561 xmlreader::Span attrLn;
562 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
563 break;
565 if (attrNsId == ParseManager::NAMESPACE_OOR &&
566 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
568 hasName = true;
569 name = reader.getAttributeValue(false).convertFromUtf8();
570 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
571 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("extensible")))
573 extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
576 if (!hasName) {
577 throw css::uno::RuntimeException(
578 (rtl::OUString(
579 RTL_CONSTASCII_USTRINGPARAM("no group name attribute in ")) +
580 reader.getUrl()),
581 css::uno::Reference< css::uno::XInterface >());
583 if (isTemplate) {
584 name = Data::fullTemplateName(componentName_, name);
586 elements_.push(
587 Element(
588 new GroupNode(
589 valueParser_.getLayer(), extensible,
590 isTemplate ? name : rtl::OUString()),
591 name));
594 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
595 bool hasName = false;
596 rtl::OUString name;
597 rtl::OUString component(componentName_);
598 bool hasNodeType = false;
599 rtl::OUString nodeType;
600 for (;;) {
601 int attrNsId;
602 xmlreader::Span attrLn;
603 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
604 break;
606 if (attrNsId == ParseManager::NAMESPACE_OOR &&
607 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
609 hasName = true;
610 name = reader.getAttributeValue(false).convertFromUtf8();
611 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
612 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("component")))
614 component = reader.getAttributeValue(false).convertFromUtf8();
615 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
616 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("node-type")))
618 hasNodeType = true;
619 nodeType = reader.getAttributeValue(false).convertFromUtf8();
622 if (!hasName) {
623 throw css::uno::RuntimeException(
624 (rtl::OUString(
625 RTL_CONSTASCII_USTRINGPARAM("no set name attribute in ")) +
626 reader.getUrl()),
627 css::uno::Reference< css::uno::XInterface >());
629 if (isTemplate) {
630 name = Data::fullTemplateName(componentName_, name);
632 elements_.push(
633 Element(
634 new SetNode(
635 valueParser_.getLayer(),
636 xmldata::parseTemplateReference(
637 component, hasNodeType, nodeType, 0),
638 isTemplate ? name : rtl::OUString()),
639 name));
642 void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
643 rtl::OUString component(componentName_);
644 bool hasNodeType = false;
645 rtl::OUString nodeType;
646 for (;;) {
647 int attrNsId;
648 xmlreader::Span attrLn;
649 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
650 break;
652 if (attrNsId == ParseManager::NAMESPACE_OOR &&
653 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("component")))
655 component = reader.getAttributeValue(false).convertFromUtf8();
656 } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
657 attrLn.equals(RTL_CONSTASCII_STRINGPARAM("node-type")))
659 hasNodeType = true;
660 nodeType = reader.getAttributeValue(false).convertFromUtf8();
663 set->getAdditionalTemplateNames().push_back(
664 xmldata::parseTemplateReference(component, hasNodeType, nodeType, 0));
665 elements_.push(Element(rtl::Reference< Node >(), rtl::OUString()));
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */