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 25500 : XcsParser::XcsParser(int layer, Data & data):
117 25500 : valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
118 25500 : {}
119 :
120 51000 : XcsParser::~XcsParser() {}
121 :
122 2967250 : xmlreader::XmlReader::Text XcsParser::getTextMode() {
123 2967250 : return valueParser_.getTextMode();
124 : }
125 :
126 1323250 : bool XcsParser::startElement(
127 : xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
128 : std::set< OUString > const * existingDependencies)
129 : {
130 1323250 : if (valueParser_.startElement(reader, nsId, name, existingDependencies)) {
131 8250 : return true;
132 : }
133 1315000 : if (state_ == STATE_START) {
134 51000 : if (nsId == ParseManager::NAMESPACE_OOR &&
135 25500 : name.equals("component-schema"))
136 : {
137 25500 : handleComponentSchema(reader);
138 25500 : state_ = STATE_COMPONENT_SCHEMA;
139 25500 : ignoring_ = 0;
140 25500 : 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 1289500 : if (ignoring_ > 0 ||
147 1289500 : (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
148 3868500 : (name.equals("info") || name.equals("import") ||
149 2579000 : name.equals("uses") || name.equals("constraints"))))
150 : {
151 : assert(ignoring_ < LONG_MAX);
152 0 : ++ignoring_;
153 0 : return true;
154 : }
155 1289500 : switch (state_) {
156 : case STATE_COMPONENT_SCHEMA:
157 51000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
158 25500 : name.equals("templates"))
159 : {
160 23500 : state_ = STATE_TEMPLATES;
161 23500 : return true;
162 : }
163 : // fall through
164 : case STATE_TEMPLATES_DONE:
165 51000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
166 25500 : name.equals("component"))
167 : {
168 25500 : state_ = STATE_COMPONENT;
169 : assert(elements_.empty());
170 : elements_.push(
171 : Element(
172 51000 : new GroupNode(valueParser_.getLayer(), false, ""),
173 76500 : componentName_));
174 25500 : return true;
175 : }
176 0 : break;
177 : case STATE_TEMPLATES:
178 298500 : if (elements_.empty()) {
179 97500 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
180 48750 : name.equals("group"))
181 : {
182 46500 : handleGroup(reader, true);
183 46500 : return true;
184 : }
185 4500 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
186 2250 : name.equals("set"))
187 : {
188 2250 : handleSet(reader, true);
189 2250 : return true;
190 : }
191 0 : break;
192 : }
193 : // fall through
194 : case STATE_COMPONENT:
195 : assert(!elements_.empty());
196 1191750 : switch (elements_.top().node->kind()) {
197 : case Node::KIND_PROPERTY:
198 : case Node::KIND_LOCALIZED_PROPERTY:
199 750500 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
200 375250 : name.equals("value"))
201 : {
202 375250 : handlePropValue(reader, elements_.top().node);
203 375250 : return true;
204 : }
205 0 : break;
206 : case Node::KIND_GROUP:
207 1633000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
208 816500 : name.equals("prop"))
209 : {
210 607000 : handleProp(reader);
211 607000 : return true;
212 : }
213 419000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
214 209500 : name.equals("node-ref"))
215 : {
216 10000 : handleNodeRef(reader);
217 10000 : return true;
218 : }
219 399000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
220 199500 : name.equals("group"))
221 : {
222 142000 : handleGroup(reader, false);
223 142000 : return true;
224 : }
225 115000 : if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
226 57500 : name.equals("set"))
227 : {
228 57500 : handleSet(reader, false);
229 57500 : 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 1323250 : void XcsParser::endElement(xmlreader::XmlReader const & reader) {
259 1323250 : if (valueParser_.endElement()) {
260 1706750 : return;
261 : }
262 939750 : if (ignoring_ > 0) {
263 0 : --ignoring_;
264 939750 : } else if (!elements_.empty()) {
265 890750 : Element top(elements_.top());
266 890750 : elements_.pop();
267 890750 : if (top.node.is()) {
268 890750 : if (elements_.empty()) {
269 74250 : switch (state_) {
270 : case STATE_TEMPLATES:
271 : {
272 48750 : NodeMap::iterator i(data_.templates.find(top.name));
273 48750 : if (i == data_.templates.end()) {
274 : data_.templates.insert(
275 48750 : NodeMap::value_type(top.name, top.node));
276 : } else {
277 0 : merge(i->second, top.node);
278 : }
279 : }
280 48750 : break;
281 : case STATE_COMPONENT:
282 : {
283 25500 : NodeMap & components = data_.getComponents();
284 25500 : NodeMap::iterator i(components.find(top.name));
285 25500 : if (i == components.end()) {
286 : components.insert(
287 25500 : NodeMap::value_type(top.name, top.node));
288 : } else {
289 0 : merge(i->second, top.node);
290 : }
291 25500 : state_ = STATE_COMPONENT_DONE;
292 : }
293 25500 : break;
294 : default:
295 : assert(false);
296 : throw css::uno::RuntimeException(
297 0 : "this cannot happen");
298 : }
299 : } else {
300 2449500 : if (!elements_.top().node->getMembers().insert(
301 2449500 : 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 890750 : }
308 : } else {
309 49000 : 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 23500 : state_ = STATE_TEMPLATES_DONE;
317 23500 : 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 25500 : break;
323 : default:
324 : assert(false); // this cannot happen
325 : }
326 : }
327 : }
328 :
329 346250 : void XcsParser::characters(xmlreader::Span const & text) {
330 346250 : valueParser_.characters(text);
331 346250 : }
332 :
333 25500 : void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
334 : //TODO: oor:version, xml:lang attributes
335 25500 : OStringBuffer buf;
336 25500 : buf.append('.');
337 25500 : bool hasPackage = false;
338 25500 : bool hasName = false;
339 : for (;;) {
340 : int attrNsId;
341 102000 : xmlreader::Span attrLn;
342 102000 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
343 25500 : break;
344 : }
345 76500 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("package"))
346 : {
347 25500 : if (hasPackage) {
348 : throw css::uno::RuntimeException(
349 0 : "multiple component-schema package attributes in " +
350 0 : reader.getUrl());
351 : }
352 25500 : hasPackage = true;
353 25500 : xmlreader::Span s(reader.getAttributeValue(false));
354 25500 : buf.insert(0, s.begin, s.length);
355 76500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
356 25500 : attrLn.equals("name"))
357 : {
358 25500 : if (hasName) {
359 : throw css::uno::RuntimeException(
360 0 : "multiple component-schema name attributes in " +
361 0 : reader.getUrl());
362 : }
363 25500 : hasName = true;
364 25500 : xmlreader::Span s(reader.getAttributeValue(false));
365 25500 : buf.append(s.begin, s.length);
366 : }
367 76500 : }
368 25500 : if (!hasPackage) {
369 : throw css::uno::RuntimeException(
370 0 : "no component-schema package attribute in " + reader.getUrl());
371 : }
372 25500 : if (!hasName) {
373 : throw css::uno::RuntimeException(
374 0 : "no component-schema name attribute in " + reader.getUrl());
375 : }
376 51000 : componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
377 51000 : convertFromUtf8();
378 25500 : }
379 :
380 10000 : void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
381 10000 : bool hasName = false;
382 10000 : OUString name;
383 20000 : OUString component(componentName_);
384 10000 : bool hasNodeType = false;
385 20000 : OUString nodeType;
386 : for (;;) {
387 : int attrNsId;
388 30000 : xmlreader::Span attrLn;
389 30000 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
390 10000 : break;
391 : }
392 20000 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
393 10000 : hasName = true;
394 10000 : name = reader.getAttributeValue(false).convertFromUtf8();
395 20000 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
396 10000 : attrLn.equals("component"))
397 : {
398 0 : component = reader.getAttributeValue(false).convertFromUtf8();
399 20000 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
400 10000 : attrLn.equals("node-type"))
401 : {
402 10000 : hasNodeType = true;
403 10000 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
404 : }
405 20000 : }
406 10000 : 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 20000 : component, hasNodeType, nodeType, 0)));
415 10000 : 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 20000 : rtl::Reference< Node > node(tmpl->clone(false));
422 10000 : node->setLayer(valueParser_.getLayer());
423 20000 : elements_.push(Element(node, name));
424 10000 : }
425 :
426 607000 : void XcsParser::handleProp(xmlreader::XmlReader & reader) {
427 607000 : bool hasName = false;
428 607000 : OUString name;
429 607000 : valueParser_.type_ = TYPE_ERROR;
430 607000 : bool localized = false;
431 607000 : bool nillable = true;
432 : for (;;) {
433 : int attrNsId;
434 2216750 : xmlreader::Span attrLn;
435 2216750 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
436 607000 : break;
437 : }
438 1609750 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
439 607000 : hasName = true;
440 607000 : name = reader.getAttributeValue(false).convertFromUtf8();
441 2005500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
442 1002750 : attrLn.equals("type"))
443 : {
444 : valueParser_.type_ = xmldata::parseType(
445 607000 : reader, reader.getAttributeValue(true));
446 791500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
447 395750 : attrLn.equals("localized"))
448 : {
449 52000 : localized = xmldata::parseBoolean(reader.getAttributeValue(true));
450 687500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
451 343750 : attrLn.equals("nillable"))
452 : {
453 343750 : nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
454 : }
455 1609750 : }
456 607000 : if (!hasName) {
457 : throw css::uno::RuntimeException(
458 0 : "no prop name attribute in " + reader.getUrl());
459 : }
460 607000 : 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 99500 : valueParser_.getLayer(), valueParser_.type_, nillable))
470 : : rtl::Reference< Node >(
471 : new PropertyNode(
472 557250 : valueParser_.getLayer(), valueParser_.type_, nillable,
473 557250 : css::uno::Any(), false))),
474 1821000 : name));
475 607000 : }
476 :
477 375250 : void XcsParser::handlePropValue(
478 : xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
479 : {
480 375250 : xmlreader::Span attrSeparator;
481 : for (;;) {
482 : int attrNsId;
483 377750 : xmlreader::Span attrLn;
484 377750 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
485 375250 : break;
486 : }
487 5000 : if (attrNsId == ParseManager::NAMESPACE_OOR &&
488 2500 : attrLn.equals("separator"))
489 : {
490 2500 : attrSeparator = reader.getAttributeValue(false);
491 2500 : if (attrSeparator.length == 0) {
492 : throw css::uno::RuntimeException(
493 0 : "bad oor:separator attribute in " + reader.getUrl());
494 : }
495 : }
496 2500 : }
497 750500 : valueParser_.separator_ = OString(
498 375250 : attrSeparator.begin, attrSeparator.length);
499 750500 : valueParser_.start(property);
500 375250 : }
501 :
502 188500 : void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
503 188500 : bool hasName = false;
504 188500 : OUString name;
505 188500 : bool extensible = false;
506 : for (;;) {
507 : int attrNsId;
508 382750 : xmlreader::Span attrLn;
509 382750 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
510 188500 : break;
511 : }
512 194250 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
513 188500 : hasName = true;
514 188500 : name = reader.getAttributeValue(false).convertFromUtf8();
515 11500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
516 5750 : attrLn.equals("extensible"))
517 : {
518 5500 : extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
519 : }
520 194250 : }
521 188500 : if (!hasName) {
522 : throw css::uno::RuntimeException(
523 0 : "no group name attribute in " + reader.getUrl());
524 : }
525 188500 : if (isTemplate) {
526 46500 : name = Data::fullTemplateName(componentName_, name);
527 : }
528 : elements_.push(
529 : Element(
530 : new GroupNode(
531 188500 : valueParser_.getLayer(), extensible,
532 188500 : isTemplate ? name : OUString()),
533 377000 : name));
534 188500 : }
535 :
536 59750 : void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
537 59750 : bool hasName = false;
538 59750 : OUString name;
539 119500 : OUString component(componentName_);
540 59750 : bool hasNodeType = false;
541 119500 : OUString nodeType;
542 : for (;;) {
543 : int attrNsId;
544 189500 : xmlreader::Span attrLn;
545 189500 : if (!reader.nextAttribute(&attrNsId, &attrLn)) {
546 59750 : break;
547 : }
548 129750 : if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn.equals("name")) {
549 59750 : hasName = true;
550 59750 : name = reader.getAttributeValue(false).convertFromUtf8();
551 140000 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
552 70000 : attrLn.equals("component"))
553 : {
554 10250 : component = reader.getAttributeValue(false).convertFromUtf8();
555 119500 : } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
556 59750 : attrLn.equals("node-type"))
557 : {
558 59750 : hasNodeType = true;
559 59750 : nodeType = reader.getAttributeValue(false).convertFromUtf8();
560 : }
561 129750 : }
562 59750 : if (!hasName) {
563 : throw css::uno::RuntimeException(
564 0 : "no set name attribute in " + reader.getUrl());
565 : }
566 59750 : if (isTemplate) {
567 2250 : name = Data::fullTemplateName(componentName_, name);
568 : }
569 : elements_.push(
570 : Element(
571 : new SetNode(
572 59750 : valueParser_.getLayer(),
573 : xmldata::parseTemplateReference(
574 : component, hasNodeType, nodeType, 0),
575 59750 : isTemplate ? name : OUString()),
576 179250 : name));
577 59750 : }
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: */
|