1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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"
40 #include "localizedpropertynode.hxx"
41 #include "groupnode.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"
54 // Conservatively merge a template or component (and its recursive parts) into
55 // an existing instance:
57 rtl::Reference
< Node
> const & original
,
58 rtl::Reference
< Node
> const & update
)
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())
84 } else if (i2
->second
->kind() == i1
->second
->kind()) {
85 merge(i1
->second
, i2
->second
);
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()))
101 } else if (i2
->second
->kind() == i1
->second
->kind() &&
102 (i2
->second
->getTemplateName() ==
103 i1
->second
->getTemplateName()))
105 merge(i1
->second
, i2
->second
);
109 case Node::KIND_ROOT
:
110 assert(false); // this cannot happen
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
)) {
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
;
144 //TODO: ignoring component-schema import, component-schema uses, and
145 // prop constraints; accepting all four at illegal places (and with
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
);
159 case STATE_COMPONENT_SCHEMA
:
160 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
161 name
.equals(RTL_CONSTASCII_STRINGPARAM("templates")))
163 state_
= STATE_TEMPLATES
;
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());
176 valueParser_
.getLayer(), false, rtl::OUString()),
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);
189 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
190 name
.equals(RTL_CONSTASCII_STRINGPARAM("set")))
192 handleSet(reader
, true);
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
);
210 case Node::KIND_GROUP
:
211 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
212 name
.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
217 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
218 name
.equals(RTL_CONSTASCII_STRINGPARAM("node-ref")))
220 handleNodeRef(reader
);
223 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
224 name
.equals(RTL_CONSTASCII_STRINGPARAM("group")))
226 handleGroup(reader
, false);
229 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
230 name
.equals(RTL_CONSTASCII_STRINGPARAM("set")))
232 handleSet(reader
, false);
237 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
238 name
.equals(RTL_CONSTASCII_STRINGPARAM("item")))
242 dynamic_cast< SetNode
* >(elements_
.top().node
.get()));
246 default: // Node::KIND_LOCALIZED_VALUE
247 assert(false); // this cannot happen
251 case STATE_COMPONENT_DONE
:
253 default: // STATE_START
254 assert(false); // this cannot happen
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()) {
271 } else if (!elements_
.empty()) {
272 Element
top(elements_
.top());
275 if (elements_
.empty()) {
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
));
284 merge(i
->second
, top
.node
);
288 case STATE_COMPONENT
:
290 NodeMap
& components
= data_
.getComponents();
291 NodeMap::iterator
i(components
.find(top
.name
));
292 if (i
== components
.end()) {
294 NodeMap::value_type(top
.name
, top
.node
));
296 merge(i
->second
, top
.node
);
298 state_
= STATE_COMPONENT_DONE
;
303 throw css::uno::RuntimeException(
305 RTL_CONSTASCII_USTRINGPARAM("this cannot happen")),
306 css::uno::Reference
< css::uno::XInterface
>());
309 if (!elements_
.top().node
->getMembers().insert(
310 NodeMap::value_type(top
.name
, top
.node
)).second
)
312 throw css::uno::RuntimeException(
314 RTL_CONSTASCII_USTRINGPARAM("duplicate ")) +
316 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
318 css::uno::Reference
< css::uno::XInterface
>());
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
;
329 case STATE_TEMPLATES
:
330 state_
= STATE_TEMPLATES_DONE
;
332 case STATE_TEMPLATES_DONE
:
333 throw css::uno::RuntimeException(
335 RTL_CONSTASCII_USTRINGPARAM("no component element in ")) +
337 css::uno::Reference
< css::uno::XInterface
>());
338 case STATE_COMPONENT_DONE
:
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
;
354 bool hasPackage
= false;
355 bool hasName
= false;
358 xmlreader::Span attrLn
;
359 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
362 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
363 attrLn
.equals(RTL_CONSTASCII_STRINGPARAM("package")))
366 throw css::uno::RuntimeException(
368 RTL_CONSTASCII_USTRINGPARAM(
369 "multiple component-schema package attributes"
372 css::uno::Reference
< css::uno::XInterface
>());
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")))
381 throw css::uno::RuntimeException(
383 RTL_CONSTASCII_USTRINGPARAM(
384 "multiple component-schema name attributes in ")) +
386 css::uno::Reference
< css::uno::XInterface
>());
389 xmlreader::Span
s(reader
.getAttributeValue(false));
390 buf
.append(s
.begin
, s
.length
);
394 throw css::uno::RuntimeException(
396 RTL_CONSTASCII_USTRINGPARAM(
397 "no component-schema package attribute in ")) +
399 css::uno::Reference
< css::uno::XInterface
>());
402 throw css::uno::RuntimeException(
404 RTL_CONSTASCII_USTRINGPARAM(
405 "no component-schema name attribute in ")) +
407 css::uno::Reference
< css::uno::XInterface
>());
409 componentName_
= xmlreader::Span(buf
.getStr(), buf
.getLength()).
413 void XcsParser::handleNodeRef(xmlreader::XmlReader
& reader
) {
414 bool hasName
= false;
416 rtl::OUString
component(componentName_
);
417 bool hasNodeType
= false;
418 rtl::OUString nodeType
;
421 xmlreader::Span attrLn
;
422 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
425 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
426 attrLn
.equals(RTL_CONSTASCII_STRINGPARAM("name")))
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")))
438 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
442 throw css::uno::RuntimeException(
444 RTL_CONSTASCII_USTRINGPARAM("no node-ref name attribute in ")) +
446 css::uno::Reference
< css::uno::XInterface
>());
448 rtl::Reference
< Node
> tmpl(
450 valueParser_
.getLayer(),
451 xmldata::parseTemplateReference(
452 component
, hasNodeType
, nodeType
, 0)));
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 ")) +
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;
470 valueParser_
.type_
= TYPE_ERROR
;
471 bool localized
= false;
472 bool nillable
= true;
475 xmlreader::Span attrLn
;
476 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
479 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
480 attrLn
.equals(RTL_CONSTASCII_STRINGPARAM("name")))
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));
500 throw css::uno::RuntimeException(
502 RTL_CONSTASCII_USTRINGPARAM("no prop name attribute in ")) +
504 css::uno::Reference
< css::uno::XInterface
>());
506 if (valueParser_
.type_
== TYPE_ERROR
) {
507 throw css::uno::RuntimeException(
509 RTL_CONSTASCII_USTRINGPARAM("no prop type attribute in ")) +
511 css::uno::Reference
< css::uno::XInterface
>());
516 ? rtl::Reference
< Node
>(
517 new LocalizedPropertyNode(
518 valueParser_
.getLayer(), valueParser_
.type_
, nillable
))
519 : rtl::Reference
< Node
>(
521 valueParser_
.getLayer(), valueParser_
.type_
, nillable
,
522 css::uno::Any(), false))),
526 void XcsParser::handlePropValue(
527 xmlreader::XmlReader
& reader
, rtl::Reference
< Node
> const & property
)
529 xmlreader::Span attrSeparator
;
532 xmlreader::Span attrLn
;
533 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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(
543 RTL_CONSTASCII_USTRINGPARAM(
544 "bad oor:separator attribute in ")) +
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;
558 bool extensible
= false;
561 xmlreader::Span attrLn
;
562 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
565 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
566 attrLn
.equals(RTL_CONSTASCII_STRINGPARAM("name")))
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));
577 throw css::uno::RuntimeException(
579 RTL_CONSTASCII_USTRINGPARAM("no group name attribute in ")) +
581 css::uno::Reference
< css::uno::XInterface
>());
584 name
= Data::fullTemplateName(componentName_
, name
);
589 valueParser_
.getLayer(), extensible
,
590 isTemplate
? name
: rtl::OUString()),
594 void XcsParser::handleSet(xmlreader::XmlReader
& reader
, bool isTemplate
) {
595 bool hasName
= false;
597 rtl::OUString
component(componentName_
);
598 bool hasNodeType
= false;
599 rtl::OUString nodeType
;
602 xmlreader::Span attrLn
;
603 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
606 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
607 attrLn
.equals(RTL_CONSTASCII_STRINGPARAM("name")))
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")))
619 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
623 throw css::uno::RuntimeException(
625 RTL_CONSTASCII_USTRINGPARAM("no set name attribute in ")) +
627 css::uno::Reference
< css::uno::XInterface
>());
630 name
= Data::fullTemplateName(componentName_
, name
);
635 valueParser_
.getLayer(),
636 xmldata::parseTemplateReference(
637 component
, hasNodeType
, nodeType
, 0),
638 isTemplate
? name
: rtl::OUString()),
642 void XcsParser::handleSetItem(xmlreader::XmlReader
& reader
, SetNode
* set
) {
643 rtl::OUString
component(componentName_
);
644 bool hasNodeType
= false;
645 rtl::OUString nodeType
;
648 xmlreader::Span attrLn
;
649 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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")))
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: */