Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/mathml/importutils.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
10
#include <oox/mathml/importutils.hxx>
11
12
#include <assert.h>
13
14
#include <com/sun/star/xml/FastAttribute.hpp>
15
#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
16
#include <oox/token/tokenmap.hxx>
17
#include <oox/token/tokens.hxx>
18
#include <oox/token/namespaces.hxx>
19
#include <rtl/ustring.hxx>
20
#include <sal/log.hxx>
21
#include <utility>
22
23
16.2k
#define OPENING( token ) XML_STREAM_OPENING( token )
24
16.2k
#define CLOSING( token ) XML_STREAM_CLOSING( token )
25
26
using namespace com::sun::star;
27
28
namespace oox::formulaimport
29
{
30
31
namespace
32
{
33
// a class that inherits from AttributeList, builds the internal data and then will be sliced off
34
// during conversion to the base class
35
class AttributeListBuilder
36
    : public XmlStream::AttributeList
37
{
38
public:
39
    explicit AttributeListBuilder( const uno::Reference< xml::sax::XFastAttributeList >& a );
40
};
41
42
AttributeListBuilder::AttributeListBuilder( const uno::Reference< xml::sax::XFastAttributeList >& a )
43
32.4k
{
44
32.4k
    if( !a )
45
16.4k
        return;
46
15.9k
    const uno::Sequence< xml::FastAttribute > aFastAttrSeq = a->getFastAttributes();
47
15.9k
    for( const xml::FastAttribute& rFastAttr : aFastAttrSeq )
48
10.3k
    {
49
10.3k
        attrs[ rFastAttr.Token ] = rFastAttr.Value;
50
10.3k
    }
51
15.9k
}
52
53
OString tokenToString( int token )
54
0
{
55
0
    uno::Sequence<sal_Int8> const& aTokenNameSeq = TokenMap::getUtf8TokenName(token & TOKEN_MASK);
56
0
    OString tokenname( reinterpret_cast< const char* >( aTokenNameSeq.getConstArray() ), aTokenNameSeq.getLength() );
57
0
    if( tokenname.isEmpty())
58
0
        tokenname = "???"_ostr;
59
0
    int nmsp = ( token & NMSP_MASK & ~( TAG_OPENING | TAG_CLOSING ));
60
#if 0 // this is awfully long
61
    OString namespacename = StaticNamespaceMap::get().count( nmsp ) != 0
62
        ? StaticNamespaceMap::get()[ nmsp ] : OString( "???" );
63
#else
64
0
    OString namespacename;
65
    // only few are needed actually
66
0
    switch( nmsp )
67
0
    {
68
0
        case NMSP_officeMath:
69
0
            namespacename = "m"_ostr;
70
0
            break;
71
0
        case NMSP_doc:
72
0
            namespacename = "w"_ostr;
73
0
            break;
74
0
        default:
75
0
            namespacename = "?"_ostr;
76
0
            break;
77
0
    }
78
0
#endif
79
0
    if( token == OPENING( token ))
80
0
        return "<" + namespacename + ":" + tokenname + ">";
81
0
    if( token == CLOSING( token ))
82
0
        return "</" + namespacename + ":" + tokenname + ">";
83
    // just the name itself, not specified whether opening or closing
84
0
    return namespacename + ":" + tokenname;
85
0
}
86
87
} // namespace
88
89
OUString& XmlStream::AttributeList::operator[] (int token)
90
0
{
91
0
    return attrs[token];
92
0
}
93
94
OUString XmlStream::AttributeList::attribute( int token, const OUString& def ) const
95
0
{
96
0
    std::map< int, OUString >::const_iterator find = attrs.find( token );
97
0
    if( find != attrs.end())
98
0
        return find->second;
99
0
    return def;
100
0
}
101
102
bool XmlStream::AttributeList::attribute( int token, bool def ) const
103
0
{
104
0
    std::map< int, OUString >::const_iterator find = attrs.find( token );
105
0
    if( find != attrs.end())
106
0
    {
107
0
        const OUString sValue = find->second;
108
0
        if( sValue.equalsIgnoreAsciiCase("true") ||
109
0
            sValue.equalsIgnoreAsciiCase("on") ||
110
0
            sValue.equalsIgnoreAsciiCase("t") ||
111
0
            sValue.equalsIgnoreAsciiCase("1") )
112
0
            return true;
113
0
        if( sValue.equalsIgnoreAsciiCase("false") ||
114
0
            sValue.equalsIgnoreAsciiCase("off") ||
115
0
            sValue.equalsIgnoreAsciiCase("f") ||
116
0
            sValue.equalsIgnoreAsciiCase("0") )
117
0
            return false;
118
0
        SAL_WARN( "oox.xmlstream", "Cannot convert \'" << sValue << "\' to bool." );
119
0
    }
120
0
    return def;
121
0
}
122
123
sal_Unicode XmlStream::AttributeList::attribute( int token, sal_Unicode def ) const
124
0
{
125
0
    std::map< int, OUString >::const_iterator find = attrs.find( token );
126
0
    if( find != attrs.end())
127
0
    {
128
0
        if( !find->second.isEmpty() )
129
0
        {
130
0
            if( find->second.getLength() != 1 )
131
0
                SAL_WARN( "oox.xmlstream", "Cannot convert \'" << find->second << "\' to sal_Unicode, stripping." );
132
0
            return find->second[ 0 ];
133
0
        }
134
0
    }
135
0
    return def;
136
0
}
137
138
XmlStream::Tag::Tag( int t, const uno::Reference< xml::sax::XFastAttributeList >& a )
139
32.4k
: token( t )
140
32.4k
, attributes( AttributeListBuilder( a ))
141
32.4k
{
142
32.4k
}
143
144
XmlStream::Tag::Tag( int t, AttributeList a )
145
0
: token( t )
146
0
, attributes(std::move( a ))
147
0
{
148
0
}
149
150
XmlStream::Tag::operator bool() const
151
0
{
152
0
    return token != XML_TOKEN_INVALID;
153
0
}
154
155
XmlStream::XmlStream()
156
10.7k
: pos( 0 )
157
10.7k
{
158
    // make sure our extra bit does not conflict with values used by oox
159
10.7k
    assert( TAG_OPENING > ( 1024 << NMSP_SHIFT ));
160
10.7k
}
161
162
bool XmlStream::atEnd() const
163
0
{
164
0
    return pos >= tags.size();
165
0
}
166
167
XmlStream::Tag XmlStream::currentTag() const
168
0
{
169
0
    if( pos >= tags.size())
170
0
        return Tag();
171
0
    return tags[ pos ];
172
0
}
173
174
int XmlStream::currentToken() const
175
0
{
176
0
    if( pos >= tags.size())
177
0
        return XML_TOKEN_INVALID;
178
0
    return tags[ pos ].token;
179
0
}
180
181
void XmlStream::moveToNextTag()
182
0
{
183
0
    if( pos < tags.size())
184
0
        ++pos;
185
0
}
186
187
XmlStream::Tag XmlStream::ensureOpeningTag( int token )
188
0
{
189
0
    return checkTag( OPENING( token ), false );
190
0
}
191
192
XmlStream::Tag XmlStream::checkOpeningTag( int token )
193
0
{
194
0
    return checkTag( OPENING( token ), true );
195
0
}
196
197
void XmlStream::ensureClosingTag( int token )
198
0
{
199
0
    checkTag( CLOSING( token ), false );
200
0
}
201
202
XmlStream::Tag XmlStream::checkTag( int token, bool optional )
203
0
{
204
    // either it's the following tag, or find it
205
0
    int savedPos = pos;
206
0
    if( optional )
207
0
    { // avoid printing debug messages about skipping tags if the optional one
208
      // will not be found and the position will be reset back
209
0
        if( currentToken() != token && !findTagInternal( token, true ))
210
0
        {
211
0
            pos = savedPos;
212
0
            return Tag();
213
0
        }
214
0
    }
215
0
    if( currentToken() == token || findTag( token ))
216
0
    {
217
0
        Tag ret = currentTag();
218
0
        moveToNextTag();
219
0
        return ret; // ok
220
0
    }
221
0
    if( optional )
222
0
    { // not a problem, just rewind
223
0
        pos = savedPos;
224
0
        return Tag();
225
0
    }
226
0
    SAL_WARN( "oox.xmlstream", "Expected tag " << tokenToString( token ) << " not found." );
227
0
    return Tag();
228
0
}
229
230
bool XmlStream::findTag( int token )
231
0
{
232
0
    return findTagInternal( token, false );
233
0
}
234
235
bool XmlStream::findTagInternal( int token, bool silent )
236
0
{
237
0
    int depth = 0;
238
0
    for(;
239
0
         !atEnd();
240
0
         moveToNextTag())
241
0
    {
242
0
        if( depth > 0 ) // we're inside a nested element, skip those
243
0
        {
244
0
            if( currentToken() == OPENING( currentToken()))
245
0
            {
246
0
                if( !silent )
247
0
                    SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken()));
248
0
                ++depth;
249
0
            }
250
0
            else if( currentToken() == CLOSING( currentToken()))
251
0
            {
252
0
                if( !silent )
253
0
                    SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken()));
254
0
                --depth;
255
0
            }
