Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include <cassert>
23 : #include <cstddef>
24 : #include <set>
25 :
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"
36 :
37 : #include "data.hxx"
38 : #include "localizedpropertynode.hxx"
39 : #include "groupnode.hxx"
40 : #include "node.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"
47 :
48 : namespace configmgr {
49 :
50 : namespace {
51 :
52 : // Conservatively merge a template or component (and its recursive parts) into
53 : // an existing instance:
54 0 : void merge(
55 : rtl::Reference< Node > const & original,
56 : rtl::Reference< Node > const & update)
57 : {
58 : assert(
59 : original.is() && update.is() && original->kind() == update->kind() &&
60 : update->getFinalized() == Data::NO_LAYER);
61 0 : if (update->getLayer() >= original->getLayer() &&
62 0 : update->getLayer() <= original->getFinalized())
63 : {
64 0 : switch (original->kind()) {
65 : case Node::KIND_PROPERTY:
66 : case Node::KIND_LOCALIZED_PROPERTY:
67 : case Node::KIND_LOCALIZED_VALUE:
68 0 : break; //TODO: merge certain parts?
69 : case Node::KIND_GROUP:
70 0 : for (NodeMap::const_iterator i2(update->getMembers().begin());
71 0 : i2 != update->getMembers().end(); ++i2)
72 : {
73 0 : NodeMap & members = original->getMembers();
74 0 : NodeMap::iterator i1(members.find(i2->first));
75 0 : if (i1 == members.end()) {
76 0 : if (i2->second->kind() == Node::KIND_PROPERTY &&
77 : static_cast< GroupNode * >(
78 0 : original.get())->isExtensible())
79 : {
80 0 : members.insert(*i2);
81 : }
82 0 : } else if (i2->second->kind() == i1->second->kind()) {
83 0 : merge(i1->second, i2->second);
84 : }
85 : }
86 0 : break;
87 : case Node::KIND_SET:
88 0 : for (NodeMap::const_iterator i2(update->getMembers().begin());
89 0 : i2 != update->getMembers().end(); ++i2)
90 : {
91 0 : NodeMap & members = original->getMembers();
92 0 : NodeMap::iterator i1(members.find(i2->first));
93 0 : if (i1 == members.end()) {
94 0 : if (static_cast< SetNode * >(original.get())->
95 0 : isValidTemplate(i2->second->getTemplateName()))
96 : {
97 0 : members.insert(*i2);
98 : }
99 0 : } else if (i2->second->kind() == i1->second->kind() &&
100 0 : (i2->second->getTemplateName() ==
101 0 : i1->second->getTemplateName()))
102 : {
103 0 : merge(i1->second, i2->second);
104 : }
105 : }
106 0 : break;
107 : case Node::KIND_ROOT:
108 : assert(false); // this cannot happen
109 0 : break;
110 : }
111 : }
112 0 : }
113 :
114 : }
115 :
116 0 : XcsParser::XcsParser(int layer, Data & data):
117 0 : valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
118 0 : {}
119 :
120 0 : XcsParser::~XcsParser() {}
121 :
122 0 : xmlreader::XmlReader::Text XcsParser::getTextMode() {
123 0 : return valueParser_.getTextMode();
124 : }
125 :
126 0 : bool XcsParser::startElement(
127 : xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
128 : std::set< OUString > const * existingDependencies)
129 : {
130 0 : if (valueParser_.startElement(reader, nsId, name, existingDependencies)) {
131 0 : return true;
132 : }
133 0 : if (state_ == STATE_START) {
134 0 : if (nsId == ParseManager::NAMESPACE_OOR &&
135 0 : name.equals("component-schema"))
136 : {
137 0 : handleComponentSchema(reader);
138 0 : state_ = STATE_COMPONENT_SCHEMA;
139 0 : ignoring_ = 0;
140 0 : return true;
141 : }
142 : } else {
143 : //TODO: ignoring component-schema import, component-schema uses, and
144 : // prop constraints; accepting all four at illegal places (and with
145 : // illegal content):
146 0 : if (ignoring_ > 0 ||
147 0 : (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
148 0 : (name.equals("info") || name.equals("import") ||
149 0 : name.equals("uses") || name.equals("constraints"))))
150 : {
151 : assert(ignoring_ < LONG_MAX);
152 0 : ++ignoring_;
153 0 : return true;
154 : }
155 0 : switch (state_) {
156 : case STATE_COMPONENT_SCHEMA:
157 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
158 0 : name.equals("templates"))
159 : {
160 0 : state_ = STATE_TEMPLATES;
161 0 : return true;
162 : }
163 : // fall through
164 : case STATE_TEMPLATES_DONE:
165 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
166 0 : name.equals("component"))
167 : {
168 0 : state_ = STATE_COMPONENT;
169 : assert(elements_.empty());
170 : elements_.push(
171 : Element(
172 0 : new GroupNode(valueParser_.getLayer(), false, ""),
173 0 : componentName_));
174 0 : return true;
175 : }
176 0 : break;
177 : case STATE_TEMPLATES:
178 0 : if (elements_.empty()) {
179 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
180 0 : name.equals("group"))
181 : {
182 0 : handleGroup(reader, true);
183 0 : return true;
184 : }
185 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
186 0 : name.equals("set"))
187 : {
188 0 : handleSet(reader, true);
189 0 : return true;
190 : }
191 0 : break;
192 : }
193 : // fall through
194 : case STATE_COMPONENT:
195 : assert(!elements_.empty());
196 0 : switch (elements_.top().node->kind()) {
197 : case Node::KIND_PROPERTY:
198 : case Node::KIND_LOCALIZED_PROPERTY:
199 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
200 0 : name.equals("value"))
201 : {
202 0 : handlePropValue(reader, elements_.top().node);
203 0 : return true;
204 : }
205 0 : break;
206 : case Node::KIND_GROUP:
207 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
208 0 : name.equals("prop"))
209 : {
210 0 : handleProp(reader);
211 0 : return true;
212 : }
213 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
214 0 : name.equals("node-ref"))
215 : {
216 0 : handleNodeRef(reader);
217 0 : return true;
218 : }
219 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
220 0 : name.equals("group"))
221 : {
222 0 : handleGroup(reader, false);
223 0 : return true;
224 : }
225 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
226 0 : name.equals("set"))
227 : {
228 0 : handleSet(reader, false);
229 0 : return true;
230 : }
231 0 : break;
232 : case Node::KIND_SET:
233 0 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
234 0 : name.equals("item"))
235 : {
236 : handleSetItem(
237 : reader,
238 0 : static_cast< SetNode * >(elements_.top().node.get()));
239 0 : return true;
240 : }
241 0 : break;
242 : default: // Node::KIND_LOCALIZED_VALUE
243 : assert(false); // this cannot happen
244 0 : break;
245 : }
246 0 : break;
247 : case STATE_COMPONENT_DONE:
248 0 : break;
249 : default: // STATE_START
250 : assert(false); // this cannot happen
251 0 : break;
252 : }
253 : }
254 : throw css::uno::RuntimeException(
255 0 : "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl(),
256 0 : css::uno::Reference< css::uno::XInterface >());
257 : }
258 :
259 0 : void XcsParser::endElement(xmlreader::XmlReader const & reader) {
260 0 : if (valueParser_.endElement()) {
261 0 : return;
262 : }
263 0 : if (ignoring_ > 0) {
264 0 : --ignoring_;
265 0 : } else if (!elements_.empty()) {
266 0 : Element top(elements_.top());
267 0 : elements_.pop();
268 0 : if (top.node.is()) {
269 0 : if (elements_.empty()) {
270 0 : switch (state_) {
271 : case STATE_TEMPLATES:
272 : {
273 0 : NodeMap::iterator i(data_.templates.find(top.name));
274 0 : if (i == data_.templates.end()) {
275 : data_.templates.insert(
276 0 : NodeMap::value_type(top.name, top.node));
277 : } else {
278 0 : merge(i->second, top.node);
279 : }
280 : }
281 0 : break;
282 : case STATE_COMPONENT:
283 : {
284 0 : NodeMap & components = data_.getComponents();
285 0 : NodeMap::iterator i(components.find(top.name));
286 0 : if (i == components.end()) {
287 : components.insert(
288 0 : NodeMap::value_type(top.name, top.node));
289 : } else {
290 0 : merge(i->second, top.node);
291 : }
292 0 : state_ = STATE_COMPONENT_DONE;
293 : }
294 0 : break;
295 : default:
296 : assert(false);
297 : throw css::uno::RuntimeException(
298 : "this cannot happen",
299 0 : css::uno::Reference< css::uno::XInterface >());
300 : }
301 : } else {
302 0 : if (!elements_.top().node->getMembers().insert(
303 0 : NodeMap::value_type(top.name, top.node)).second)
304 : {
305 : throw css::uno::RuntimeException(
306 0 : "duplicate " + top.name + " in " + reader.getUrl(),
307 0 : css::uno::Reference< css::uno::XInterface >());
308 : }
309 : }
310 0 : }
311 : } else {
312 0 : switch (state_) {
313 : case STATE_COMPONENT_SCHEMA:
314 : // To support old, broken extensions with .xcs files that contain
315 : // empty <component-schema> elements:
316 0 : state_ = STATE_COMPONENT_DONE;
317 0 : break;
318 : case STATE_TEMPLATES:
319 0 : state_ = STATE_TEMPLATES_DONE;
320 0 : break;
321 : case STATE_TEMPLATES_DONE:
322 : throw css::uno::RuntimeException(
323 0 : "no component element in " + reader.getUrl(),
324 0 : css::uno::Reference< css::uno::XInterface >());
325 : case STATE_COMPONENT_DONE:
326 0 : break;
327 : default:
328 : assert(false); // this cannot happen
329 : }
330 : }
331 : }
332 :
333 0 : void XcsParser::characters(xmlreader::Span const & text) {
334 0 : valueParser_.characters(text);
335 0 : }
336 :
337 0 : void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
338 : //TODO: oor:version, xml:lang attributes
339 0 : OStringBuffer buf;
340 0 : buf.append('.');
341 0 : bool hasPackage = false;
342 0 : bool hasName = false;
343 : for (;;) {
344 : int attrNsId;
345 0 : xmlreader::Span attrLn;
346 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
347 0 : break;
348 : }
349 0 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
350 : {
351 0 : if (hasPackage) {
352 : throw css::uno::RuntimeException(
353 0 : ("multiple component-schema package attributes in " +
354 0 : reader.getUrl()),
355 0 : css::uno::Reference< css::uno::XInterface >());
356 : }
357 0 : hasPackage = true;
358 0 : xmlreader::Span s(reader.getAttributeValue(false));
359 0 : buf.insert(0, s.begin, s.length);
360 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
361 0 : attrLn.equals("name"))
362 : {
363 0 : if (hasName) {
364 : throw css::uno::RuntimeException(
365 0 : ("multiple component-schema name attributes in " +
366 0 : reader.getUrl()),
367 0 : css::uno::Reference< css::uno::XInterface >());
368 : }
369 0 : hasName = true;
370 0 : xmlreader::Span s(reader.getAttributeValue(false));
371 0 : buf.append(s.begin, s.length);
372 : }
373 0 : }
374 0 : if (!hasPackage) {
375 : throw css::uno::RuntimeException(
376 0 : "no component-schema package attribute in " + reader.getUrl(),
377 0 : css::uno::Reference< css::uno::XInterface >());
378 : }
379 0 : if (!hasName) {
380 : throw css::uno::RuntimeException(
381 0 : "no component-schema name attribute in " + reader.getUrl(),
382 0 : css::uno::Reference< css::uno::XInterface >());
383 : }
384 0 : componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
385 0 : convertFromUtf8();
386 0 : }
387 :
388 0 : void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
389 0 : bool hasName = false;
390 0 : OUString name;
391 0 : OUString component(componentName_);
392 0 : bool hasNodeType = false;
393 0 : OUString nodeType;
394 : for (;;) {
395 : int attrNsId;
396 0 : xmlreader::Span attrLn;
397 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
398 0 : break;
399 : }
400 0 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
401 0 : hasName = true;
402 0 : name = reader.getAttributeValue(false).convertFromUtf8();
403 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
404 0 : attrLn.equals("component"))
405 : {
406 0 : component = reader.getAttributeValue(false).convertFromUtf8();
407 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
408 0 : attrLn.equals("node-type"))
409 : {
410 0 : hasNodeType = true;
411 0 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
412 : }
413 0 : }
414 0 : if (!hasName) {
415 : throw css::uno::RuntimeException(
416 0 : "no node-ref name attribute in " + reader.getUrl(),
417 0 : css::uno::Reference< css::uno::XInterface >());
418 : }
419 : rtl::Reference< Node > tmpl(
420 : data_.getTemplate(
421 : valueParser_.getLayer(),
422 : xmldata::parseTemplateReference(
423 0 : component, hasNodeType, nodeType, 0)));
424 0 : if (!tmpl.is()) {
425 : //TODO: this can erroneously happen as long as import/uses attributes
426 : // are not correctly processed
427 : throw css::uno::RuntimeException(
428 0 : "unknown node-ref " + name + " in " + reader.getUrl(),
429 0 : css::uno::Reference< css::uno::XInterface >());
430 : }
431 0 : rtl::Reference< Node > node(tmpl->clone(false));
432 0 : node->setLayer(valueParser_.getLayer());
433 0 : elements_.push(Element(node, name));
434 0 : }
435 :
436 0 : void XcsParser::handleProp(xmlreader::XmlReader & reader) {
437 0 : bool hasName = false;
438 0 : OUString name;
439 0 : valueParser_.type_ = TYPE_ERROR;
440 0 : bool localized = false;
441 0 : bool nillable = true;
442 : for (;;) {
443 : int attrNsId;
444 0 : xmlreader::Span attrLn;
445 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
446 0 : break;
447 : }
448 0 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
449 0 : hasName = true;
450 0 : name = reader.getAttributeValue(false).convertFromUtf8();
451 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
452 0 : attrLn.equals("type"))
453 : {
454 : valueParser_.type_ = xmldata::parseType(
455 0 : reader, reader.getAttributeValue(true));
456 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
457 0 : attrLn.equals("localized"))
458 : {
459 0 : localized = xmldata::parseBoolean(reader.getAttributeValue(true));
460 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
461 0 : attrLn.equals("nillable"))
462 : {
463 0 : nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
464 : }
465 0 : }
466 0 : if (!hasName) {
467 : throw css::uno::RuntimeException(
468 0 : "no prop name attribute in " + reader.getUrl(),
469 0 : css::uno::Reference< css::uno::XInterface >());
470 : }
471 0 : if (valueParser_.type_ == TYPE_ERROR) {
472 : throw css::uno::RuntimeException(
473 0 : "no prop type attribute in " + reader.getUrl(),
474 0 : css::uno::Reference< css::uno::XInterface >());
475 : }
476 : elements_.push(
477 : Element(
478 : (localized
479 : ? rtl::Reference< Node >(
480 : new LocalizedPropertyNode(
481 0 : valueParser_.getLayer(), valueParser_.type_, nillable))
482 : : rtl::Reference< Node >(
483 : new PropertyNode(
484 0 : valueParser_.getLayer(), valueParser_.type_, nillable,
485 0 : css::uno::Any(), false))),
486 0 : name));
487 0 : }
488 :
489 0 : void XcsParser::handlePropValue(
490 : xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
491 : {
492 0 : xmlreader::Span attrSeparator;
493 : for (;;) {
494 : int attrNsId;
495 0 : xmlreader::Span attrLn;
496 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
497 0 : break;
498 : }
499 0 : if (attrNsId == ParseManager::NAMESPACE_OOR &&
500 0 : attrLn.equals("separator"))
501 : {
502 0 : attrSeparator = reader.getAttributeValue(false);
503 0 : if (attrSeparator.length == 0) {
504 : throw css::uno::RuntimeException(
505 0 : "bad oor:separator attribute in " + reader.getUrl(),
506 0 : css::uno::Reference< css::uno::XInterface >());
507 : }
508 : }
509 0 : }
510 0 : valueParser_.separator_ = OString(
511 0 : attrSeparator.begin, attrSeparator.length);
512 0 : valueParser_.start(property);
513 0 : }
514 :
515 0 : void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
516 0 : bool hasName = false;
517 0 : OUString name;
518 0 : bool extensible = false;
519 : for (;;) {
520 : int attrNsId;
521 0 : xmlreader::Span attrLn;
522 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
523 0 : break;
524 : }
525 0 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
526 0 : hasName = true;
527 0 : name = reader.getAttributeValue(false).convertFromUtf8();
528 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
529 0 : attrLn.equals("extensible"))
530 : {
531 0 : extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
532 : }
533 0 : }
534 0 : if (!hasName) {
535 : throw css::uno::RuntimeException(
536 0 : "no group name attribute in " + reader.getUrl(),
537 0 : css::uno::Reference< css::uno::XInterface >());
538 : }
539 0 : if (isTemplate) {
540 0 : name = Data::fullTemplateName(componentName_, name);
541 : }
542 : elements_.push(
543 : Element(
544 : new GroupNode(
545 0 : valueParser_.getLayer(), extensible,
546 0 : isTemplate ? name : OUString()),
547 0 : name));
548 0 : }
549 :
550 0 : void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
551 0 : bool hasName = false;
552 0 : OUString name;
553 0 : OUString component(componentName_);
554 0 : bool hasNodeType = false;
555 0 : OUString nodeType;
556 : for (;;) {
557 : int attrNsId;
558 0 : xmlreader::Span attrLn;
559 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
560 0 : break;
561 : }
562 0 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
563 0 : hasName = true;
564 0 : name = reader.getAttributeValue(false).convertFromUtf8();
565 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
566 0 : attrLn.equals("component"))
567 : {
568 0 : component = reader.getAttributeValue(false).convertFromUtf8();
569 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
570 0 : attrLn.equals("node-type"))
571 : {
572 0 : hasNodeType = true;
573 0 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
574 : }
575 0 : }
576 0 : if (!hasName) {
577 : throw css::uno::RuntimeException(
578 0 : "no set name attribute in " + reader.getUrl(),
579 0 : css::uno::Reference< css::uno::XInterface >());
580 : }
581 0 : if (isTemplate) {
582 0 : name = Data::fullTemplateName(componentName_, name);
583 : }
584 : elements_.push(
585 : Element(
586 : new SetNode(
587 0 : valueParser_.getLayer(),
588 : xmldata::parseTemplateReference(
589 : component, hasNodeType, nodeType, 0),
590 0 : isTemplate ? name : OUString()),
591 0 : name));
592 0 : }
593 :
594 0 : void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
595 0 : OUString component(componentName_);
596 0 : bool hasNodeType = false;
597 0 : OUString nodeType;
598 : for (;;) {
599 : int attrNsId;
600 0 : xmlreader::Span attrLn;
601 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
602 0 : break;
603 : }
604 0 : if (attrNsId == ParseManager::NAMESPACE_OOR &&
605 0 : attrLn.equals("component"))
606 : {
607 0 : component = reader.getAttributeValue(false).convertFromUtf8();
608 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
609 0 : attrLn.equals("node-type"))
610 : {
611 0 : hasNodeType = true;
612 0 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
613 : }
614 0 : }
615 0 : set->getAdditionalTemplateNames().push_back(
616 0 : xmldata::parseTemplateReference(component, hasNodeType, nodeType, 0));
617 0 : elements_.push(Element(rtl::Reference< Node >(), ""));
618 0 : }
619 :
620 : }
621 :
622 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|