Coverage Report

Created: 2025-07-07 10:01

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