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 (auto const& updateMember
: update
->getMembers())
72 NodeMap
& members
= original
->getMembers();
73 NodeMap::iterator
i1(members
.find(updateMember
.first
));
74 if (i1
== members
.end()) {
75 if (updateMember
.second
->kind() == Node::KIND_PROPERTY
&&
76 static_cast< GroupNode
* >(
77 original
.get())->isExtensible())
79 members
.insert(updateMember
);
81 } else if (updateMember
.second
->kind() == i1
->second
->kind()) {
82 merge(i1
->second
, updateMember
.second
);
87 for (auto const& updateMember
: update
->getMembers())
89 NodeMap
& members
= original
->getMembers();
90 NodeMap::iterator
i1(members
.find(updateMember
.first
));
91 if (i1
== members
.end()) {
92 if (static_cast< SetNode
* >(original
.get())->
93 isValidTemplate(updateMember
.second
->getTemplateName()))
95 members
.insert(updateMember
);
97 } else if (updateMember
.second
->kind() == i1
->second
->kind() &&
98 (updateMember
.second
->getTemplateName() ==
99 i1
->second
->getTemplateName()))
101 merge(i1
->second
, updateMember
.second
);
105 case Node::KIND_ROOT
:
106 assert(false); // this cannot happen
114 XcsParser::XcsParser(int layer
, Data
& data
):
115 valueParser_(layer
), data_(data
), state_(STATE_START
), ignoring_()
118 XcsParser::~XcsParser() {}
120 xmlreader::XmlReader::Text
XcsParser::getTextMode() {
121 return valueParser_
.getTextMode();
124 bool XcsParser::startElement(
125 xmlreader::XmlReader
& reader
, int nsId
, xmlreader::Span
const & name
,
126 std::set
< OUString
> const * /*existingDependencies*/)
128 if (valueParser_
.startElement(reader
, nsId
, name
)) {
131 if (state_
== STATE_START
) {
132 if (nsId
== ParseManager::NAMESPACE_OOR
&&
133 name
.equals("component-schema"))
135 handleComponentSchema(reader
);
136 state_
= STATE_COMPONENT_SCHEMA
;
141 //TODO: ignoring component-schema import, component-schema uses, and
142 // prop constraints; accepting all four at illegal places (and with
145 (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
146 (name
.equals("info") || name
.equals("import") ||
147 name
.equals("uses") || name
.equals("constraints"))))
149 assert(ignoring_
< LONG_MAX
);
154 case STATE_COMPONENT_SCHEMA
:
155 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
156 name
.equals("templates"))
158 state_
= STATE_TEMPLATES
;
162 case STATE_TEMPLATES_DONE
:
163 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
164 name
.equals("component"))
166 state_
= STATE_COMPONENT
;
167 assert(elements_
.empty());
170 new GroupNode(valueParser_
.getLayer(), false, ""),
175 case STATE_TEMPLATES
:
176 if (elements_
.empty()) {
177 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
178 name
.equals("group"))
180 handleGroup(reader
, true);
183 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
186 handleSet(reader
, true);
192 case STATE_COMPONENT
:
193 assert(!elements_
.empty());
194 switch (elements_
.top().node
->kind()) {
195 case Node::KIND_PROPERTY
:
196 case Node::KIND_LOCALIZED_PROPERTY
:
197 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
198 name
.equals("value"))
200 handlePropValue(reader
, elements_
.top().node
);
204 case Node::KIND_GROUP
:
205 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
211 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
212 name
.equals("node-ref"))
214 handleNodeRef(reader
);
217 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
218 name
.equals("group"))
220 handleGroup(reader
, false);
223 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
226 handleSet(reader
, false);
231 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
236 static_cast< SetNode
* >(elements_
.top().node
.get()));
240 default: // Node::KIND_LOCALIZED_VALUE
241 assert(false); // this cannot happen
245 case STATE_COMPONENT_DONE
:
247 default: // STATE_START
248 assert(false); // this cannot happen
252 throw css::uno::RuntimeException(
253 "bad member <" + name
.convertFromUtf8() + "> in " + reader
.getUrl());
256 void XcsParser::endElement(xmlreader::XmlReader
const & reader
) {
257 if (valueParser_
.endElement()) {
262 } else if (!elements_
.empty()) {
263 Element
top(elements_
.top());
266 if (elements_
.empty()) {
268 case STATE_TEMPLATES
:
270 NodeMap::iterator
i(data_
.templates
.find(top
.name
));
271 if (i
== data_
.templates
.end()) {
272 data_
.templates
.insert(
273 NodeMap::value_type(top
.name
, top
.node
));
275 merge(i
->second
, top
.node
);
279 case STATE_COMPONENT
:
281 NodeMap
& components
= data_
.getComponents();
282 NodeMap::iterator
i(components
.find(top
.name
));
283 if (i
== components
.end()) {
285 NodeMap::value_type(top
.name
, top
.node
));
287 merge(i
->second
, top
.node
);
289 state_
= STATE_COMPONENT_DONE
;
294 throw css::uno::RuntimeException(
295 "this cannot happen");
298 if (!elements_
.top().node
->getMembers().insert(
299 NodeMap::value_type(top
.name
, top
.node
)).second
)
301 throw css::uno::RuntimeException(
302 "duplicate " + top
.name
+ " in " + reader
.getUrl());
308 case STATE_COMPONENT_SCHEMA
:
309 // To support old, broken extensions with .xcs files that contain
310 // empty <component-schema> elements:
311 state_
= STATE_COMPONENT_DONE
;
313 case STATE_TEMPLATES
:
314 state_
= STATE_TEMPLATES_DONE
;
316 case STATE_TEMPLATES_DONE
:
317 throw css::uno::RuntimeException(
318 "no component element in " + reader
.getUrl());
319 case STATE_COMPONENT_DONE
:
322 assert(false); // this cannot happen
327 void XcsParser::characters(xmlreader::Span
const & text
) {
328 valueParser_
.characters(text
);
331 void XcsParser::handleComponentSchema(xmlreader::XmlReader
& reader
) {
332 //TODO: oor:version, xml:lang attributes
335 bool hasPackage
= false;
336 bool hasName
= false;
339 xmlreader::Span attrLn
;
340 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
343 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("package"))
346 throw css::uno::RuntimeException(
347 "multiple component-schema package attributes in " +
351 xmlreader::Span
s(reader
.getAttributeValue(false));
352 buf
.insert(0, s
.begin
, s
.length
);
353 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
354 attrLn
.equals("name"))
357 throw css::uno::RuntimeException(
358 "multiple component-schema name attributes in " +
362 xmlreader::Span
s(reader
.getAttributeValue(false));
363 buf
.append(s
.begin
, s
.length
);
367 throw css::uno::RuntimeException(
368 "no component-schema package attribute in " + reader
.getUrl());
371 throw css::uno::RuntimeException(
372 "no component-schema name attribute in " + reader
.getUrl());
374 componentName_
= xmlreader::Span(buf
.getStr(), buf
.getLength()).
378 void XcsParser::handleNodeRef(xmlreader::XmlReader
& reader
) {
379 bool hasName
= false;
381 OUString
component(componentName_
);
382 bool hasNodeType
= false;
386 xmlreader::Span attrLn
;
387 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
390 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
392 name
= reader
.getAttributeValue(false).convertFromUtf8();
393 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
394 attrLn
.equals("component"))
396 component
= reader
.getAttributeValue(false).convertFromUtf8();
397 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
398 attrLn
.equals("node-type"))
401 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
405 throw css::uno::RuntimeException(
406 "no node-ref name attribute in " + reader
.getUrl());
408 rtl::Reference
< Node
> tmpl(
410 valueParser_
.getLayer(),
411 xmldata::parseTemplateReference(
412 component
, hasNodeType
, nodeType
, nullptr)));
414 //TODO: this can erroneously happen as long as import/uses attributes
415 // are not correctly processed
416 throw css::uno::RuntimeException(
417 "unknown node-ref " + name
+ " in " + reader
.getUrl());
419 rtl::Reference
< Node
> node(tmpl
->clone(false));
420 node
->setLayer(valueParser_
.getLayer());
421 elements_
.push(Element(node
, name
));
424 void XcsParser::handleProp(xmlreader::XmlReader
& reader
) {
425 bool hasName
= false;
427 valueParser_
.type_
= TYPE_ERROR
;
428 bool localized
= false;
429 bool nillable
= true;
432 xmlreader::Span attrLn
;
433 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
436 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
438 name
= reader
.getAttributeValue(false).convertFromUtf8();
439 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
440 attrLn
.equals("type"))
442 valueParser_
.type_
= xmldata::parseType(
443 reader
, reader
.getAttributeValue(true));
444 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
445 attrLn
.equals("localized"))
447 localized
= xmldata::parseBoolean(reader
.getAttributeValue(true));
448 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
449 attrLn
.equals("nillable"))
451 nillable
= xmldata::parseBoolean(reader
.getAttributeValue(true));
455 throw css::uno::RuntimeException(
456 "no prop name attribute in " + reader
.getUrl());
458 if (valueParser_
.type_
== TYPE_ERROR
) {
459 throw css::uno::RuntimeException(
460 "no prop type attribute in " + reader
.getUrl());
465 ? rtl::Reference
< Node
>(
466 new LocalizedPropertyNode(
467 valueParser_
.getLayer(), valueParser_
.type_
, nillable
))
468 : rtl::Reference
< Node
>(
470 valueParser_
.getLayer(), valueParser_
.type_
, nillable
,
471 css::uno::Any(), false))),
475 void XcsParser::handlePropValue(
476 xmlreader::XmlReader
& reader
, rtl::Reference
< Node
> const & property
)
478 xmlreader::Span attrSeparator
;
481 xmlreader::Span attrLn
;
482 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
485 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
486 attrLn
.equals("separator"))
488 attrSeparator
= reader
.getAttributeValue(false);
489 if (attrSeparator
.length
== 0) {
490 throw css::uno::RuntimeException(
491 "bad oor:separator attribute in " + reader
.getUrl());
495 valueParser_
.separator_
= OString(
496 attrSeparator
.begin
, attrSeparator
.length
);
497 valueParser_
.start(property
);
500 void XcsParser::handleGroup(xmlreader::XmlReader
& reader
, bool isTemplate
) {
501 bool hasName
= false;
503 bool extensible
= false;
506 xmlreader::Span attrLn
;
507 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
510 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
512 name
= reader
.getAttributeValue(false).convertFromUtf8();
513 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
514 attrLn
.equals("extensible"))
516 extensible
= xmldata::parseBoolean(reader
.getAttributeValue(true));
520 throw css::uno::RuntimeException(
521 "no group name attribute in " + reader
.getUrl());
524 name
= Data::fullTemplateName(componentName_
, name
);
529 valueParser_
.getLayer(), extensible
,
530 isTemplate
? name
: OUString()),
534 void XcsParser::handleSet(xmlreader::XmlReader
& reader
, bool isTemplate
) {
535 bool hasName
= false;
537 OUString
component(componentName_
);
538 bool hasNodeType
= false;
542 xmlreader::Span attrLn
;
543 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
546 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
548 name
= reader
.getAttributeValue(false).convertFromUtf8();
549 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
550 attrLn
.equals("component"))
552 component
= reader
.getAttributeValue(false).convertFromUtf8();
553 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
554 attrLn
.equals("node-type"))
557 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
561 throw css::uno::RuntimeException(
562 "no set name attribute in " + reader
.getUrl());
565 name
= Data::fullTemplateName(componentName_
, name
);
570 valueParser_
.getLayer(),
571 xmldata::parseTemplateReference(
572 component
, hasNodeType
, nodeType
, nullptr),
573 isTemplate
? name
: OUString()),
577 void XcsParser::handleSetItem(xmlreader::XmlReader
& reader
, SetNode
* set
) {
578 OUString
component(componentName_
);
579 bool hasNodeType
= false;
583 xmlreader::Span attrLn
;
584 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
587 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
588 attrLn
.equals("component"))
590 component
= reader
.getAttributeValue(false).convertFromUtf8();
591 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
592 attrLn
.equals("node-type"))
595 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
598 set
->getAdditionalTemplateNames().push_back(
599 xmldata::parseTemplateReference(component
, hasNodeType
, nodeType
, nullptr));
600 elements_
.push(Element(rtl::Reference
< Node
>(), ""));
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */