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 40005 : XcsParser::XcsParser(int layer, Data & data):
117 40005 : valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
118 40005 : {}
119 :
120 80010 : XcsParser::~XcsParser() {}
121 :
122 4517136 : xmlreader::XmlReader::Text XcsParser::getTextMode() {
123 4517136 : return valueParser_.getTextMode();
124 : }
125 :
126 2016252 : bool XcsParser::startElement(
127 : xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
128 : std::set< OUString > const * existingDependencies)
129 : {
130 2016252 : if (valueParser_.startElement(reader, nsId, name, existingDependencies)) {
131 11811 : return true;
132 : }
133 2004441 : if (state_ == STATE_START) {
134 80010 : if (nsId == ParseManager::NAMESPACE_OOR &&
135 40005 : name.equals("component-schema"))
136 : {
137 40005 : handleComponentSchema(reader);
138 40005 : state_ = STATE_COMPONENT_SCHEMA;
139 40005 : ignoring_ = 0;
140 40005 : 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 1964436 : if (ignoring_ > 0 ||
147 1964436 : (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
148 5893308 : (name.equals("info") || name.equals("import") ||
149 3928872 : name.equals("uses") || name.equals("constraints"))))
150 : {
151 : assert(ignoring_ < LONG_MAX);
152 0 : ++ignoring_;
153 0 : return true;
154 : }
155 1964436 : switch (state_) {
156 : case STATE_COMPONENT_SCHEMA:
157 80010 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
158 40005 : name.equals("templates"))
159 : {
160 36576 : state_ = STATE_TEMPLATES;
161 36576 : return true;
162 : }
163 : // fall through
164 : case STATE_TEMPLATES_DONE:
165 80010 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
166 40005 : name.equals("component"))
167 : {
168 40005 : state_ = STATE_COMPONENT;
169 : assert(elements_.empty());
170 : elements_.push(
171 : Element(
172 80010 : new GroupNode(valueParser_.getLayer(), false, ""),
173 120015 : componentName_));
174 40005 : return true;
175 : }
176 0 : break;
177 : case STATE_TEMPLATES:
178 453390 : if (elements_.empty()) {
179 148590 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
180 74295 : name.equals("group"))
181 : {
182 70866 : handleGroup(reader, true);
183 70866 : return true;
184 : }
185 6858 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
186 3429 : name.equals("set"))
187 : {
188 3429 : handleSet(reader, true);
189 3429 : return true;
190 : }
191 0 : break;
192 : }
193 : // fall through
194 : case STATE_COMPONENT:
195 : assert(!elements_.empty());
196 1813560 : switch (elements_.top().node->kind()) {
197 : case Node::KIND_PROPERTY:
198 : case Node::KIND_LOCALIZED_PROPERTY:
199 1139190 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
200 569595 : name.equals("value"))
201 : {
202 569595 : handlePropValue(reader, elements_.top().node);
203 569595 : return true;
204 : }
205 0 : break;
206 : case Node::KIND_GROUP:
207 2487930 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
208 1243965 : name.equals("prop"))
209 : {
210 922020 : handleProp(reader);
211 922020 : return true;
212 : }
213 643890 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
214 321945 : name.equals("node-ref"))
215 : {
216 15240 : handleNodeRef(reader);
217 15240 : return true;
218 : }
219 613410 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
220 306705 : name.equals("group"))
221 : {
222 217932 : handleGroup(reader, false);
223 217932 : return true;
224 : }
225 177546 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
226 88773 : name.equals("set"))
227 : {
228 88773 : handleSet(reader, false);
229 88773 : 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 : }
257 :
258 2016252 : void XcsParser::endElement(xmlreader::XmlReader const & reader) {
259 2016252 : if (valueParser_.endElement()) {
260 2597658 : return;
261 : }
262 1434846 : if (ignoring_ > 0) {
263 0 : --ignoring_;
264 1434846 : } else if (!elements_.empty()) {
265 1358265 : Element top(elements_.top());
266 1358265 : elements_.pop();
267 1358265 : if (top.node.is()) {
268 1358265 : if (elements_.empty()) {
269 114300 : switch (state_) {
270 : case STATE_TEMPLATES:
271 : {
272 74295 : NodeMap::iterator i(data_.templates.find(top.name));
273 74295 : if (i == data_.templates.end()) {
274 : data_.templates.insert(
275 74295 : NodeMap::value_type(top.name, top.node));
276 : } else {
277 0 : merge(i->second, top.node);
278 : }
279 : }
280 74295 : break;
281 : case STATE_COMPONENT:
282 : {
283 40005 : NodeMap & components = data_.getComponents();
284 40005 : NodeMap::iterator i(components.find(top.name));
285 40005 : if (i == components.end()) {
286 : components.insert(
287 40005 : NodeMap::value_type(top.name, top.node));
288 : } else {
289 0 : merge(i->second, top.node);
290 : }
291 40005 : state_ = STATE_COMPONENT_DONE;
292 : }
293 40005 : break;
294 : default:
295 : assert(false);
296 : throw css::uno::RuntimeException(
297 0 : "this cannot happen");
298 : }
299 : } else {
300 3731895 : if (!elements_.top().node->getMembers().insert(
301 3731895 : NodeMap::value_type(top.name, top.node)).second)
302 : {
303 : throw css::uno::RuntimeException(
304 0 : "duplicate " + top.name + " in " + reader.getUrl());
305 : }
306 : }
307 1358265 : }
308 : } else {
309 76581 : switch (state_) {
310 : case STATE_COMPONENT_SCHEMA:
311 : // To support old, broken extensions with .xcs files that contain
312 : // empty <component-schema> elements:
313 0 : state_ = STATE_COMPONENT_DONE;
314 0 : break;
315 : case STATE_TEMPLATES:
316 36576 : state_ = STATE_TEMPLATES_DONE;
317 36576 : break;
318 : case STATE_TEMPLATES_DONE:
319 : throw css::uno::RuntimeException(
320 0 : "no component element in " + reader.getUrl());
321 : case STATE_COMPONENT_DONE:
322 40005 : break;
323 : default:
324 : assert(false); // this cannot happen
325 : }
326 : }
327 : }
328 :
329 524637 : void XcsParser::characters(xmlreader::Span const & text) {
330 524637 : valueParser_.characters(text);
331 524637 : }
332 :
333 40005 : void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
334 : //TODO: oor:version, xml:lang attributes
335 40005 : OStringBuffer buf;
336 40005 : buf.append('.');
337 40005 : bool hasPackage = false;
338 40005 : bool hasName = false;
339 : for (;;) {
340 : int attrNsId;
341 160020 : xmlreader::Span attrLn;
342 160020 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
343 40005 : break;
344 : }
345 120015 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
346 : {
347 40005 : if (hasPackage) {
348 : throw css::uno::RuntimeException(
349 0 : "multiple component-schema package attributes in " +
350 0 : reader.getUrl());
351 : }
352 40005 : hasPackage = true;
353 40005 : xmlreader::Span s(reader.getAttributeValue(false));
354 40005 : buf.insert(0, s.begin, s.length);
355 120015 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
356 40005 : attrLn.equals("name"))
357 : {
358 40005 : if (hasName) {
359 : throw css::uno::RuntimeException(
360 0 : "multiple component-schema name attributes in " +
361 0 : reader.getUrl());
362 : }
363 40005 : hasName = true;
364 40005 : xmlreader::Span s(reader.getAttributeValue(false));
365 40005 : buf.append(s.begin, s.length);
366 : }
367 120015 : }
368 40005 : if (!hasPackage) {
369 : throw css::uno::RuntimeException(
370 0 : "no component-schema package attribute in " + reader.getUrl());
371 : }
372 40005 : if (!hasName) {
373 : throw css::uno::RuntimeException(
374 0 : "no component-schema name attribute in " + reader.getUrl());
375 : }
376 80010 : componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
377 80010 : convertFromUtf8();
378 40005 : }
379 :
380 15240 : void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
381 15240 : bool hasName = false;
382 15240 : OUString name;
383 30480 : OUString component(componentName_);
384 15240 : bool hasNodeType = false;
385 30480 : OUString nodeType;
386 : for (;;) {
387 : int attrNsId;
388 45720 : xmlreader::Span attrLn;
389 45720 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
390 15240 : break;
391 : }
392 30480 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
393 15240 : hasName = true;
394 15240 : name = reader.getAttributeValue(false).convertFromUtf8();
395 30480 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
396 15240 : attrLn.equals("component"))
397 : {
398 0 : component = reader.getAttributeValue(false).convertFromUtf8();
399 30480 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
400 15240 : attrLn.equals("node-type"))
401 : {
402 15240 : hasNodeType = true;
403 15240 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
404 : }
405 30480 : }
406 15240 : if (!hasName) {
407 : throw css::uno::RuntimeException(
408 0 : "no node-ref name attribute in " + reader.getUrl());
409 : }
410 : rtl::Reference< Node > tmpl(
411 : data_.getTemplate(
412 : valueParser_.getLayer(),
413 : xmldata::parseTemplateReference(
414 30480 : component, hasNodeType, nodeType, 0)));
415 15240 : if (!tmpl.is()) {
416 : //TODO: this can erroneously happen as long as import/uses attributes
417 : // are not correctly processed
418 : throw css::uno::RuntimeException(
419 0 : "unknown node-ref " + name + " in " + reader.getUrl());
420 : }
421 30480 : rtl::Reference< Node > node(tmpl->clone(false));
422 15240 : node->setLayer(valueParser_.getLayer());
423 30480 : elements_.push(Element(node, name));
424 15240 : }
425 :
426 922020 : void XcsParser::handleProp(xmlreader::XmlReader & reader) {
427 922020 : bool hasName = false;
428 922020 : OUString name;
429 922020 : valueParser_.type_ = TYPE_ERROR;
430 922020 : bool localized = false;
431 922020 : bool nillable = true;
432 : for (;;) {
433 : int attrNsId;
434 3366516 : xmlreader::Span attrLn;
435 3366516 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
436 922020 : break;
437 : }
438 2444496 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
439 922020 : hasName = true;
440 922020 : name = reader.getAttributeValue(false).convertFromUtf8();
441 3044952 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
442 1522476 : attrLn.equals("type"))
443 : {
444 : valueParser_.type_ = xmldata::parseType(
445 922020 : reader, reader.getAttributeValue(true));
446 1200912 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
447 600456 : attrLn.equals("localized"))
448 : {
449 79248 : localized = xmldata::parseBoolean(reader.getAttributeValue(true));
450 1042416 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
451 521208 : attrLn.equals("nillable"))
452 : {
453 521208 : nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
454 : }
455 2444496 : }
456 922020 : if (!hasName) {
457 : throw css::uno::RuntimeException(
458 0 : "no prop name attribute in " + reader.getUrl());
459 : }
460 922020 : if (valueParser_.type_ == TYPE_ERROR) {
461 : throw css::uno::RuntimeException(
462 0 : "no prop type attribute in " + reader.getUrl());
463 : }
464 : elements_.push(
465 : Element(
466 : (localized
467 : ? rtl::Reference< Node >(
468 : new LocalizedPropertyNode(
469 151638 : valueParser_.getLayer(), valueParser_.type_, nillable))
470 : : rtl::Reference< Node >(
471 : new PropertyNode(
472 846201 : valueParser_.getLayer(), valueParser_.type_, nillable,
473 846201 : css::uno::Any(), false))),
474 2766060 : name));
475 922020 : }
476 :
477 569595 : void XcsParser::handlePropValue(
478 : xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
479 : {
480 569595 : xmlreader::Span attrSeparator;
481 : for (;;) {
482 : int attrNsId;
483 572643 : xmlreader::Span attrLn;
484 572643 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
485 569595 : break;
486 : }
487 6096 : if (attrNsId == ParseManager::NAMESPACE_OOR &&
488 3048 : attrLn.equals("separator"))
489 : {
490 3048 : attrSeparator = reader.getAttributeValue(false);
491 3048 : if (attrSeparator.length == 0) {
492 : throw css::uno::RuntimeException(
493 0 : "bad oor:separator attribute in " + reader.getUrl());
494 : }
495 : }
496 3048 : }
497 1139190 : valueParser_.separator_ = OString(
498 569595 : attrSeparator.begin, attrSeparator.length);
499 1139190 : valueParser_.start(property);
500 569595 : }
501 :
502 288798 : void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
503 288798 : bool hasName = false;
504 288798 : OUString name;
505 288798 : bool extensible = false;
506 : for (;;) {
507 : int attrNsId;
508 586740 : xmlreader::Span attrLn;
509 586740 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
510 288798 : break;
511 : }
512 297942 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
513 288798 : hasName = true;
514 288798 : name = reader.getAttributeValue(false).convertFromUtf8();
515 18288 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
516 9144 : attrLn.equals("extensible"))
517 : {
518 8763 : extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
519 : }
520 297942 : }
521 288798 : if (!hasName) {
522 : throw css::uno::RuntimeException(
523 0 : "no group name attribute in " + reader.getUrl());
524 : }
525 288798 : if (isTemplate) {
526 70866 : name = Data::fullTemplateName(componentName_, name);
527 : }
528 : elements_.push(
529 : Element(
530 : new GroupNode(
531 288798 : valueParser_.getLayer(), extensible,
532 288798 : isTemplate ? name : OUString()),
533 577596 : name));
534 288798 : }
535 :
536 92202 : void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
537 92202 : bool hasName = false;
538 92202 : OUString name;
539 184404 : OUString component(componentName_);
540 92202 : bool hasNodeType = false;
541 184404 : OUString nodeType;
542 : for (;;) {
543 : int attrNsId;
544 293370 : xmlreader::Span attrLn;
545 293370 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
546 92202 : break;
547 : }
548 201168 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
549 92202 : hasName = true;
550 92202 : name = reader.getAttributeValue(false).convertFromUtf8();
551 217932 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
552 108966 : attrLn.equals("component"))
553 : {
554 16764 : component = reader.getAttributeValue(false).convertFromUtf8();
555 184404 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
556 92202 : attrLn.equals("node-type"))
557 : {
558 92202 : hasNodeType = true;
559 92202 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
560 : }
561 201168 : }
562 92202 : if (!hasName) {
563 : throw css::uno::RuntimeException(
564 0 : "no set name attribute in " + reader.getUrl());
565 : }
566 92202 : if (isTemplate) {
567 3429 : name = Data::fullTemplateName(componentName_, name);
568 : }
569 : elements_.push(
570 : Element(
571 : new SetNode(
572 92202 : valueParser_.getLayer(),
573 : xmldata::parseTemplateReference(
574 : component, hasNodeType, nodeType, 0),
575 92202 : isTemplate ? name : OUString()),
576 276606 : name));
577 92202 : }
578 :
579 0 : void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
580 0 : OUString component(componentName_);
581 0 : bool hasNodeType = false;
582 0 : OUString nodeType;
583 : for (;;) {
584 : int attrNsId;
585 0 : xmlreader::Span attrLn;
586 0 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
587 0 : break;
588 : }
589 0 : if (attrNsId == ParseManager::NAMESPACE_OOR &&
590 0 : attrLn.equals("component"))
591 : {
592 0 : component = reader.getAttributeValue(false).convertFromUtf8();
593 0 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
594 0 : attrLn.equals("node-type"))
595 : {
596 0 : hasNodeType = true;
597 0 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
598 : }
599 0 : }
600 0 : set->getAdditionalTemplateNames().push_back(
601 0 : xmldata::parseTemplateReference(component, hasNodeType, nodeType, 0));
602 0 : elements_.push(Element(rtl::Reference< Node >(), ""));
603 0 : }
604 :
605 : }
606 :
607 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|