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.hxx"
33 #include "rtl/ustring.hxx"
34 #include "xmlreader/span.hxx"
35 #include "xmlreader/xmlreader.hxx"
38 #include "localizedpropertynode.hxx"
39 #include "groupnode.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"
52 // Conservatively merge a template or component (and its recursive parts) into
53 // an existing instance:
55 rtl::Reference
< Node
> const & original
,
56 rtl::Reference
< Node
> const & update
)
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())
82 } else if (i2
->second
->kind() == i1
->second
->kind()) {
83 merge(i1
->second
, i2
->second
);
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()))
99 } else if (i2
->second
->kind() == i1
->second
->kind() &&
100 (i2
->second
->getTemplateName() ==
101 i1
->second
->getTemplateName()))
103 merge(i1
->second
, i2
->second
);
107 case Node::KIND_ROOT
:
108 assert(false); // this cannot happen
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
)) {
133 if (state_
== STATE_START
) {
134 if (nsId
== ParseManager::NAMESPACE_OOR
&&
135 name
.equals("component-schema"))
137 handleComponentSchema(reader
);
138 state_
= STATE_COMPONENT_SCHEMA
;
143 //TODO: ignoring component-schema import, component-schema uses, and
144 // prop constraints; accepting all four at illegal places (and with
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
);
156 case STATE_COMPONENT_SCHEMA
:
157 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
158 name
.equals("templates"))
160 state_
= STATE_TEMPLATES
;
164 case STATE_TEMPLATES_DONE
:
165 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
166 name
.equals("component"))
168 state_
= STATE_COMPONENT
;
169 assert(elements_
.empty());
173 valueParser_
.getLayer(), false, OUString()),
178 case STATE_TEMPLATES
:
179 if (elements_
.empty()) {
180 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
181 name
.equals("group"))
183 handleGroup(reader
, true);
186 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
189 handleSet(reader
, true);
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
);
207 case Node::KIND_GROUP
:
208 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
214 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
215 name
.equals("node-ref"))
217 handleNodeRef(reader
);
220 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
221 name
.equals("group"))
223 handleGroup(reader
, false);
226 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
229 handleSet(reader
, false);
234 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
239 dynamic_cast< SetNode
* >(elements_
.top().node
.get()));
243 default: // Node::KIND_LOCALIZED_VALUE
244 assert(false); // this cannot happen
248 case STATE_COMPONENT_DONE
:
250 default: // STATE_START
251 assert(false); // this cannot happen
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()) {
268 } else if (!elements_
.empty()) {
269 Element
top(elements_
.top());
272 if (elements_
.empty()) {
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
));
281 merge(i
->second
, top
.node
);
285 case STATE_COMPONENT
:
287 NodeMap
& components
= data_
.getComponents();
288 NodeMap::iterator
i(components
.find(top
.name
));
289 if (i
== components
.end()) {
291 NodeMap::value_type(top
.name
, top
.node
));
293 merge(i
->second
, top
.node
);
295 state_
= STATE_COMPONENT_DONE
;
300 throw css::uno::RuntimeException(
301 OUString("this cannot happen"),
302 css::uno::Reference
< css::uno::XInterface
>());
305 if (!elements_
.top().node
->getMembers().insert(
306 NodeMap::value_type(top
.name
, top
.node
)).second
)
308 throw css::uno::RuntimeException(
309 (OUString("duplicate ") +
313 css::uno::Reference
< css::uno::XInterface
>());
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
;
324 case STATE_TEMPLATES
:
325 state_
= STATE_TEMPLATES_DONE
;
327 case STATE_TEMPLATES_DONE
:
328 throw css::uno::RuntimeException(
329 (OUString("no component element in ") +
331 css::uno::Reference
< css::uno::XInterface
>());
332 case STATE_COMPONENT_DONE
:
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
348 bool hasPackage
= false;
349 bool hasName
= false;
352 xmlreader::Span attrLn
;
353 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
356 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("package"))
359 throw css::uno::RuntimeException(
360 (OUString("multiple component-schema package attributes"
363 css::uno::Reference
< css::uno::XInterface
>());
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"))
372 throw css::uno::RuntimeException(
373 (OUString("multiple component-schema name attributes in ") +
375 css::uno::Reference
< css::uno::XInterface
>());
378 xmlreader::Span
s(reader
.getAttributeValue(false));
379 buf
.append(s
.begin
, s
.length
);
383 throw css::uno::RuntimeException(
384 (OUString("no component-schema package attribute in ") +
386 css::uno::Reference
< css::uno::XInterface
>());
389 throw css::uno::RuntimeException(
390 (OUString("no component-schema name attribute in ") +
392 css::uno::Reference
< css::uno::XInterface
>());
394 componentName_
= xmlreader::Span(buf
.getStr(), buf
.getLength()).
398 void XcsParser::handleNodeRef(xmlreader::XmlReader
& reader
) {
399 bool hasName
= false;
401 OUString
component(componentName_
);
402 bool hasNodeType
= false;
406 xmlreader::Span attrLn
;
407 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
410 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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"))
421 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
425 throw css::uno::RuntimeException(
426 (OUString("no node-ref name attribute in ") +
428 css::uno::Reference
< css::uno::XInterface
>());
430 rtl::Reference
< Node
> tmpl(
432 valueParser_
.getLayer(),
433 xmldata::parseTemplateReference(
434 component
, hasNodeType
, nodeType
, 0)));
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 ") +
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;
452 valueParser_
.type_
= TYPE_ERROR
;
453 bool localized
= false;
454 bool nillable
= true;
457 xmlreader::Span attrLn
;
458 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
461 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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));
480 throw css::uno::RuntimeException(
481 (OUString("no prop name attribute in ") +
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 ") +
489 css::uno::Reference
< css::uno::XInterface
>());
494 ? rtl::Reference
< Node
>(
495 new LocalizedPropertyNode(
496 valueParser_
.getLayer(), valueParser_
.type_
, nillable
))
497 : rtl::Reference
< Node
>(
499 valueParser_
.getLayer(), valueParser_
.type_
, nillable
,
500 css::uno::Any(), false))),
504 void XcsParser::handlePropValue(
505 xmlreader::XmlReader
& reader
, rtl::Reference
< Node
> const & property
)
507 xmlreader::Span attrSeparator
;
510 xmlreader::Span attrLn
;
511 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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 ") +
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;
534 bool extensible
= false;
537 xmlreader::Span attrLn
;
538 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
541 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
543 name
= reader
.getAttributeValue(false).convertFromUtf8();
544 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
545 attrLn
.equals("extensible"))
547 extensible
= xmldata::parseBoolean(reader
.getAttributeValue(true));
551 throw css::uno::RuntimeException(
552 (OUString("no group name attribute in ") +
554 css::uno::Reference
< css::uno::XInterface
>());
557 name
= Data::fullTemplateName(componentName_
, name
);
562 valueParser_
.getLayer(), extensible
,
563 isTemplate
? name
: OUString()),
567 void XcsParser::handleSet(xmlreader::XmlReader
& reader
, bool isTemplate
) {
568 bool hasName
= false;
570 OUString
component(componentName_
);
571 bool hasNodeType
= false;
575 xmlreader::Span attrLn
;
576 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
579 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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"))
590 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
594 throw css::uno::RuntimeException(
595 (OUString("no set name attribute in ") +
597 css::uno::Reference
< css::uno::XInterface
>());
600 name
= Data::fullTemplateName(componentName_
, name
);
605 valueParser_
.getLayer(),
606 xmldata::parseTemplateReference(
607 component
, hasNodeType
, nodeType
, 0),
608 isTemplate
? name
: OUString()),
612 void XcsParser::handleSetItem(xmlreader::XmlReader
& reader
, SetNode
* set
) {
613 OUString
component(componentName_
);
614 bool hasNodeType
= false;
618 xmlreader::Span attrLn
;
619 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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"))
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: */