Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/unoxml/source/dom/saxbuilder.cxx
Line
Count
Source
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 "saxbuilder.hxx"
21
22
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
23
#include <com/sun/star/xml/sax/SAXException.hpp>
24
#include <cppuhelper/supportsservice.hxx>
25
#include <sax/fastattribs.hxx>
26
#include <xmloff/xmlimp.hxx>
27
28
using namespace css::lang;
29
using namespace css::uno;
30
using namespace css::xml::dom;
31
using namespace css::xml::sax;
32
33
namespace DOM
34
{
35
    CSAXDocumentBuilder::CSAXDocumentBuilder(const Reference< XComponentContext >& ctx)
36
18.1k
        : m_xContext(ctx)
37
18.1k
        , m_aState( SAXDocumentBuilderState_READY)
38
18.1k
    {}
39
40
    Sequence< OUString > SAL_CALL CSAXDocumentBuilder::getSupportedServiceNames()
41
0
    {
42
0
        return { u"com.sun.star.xml.dom.SAXDocumentBuilder"_ustr };
43
0
    }
44
45
    OUString SAL_CALL CSAXDocumentBuilder::getImplementationName()
46
0
    {
47
0
        return u"com.sun.star.comp.xml.dom.SAXDocumentBuilder"_ustr;
48
0
    }
49
50
    sal_Bool SAL_CALL CSAXDocumentBuilder::supportsService(const OUString& aServiceName)
51
0
    {
52
0
        return cppu::supportsService(this, aServiceName);
53
0
    }
54
55
    SAXDocumentBuilderState SAL_CALL CSAXDocumentBuilder::getState()
56
0
    {
57
0
        std::scoped_lock g(m_Mutex);
58
59
0
        return m_aState;
60
0
    }
61
62
    void SAL_CALL CSAXDocumentBuilder::reset()
63
0
    {
64
0
        std::scoped_lock g(m_Mutex);
65
66
0
        m_aDocument.clear();
67
0
        m_aFragment.clear();
68
0
        while (!m_aNodeStack.empty()) m_aNodeStack.pop();
69
0
        m_aState = SAXDocumentBuilderState_READY;
70
0
    }
71
72
    Reference< XDocument > SAL_CALL CSAXDocumentBuilder::getDocument()
73
6.36k
    {
74
6.36k
        std::scoped_lock g(m_Mutex);
75
76
6.36k
        if (m_aState != SAXDocumentBuilderState_DOCUMENT_FINISHED)
77
0
            throw RuntimeException();
78
79
6.36k
        return m_aDocument;
80
6.36k
    }
81
82
    Reference< XDocumentFragment > SAL_CALL CSAXDocumentBuilder::getDocumentFragment()
83
0
    {
84
0
        std::scoped_lock g(m_Mutex);
85
86
0
        if (m_aState != SAXDocumentBuilderState_FRAGMENT_FINISHED)
87
0
            throw RuntimeException();
88
0
        return m_aFragment;
89
0
    }
90
91
    void SAL_CALL CSAXDocumentBuilder::startDocumentFragment(const Reference< XDocument >& ownerDoc)
92
0
    {
93
0
        std::scoped_lock g(m_Mutex);
94
95
        // start a new document fragment and push it onto the stack
96
        // we have to be in a clean state to do this
97
0
        if (m_aState != SAXDocumentBuilderState_READY)
98
0
            throw RuntimeException();
99
100
0
        m_aDocument = ownerDoc;
101
0
        Reference< XDocumentFragment > aFragment = m_aDocument->createDocumentFragment();
102
0
        m_aNodeStack.push(aFragment);
103
0
        m_aFragment = std::move(aFragment);
104
0
        m_aState = SAXDocumentBuilderState_BUILDING_FRAGMENT;
105
0
    }
106
107
    void SAL_CALL CSAXDocumentBuilder::endDocumentFragment()
108
0
    {
109
0
        std::scoped_lock g(m_Mutex);
110
111
        // there should only be the document left on the node stack
112
0
        if (m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
113
0
            throw RuntimeException();
114
115
0
        Reference< XNode > aNode = m_aNodeStack.top();
116
0
        if ( aNode->getNodeType() != NodeType_DOCUMENT_FRAGMENT_NODE)
117
0
            throw RuntimeException();
118
0
        m_aNodeStack.pop();
119
0
        m_aState = SAXDocumentBuilderState_FRAGMENT_FINISHED;
120
0
    }
121
122
    //XFastDocumentHandler
123
    void SAL_CALL CSAXDocumentBuilder::startDocument()
124
18.1k
    {
125
18.1k
        std::scoped_lock g(m_Mutex);
126
127
        // start a new document and push it onto the stack
128
        // we have to be in a clean state to do this
129
18.1k
        if (m_aState != SAXDocumentBuilderState_READY)
130
0
            throw SAXException();
131
132
18.1k
        Reference< XDocumentBuilder > aBuilder(DocumentBuilder::create(m_xContext));
133
18.1k
        Reference< XDocument > aDocument = aBuilder->newDocument();
134
18.1k
        m_aNodeStack.push(aDocument);
135
18.1k
        m_aDocument = std::move(aDocument);
136
18.1k
        m_aState = SAXDocumentBuilderState_BUILDING_DOCUMENT;
137
18.1k
    }
138
139
    void SAL_CALL CSAXDocumentBuilder::endDocument()
140
6.36k
    {
141
6.36k
        std::scoped_lock g(m_Mutex);
142
143
        // there should only be the document left on the node stack
144
6.36k
        if (m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT)
145
0
            throw SAXException();
146
147
6.36k
        Reference< XNode > aNode = m_aNodeStack.top();
148
6.36k
        if ( aNode->getNodeType() != NodeType_DOCUMENT_NODE)
149
0
            throw SAXException();
150
6.36k
        m_aNodeStack.pop();
151
6.36k
        m_aState = SAXDocumentBuilderState_DOCUMENT_FINISHED;
152
6.36k
    }
153
154
    void SAL_CALL CSAXDocumentBuilder::processingInstruction( const OUString& rTarget, const OUString& rData )
155
0
    {
156
0
        std::scoped_lock g(m_Mutex);
157
158
        //  append PI node to the current top
159
0
        if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
160
0
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
161
0
            throw SAXException();
162
163
0
        Reference< XProcessingInstruction > aInstruction = m_aDocument->createProcessingInstruction(
164
0
                rTarget, rData);
165
0
        m_aNodeStack.top()->appendChild(aInstruction);
166
0
    }
167
168
    void SAL_CALL CSAXDocumentBuilder::setDocumentLocator( const Reference< XLocator >& )
169
0
    {
170
0
    }
171
172
    void SAL_CALL CSAXDocumentBuilder::startFastElement( sal_Int32 nElement , const Reference< XFastAttributeList >& xAttribs  )
173
166k
    {
174
166k
        std::scoped_lock g(m_Mutex);
175
176
166k
        if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
177
1.75k
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
178
1.75k
        {
179
1.75k
            throw SAXException();
180
1.75k
        }
181
182
164k
        Reference< XElement > aElement;
183
164k
        const OUString aPrefix(SvXMLImport::getNamespacePrefixFromToken(nElement, nullptr));
184
164k
        const OUString aURI( SvXMLImport::getNamespaceURIFromToken( nElement ) );
185
164k
        OUString aQualifiedName( SvXMLImport::getNameFromToken( nElement ) );
186
164k
        if( !aPrefix.isEmpty() )
187
87.2k
            aQualifiedName = aPrefix + SvXMLImport::aNamespaceSeparator + aQualifiedName;
188
189
164k
        if ( !aURI.isEmpty() )
190
87.2k
        {
191
            // found a URI for prefix
192
            // qualified name
193
87.2k
            aElement = m_aDocument->createElementNS( aURI, aQualifiedName );
194
87.2k
        }
195
77.5k
        else
196
77.5k
        {
197
            // no URI for prefix
198
77.5k
            aElement = m_aDocument->createElement( aQualifiedName );
199
77.5k
        }
200
164k
        aElement.set( m_aNodeStack.top()->appendChild(aElement), UNO_QUERY);
201
164k
        m_aNodeStack.push(aElement);
202
203
164k
        if (xAttribs.is())
204
164k
            setElementFastAttributes(aElement, xAttribs);
205
164k
    }
206
207
    // For arbitrary meta elements
208
    void SAL_CALL CSAXDocumentBuilder::startUnknownElement( const OUString& rNamespace, const OUString& rName, const Reference< XFastAttributeList >& xAttribs )
209
741k
    {
210
741k
        std::scoped_lock g(m_Mutex);
211
212
741k
        if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
213
0
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
214
0
        {
215
0
            throw SAXException();
216
0
        }
217
218
741k
        Reference< XElement > aElement;
219
741k
        if ( !rNamespace.isEmpty() )
220
90.3k
            aElement = m_aDocument->createElementNS( rNamespace, rName );
221
650k
        else
222
650k
            aElement = m_aDocument->createElement( rName );
223
224
741k
        aElement.set( m_aNodeStack.top()->appendChild(aElement), UNO_QUERY);
225
741k
        m_aNodeStack.push(aElement);
226
227
741k
        if (!xAttribs.is())
228
0
            return;
229
230
741k
        setElementFastAttributes(aElement, xAttribs);
231
741k
        const Sequence< css::xml::Attribute > unknownAttribs = xAttribs->getUnknownAttributes();
232
741k
        for ( const auto& rUnknownAttrib : unknownAttribs )
233
25.5k
        {
234
25.5k
            const OUString& rAttrValue = rUnknownAttrib.Value;
235
25.5k
            const OUString& rAttrName = rUnknownAttrib.Name;
236
25.5k
            const OUString& rAttrNamespace = rUnknownAttrib.NamespaceURL;
237
25.5k
            if ( !rAttrNamespace.isEmpty() )
238
12.9k
                aElement->setAttributeNS( rAttrNamespace, rAttrName, rAttrValue );
239
12.5k
            else
240
12.5k
                aElement->setAttribute( rAttrName, rAttrValue );
241
25.5k
        }
242
741k
    }
243
244
    void CSAXDocumentBuilder::setElementFastAttributes(const Reference< XElement >& aElement, const Reference< XFastAttributeList >& xAttribs)
245
905k
    {
246
905k
        for (auto &it : sax_fastparser::castToFastAttributeList( xAttribs ))
247
70.7k
        {
248
70.7k
            sal_Int32 nAttrToken = it.getToken();
249
70.7k
            const OUString aAttrPrefix(SvXMLImport::getNamespacePrefixFromToken(nAttrToken, nullptr));
250
70.7k
            const OUString aAttrURI( SvXMLImport::getNamespaceURIFromToken( nAttrToken ) );
251
70.7k
            OUString aAttrQualifiedName( SvXMLImport::getNameFromToken( nAttrToken ) );
252
70.7k
            if( !aAttrPrefix.isEmpty() )
253
69.6k
                aAttrQualifiedName = aAttrPrefix + SvXMLImport::aNamespaceSeparator + aAttrQualifiedName;
254
255
70.7k
            if ( !aAttrURI.isEmpty() )
256
69.6k
                aElement->setAttributeNS( aAttrURI, aAttrQualifiedName, it.toString() );
257
1.13k
            else
258
1.13k
                aElement->setAttribute( aAttrQualifiedName, it.toString() );
259
70.7k
        }
260
905k
    }
261
262
    void SAL_CALL CSAXDocumentBuilder::endFastElement( sal_Int32 nElement )
263
70.7k
    {
264
70.7k
        std::scoped_lock g(m_Mutex);
265
266
        // pop the current element from the stack
267
70.7k
        if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
268
0
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
269
0
            throw SAXException();
270
271
70.7k
        Reference< XNode > aNode(m_aNodeStack.top());
272
70.7k
        if (aNode->getNodeType() != NodeType_ELEMENT_NODE)
273
0
            throw SAXException();
274
275
70.7k
        Reference< XElement > aElement(aNode, UNO_QUERY);
276
70.7k
        if( aElement->getPrefix() != SvXMLImport::getNamespacePrefixFromToken(nElement, nullptr) ||
277
70.6k
            aElement->getTagName() != SvXMLImport::getNameFromToken( nElement ) ) // consistency check
278
71
            throw SAXException();
279
280
        // pop it
281
70.6k
        m_aNodeStack.pop();
282
70.6k
    }
283
284
285
    void SAL_CALL CSAXDocumentBuilder::endUnknownElement( const OUString& /*rNamespace*/, const OUString& rName )
286
73.8k
    {
287
73.8k
        std::scoped_lock g(m_Mutex);
288
289
        // pop the current element from the stack
290
73.8k
        if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
291
0
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
292
0
            throw SAXException();
293
294
73.8k
        Reference< XNode > aNode(m_aNodeStack.top());
295
73.8k
        if (aNode->getNodeType() != NodeType_ELEMENT_NODE)
296
0
            throw SAXException();
297
298
73.8k
        Reference< XElement > aElement(aNode, UNO_QUERY);
299
73.8k
        OUString aRefName;
300
73.8k
        const OUString aPrefix = aElement->getPrefix();
301
73.8k
        if (!aPrefix.isEmpty())
302
14.5k
            aRefName = aPrefix + SvXMLImport::aNamespaceSeparator + aElement->getTagName();
303
59.2k
        else
304
59.2k
            aRefName = aElement->getTagName();
305
73.8k
        if (aRefName != rName) // consistency check
306
15.7k
            throw SAXException();
307
308
        // pop it
309
58.0k
        m_aNodeStack.pop();
310
58.0k
    }
311
312
    Reference< XFastContextHandler > SAL_CALL CSAXDocumentBuilder::createFastChildContext( sal_Int32/* nElement */, const Reference< XFastAttributeList >&/* xAttribs */ )
313
0
    {
314
0
        return nullptr;
315
0
    }
316
317
318
    Reference< XFastContextHandler > SAL_CALL CSAXDocumentBuilder::createUnknownChildContext( const OUString&/* rNamespace */, const OUString&/* rName */, const Reference< XFastAttributeList >&/* xAttribs */ )
319
0
    {
320
0
        return nullptr;
321
0
    }
322
323
    void SAL_CALL CSAXDocumentBuilder::characters( const OUString& rChars )
324
303k
    {
325
303k
        std::scoped_lock g(m_Mutex);
326
327
        //  append text node to the current top element
328
303k
        if (m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT &&
329
0
             m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT)
330
0
            throw SAXException();
331
332
303k
        Reference< XText > aText = m_aDocument->createTextNode(rChars);
333
303k
        m_aNodeStack.top()->appendChild(aText);
334
303k
    }
335
}
336
337
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
338
unoxml_CSAXDocumentBuilder_get_implementation(
339
    css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
340
18.1k
{
341
18.1k
    return cppu::acquire(new DOM::CSAXDocumentBuilder(context));
342
18.1k
}
343
344
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */