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 static_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 (static_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
), 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
)) {
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());
172 new GroupNode(valueParser_
.getLayer(), false, ""),
177 case STATE_TEMPLATES
:
178 if (elements_
.empty()) {
179 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
180 name
.equals("group"))
182 handleGroup(reader
, true);
185 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
188 handleSet(reader
, true);
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
);
206 case Node::KIND_GROUP
:
207 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
213 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
214 name
.equals("node-ref"))
216 handleNodeRef(reader
);
219 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
220 name
.equals("group"))
222 handleGroup(reader
, false);
225 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
228 handleSet(reader
, false);
233 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
238 static_cast< SetNode
* >(elements_
.top().node
.get()));
242 default: // Node::KIND_LOCALIZED_VALUE
243 assert(false); // this cannot happen
247 case STATE_COMPONENT_DONE
:
249 default: // STATE_START
250 assert(false); // this cannot happen
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()) {
264 } else if (!elements_
.empty()) {
265 Element
top(elements_
.top());
268 if (elements_
.empty()) {
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
));
277 merge(i
->second
, top
.node
);
281 case STATE_COMPONENT
:
283 NodeMap
& components
= data_
.getComponents();
284 NodeMap::iterator
i(components
.find(top
.name
));
285 if (i
== components
.end()) {
287 NodeMap::value_type(top
.name
, top
.node
));
289 merge(i
->second
, top
.node
);
291 state_
= STATE_COMPONENT_DONE
;
296 throw css::uno::RuntimeException(
297 "this cannot happen");
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());
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
;
315 case STATE_TEMPLATES
:
316 state_
= STATE_TEMPLATES_DONE
;
318 case STATE_TEMPLATES_DONE
:
319 throw css::uno::RuntimeException(
320 "no component element in " + reader
.getUrl());
321 case STATE_COMPONENT_DONE
:
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
337 bool hasPackage
= false;
338 bool hasName
= false;
341 xmlreader::Span attrLn
;
342 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
345 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("package"))
348 throw css::uno::RuntimeException(
349 "multiple component-schema package attributes in " +
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"))
359 throw css::uno::RuntimeException(
360 "multiple component-schema name attributes in " +
364 xmlreader::Span
s(reader
.getAttributeValue(false));
365 buf
.append(s
.begin
, s
.length
);
369 throw css::uno::RuntimeException(
370 "no component-schema package attribute in " + reader
.getUrl());
373 throw css::uno::RuntimeException(
374 "no component-schema name attribute in " + reader
.getUrl());
376 componentName_
= xmlreader::Span(buf
.getStr(), buf
.getLength()).
380 void XcsParser::handleNodeRef(xmlreader::XmlReader
& reader
) {
381 bool hasName
= false;
383 OUString
component(componentName_
);
384 bool hasNodeType
= false;
388 xmlreader::Span attrLn
;
389 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
392 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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"))
403 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
407 throw css::uno::RuntimeException(
408 "no node-ref name attribute in " + reader
.getUrl());
410 rtl::Reference
< Node
> tmpl(
412 valueParser_
.getLayer(),
413 xmldata::parseTemplateReference(
414 component
, hasNodeType
, nodeType
, 0)));
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;
429 valueParser_
.type_
= TYPE_ERROR
;
430 bool localized
= false;
431 bool nillable
= true;
434 xmlreader::Span attrLn
;
435 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
438 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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));
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());
467 ? rtl::Reference
< Node
>(
468 new LocalizedPropertyNode(
469 valueParser_
.getLayer(), valueParser_
.type_
, nillable
))
470 : rtl::Reference
< Node
>(
472 valueParser_
.getLayer(), valueParser_
.type_
, nillable
,
473 css::uno::Any(), false))),
477 void XcsParser::handlePropValue(
478 xmlreader::XmlReader
& reader
, rtl::Reference
< Node
> const & property
)
480 xmlreader::Span attrSeparator
;
483 xmlreader::Span attrLn
;
484 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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;
505 bool extensible
= false;
508 xmlreader::Span attrLn
;
509 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
512 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
514 name
= reader
.getAttributeValue(false).convertFromUtf8();
515 } else if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
516 attrLn
.equals("extensible"))
518 extensible
= xmldata::parseBoolean(reader
.getAttributeValue(true));
522 throw css::uno::RuntimeException(
523 "no group name attribute in " + reader
.getUrl());
526 name
= Data::fullTemplateName(componentName_
, name
);
531 valueParser_
.getLayer(), extensible
,
532 isTemplate
? name
: OUString()),
536 void XcsParser::handleSet(xmlreader::XmlReader
& reader
, bool isTemplate
) {
537 bool hasName
= false;
539 OUString
component(componentName_
);
540 bool hasNodeType
= false;
544 xmlreader::Span attrLn
;
545 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
548 if (attrNsId
== ParseManager::NAMESPACE_OOR
&& attrLn
.equals("name")) {
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"))
559 nodeType
= reader
.getAttributeValue(false).convertFromUtf8();
563 throw css::uno::RuntimeException(
564 "no set name attribute in " + reader
.getUrl());
567 name
= Data::fullTemplateName(componentName_
, name
);
572 valueParser_
.getLayer(),
573 xmldata::parseTemplateReference(
574 component
, hasNodeType
, nodeType
, 0),
575 isTemplate
? name
: OUString()),
579 void XcsParser::handleSetItem(xmlreader::XmlReader
& reader
, SetNode
* set
) {
580 OUString
component(componentName_
);
581 bool hasNodeType
= false;
585 xmlreader::Span attrLn
;
586 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
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"))
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: */