256
0
            else
257
0
            {
258
0
                if( !silent )
259
0
                    SAL_WARN( "oox.xmlstream", "Malformed token " << currentToken() << " ("
260
0
                        << tokenToString( currentToken()) << ")" );
261
0
                abort();
262
0
            }
263
0
            continue;
264
0
        }
265
0
        if( currentToken() == token )
266
0
            return true; // ok, found
267
0
        if( currentToken() == CLOSING( currentToken()))
268
0
            return false; // that would be leaving current element, so not found
269
0
        if( currentToken() == OPENING( currentToken()))
270
0
        {
271
0
            if( !silent )
272
0
                SAL_INFO( "oox.xmlstream", "Skipping tag " << tokenToString( currentToken()));
273
0
            ++depth;
274
0
        }
275
0
        else
276
0
            abort();
277
0
    }
278
0
    if( !silent )
279
0
        SAL_WARN( "oox.xmlstream", "Unexpected end of stream reached." );
280
0
    return false;
281
0
}
282
283
void XmlStream::skipElementInternal( int token, bool silent )
284
0
{
285
0
    int closing = ( token & ~TAG_OPENING ) | TAG_CLOSING; // make it a closing tag
286
0
    assert( currentToken() == OPENING( token ));
287
0
    if( !silent )
288
0
        SAL_INFO( "oox.xmlstream", "Skipping unexpected element " << tokenToString( currentToken()));
289
0
    moveToNextTag();
290
    // and just find the matching closing tag
291
0
    if( findTag( closing ))
292
0
    {
293
0
        if( !silent )
294
0
            SAL_INFO( "oox.xmlstream", "Skipped unexpected element " << tokenToString( token ));
295
0
        moveToNextTag(); // and skip it too
296
0
        return;
297
0
    }
298
    // this one is an unexpected problem, do not silent it
299
0
    SAL_WARN( "oox.xmlstream", "Expected end of element " << tokenToString( token ) << " not found." );
300
0
}
301
302
void XmlStream::handleUnexpectedTag()
303
0
{
304
0
    if( atEnd())
305
0
        return;
306
0
    if( currentToken() == CLOSING( currentToken()))
307
0
    {
308
0
        SAL_INFO( "oox.xmlstream", "Skipping unexpected tag " << tokenToString( currentToken()));
309
0
        moveToNextTag(); // just skip it
310
0
        return;
311
0
    }
312
0
    skipElementInternal( currentToken(), false ); // otherwise skip the entire element
313
0
}
314
315
std::vector<OUString> XmlStream::tagAttributesWithin( int nEnclosingToken, int nTagToken,
316
    int nAttributeToken ) const
317
0
{
318
0
    std::vector<OUString> aValues;
319
0
    int nDepth = 0;
320
0
    for( const Tag& rTag : tags )
321
0
    {
322
0
        if( rTag.token == OPENING( nEnclosingToken ))
323
0
            ++nDepth;
324
0
        else if( rTag.token == CLOSING( nEnclosingToken ))
325
0
            --nDepth;
326
0
        else if( nDepth > 0 && rTag.token == OPENING( nTagToken ))
327
0
            aValues.push_back( rTag.attribute( nAttributeToken ));
328
0
    }
329
0
    return aValues;
330
0
}
331
332
void XmlStreamBuilder::appendOpeningTag( int token, const uno::Reference< xml::sax::XFastAttributeList >& attrs )
333
16.2k
{
334
16.2k
    tags.emplace_back( OPENING( token ), attrs );
335
16.2k
}
336
337
void XmlStreamBuilder::appendOpeningTag( int token, const AttributeList& attrs )
338
0
{
339
0
    tags.emplace_back( OPENING( token ), attrs );
340
0
}
341
342
void XmlStreamBuilder::appendClosingTag( int token )
343
16.2k
{
344
16.2k
    tags.emplace_back( CLOSING( token ));
345
16.2k
}
346
347
void XmlStreamBuilder::appendCharacters( std::u16string_view chars )
348
1.99k
{
349
    assert( !tags.empty());
350
1.99k
    tags.back().text += chars;
351
1.99k
}
352
353
} // namespace
354
355
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */