Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/xmloff/source/text/XMLTextListBlockContext.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 <com/sun/star/beans/XPropertySet.hpp>
21
#include <xmloff/xmlimp.hxx>
22
#include <xmloff/xmlnamespace.hxx>
23
#include <xmloff/xmltoken.hxx>
24
#include "XMLTextListItemContext.hxx"
25
#include "XMLTextListBlockContext.hxx"
26
#include <txtlists.hxx>
27
#include <sal/log.hxx>
28
29
30
using namespace ::com::sun::star;
31
using namespace ::com::sun::star::uno;
32
using namespace ::com::sun::star::beans;
33
using namespace ::xmloff::token;
34
35
36
// OD 2008-05-07 #refactorlists#
37
// add optional parameter <bRestartNumberingAtSubList> and its handling
38
XMLTextListBlockContext::XMLTextListBlockContext(
39
        SvXMLImport& rImport,
40
        XMLTextImportHelper& rTxtImp,
41
        const Reference< xml::sax::XFastAttributeList > & xAttrList,
42
        const bool bRestartNumberingAtSubList )
43
72.7k
:   SvXMLImportContext( rImport )
44
72.7k
,   mrTxtImport( rTxtImp )
45
72.7k
,   mnLevel( 0 )
46
72.7k
,   mbRestartNumbering( false )
47
72.7k
,   mbSetDefaults( false )
48
72.7k
{
49
72.7k
    static constexpr OUString s_PropNameDefaultListId = u"DefaultListId"_ustr;
50
72.7k
    {
51
        // get the parent list block context (if any); this is a bit ugly...
52
72.7k
        XMLTextListBlockContext * pLB(nullptr);
53
72.7k
        XMLTextListItemContext  * pLI(nullptr);
54
72.7k
        XMLNumberedParaContext  * pNP(nullptr);
55
72.7k
        rTxtImp.GetTextListHelper().ListContextTop(pLB, pLI, pNP);
56
72.7k
        mxParentListBlock = pLB;
57
72.7k
    }
58
    // Inherit style name from parent list, as well as the flags whether
59
    // numbering must be restarted and formats have to be created.
60
72.7k
    OUString sParentListStyleName;
61
72.7k
    if( mxParentListBlock.is() )
62
0
    {
63
0
        XMLTextListBlockContext *pParent = mxParentListBlock.get();
64
0
        msListStyleName = pParent->msListStyleName;
65
0
        sParentListStyleName = msListStyleName;
66
0
        mxNumRules = pParent->GetNumRules();
67
0
        mnLevel = pParent->GetLevel() + 1;
68
0
        mbRestartNumbering = pParent->IsRestartNumbering() ||
69
0
                             bRestartNumberingAtSubList;
70
0
        mbSetDefaults = pParent->mbSetDefaults;
71
0
        msListId = pParent->GetListId();
72
0
        msContinueListId = pParent->GetContinueListId();
73
0
    }
74
75
72.7k
    bool bIsContinueNumberingAttributePresent( false );
76
72.7k
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
77
9
    {
78
9
        switch( aIter.getToken() )
79
9
        {
80
0
        case XML_ELEMENT(XML, XML_ID):
81
//FIXME: there is no UNO API for lists
82
            // xml:id is also the list ID (#i92221#)
83
0
            if ( mnLevel == 0 ) // root <list> element
84
0
            {
85
0
                msListId = aIter.toString();
86
0
            }
87
0
            break;
88
0
        case XML_ELEMENT(TEXT, XML_CONTINUE_NUMBERING):
89
0
            mbRestartNumbering = !IsXMLToken(aIter, XML_TRUE);
90
0
            bIsContinueNumberingAttributePresent = true;
91
0
            break;
92
0
        case XML_ELEMENT(TEXT, XML_STYLE_NAME):
93
0
            msListStyleName = aIter.toString();
94
0
            break;
95
0
        case XML_ELEMENT(TEXT, XML_CONTINUE_LIST):
96
0
            if ( mnLevel == 0 ) // root <list> element
97
0
            {
98
0
                msContinueListId = aIter.toString();
99
0
            }
100
0
            break;
101
9
        default:
102
9
            XMLOFF_WARN_UNKNOWN("xmloff", aIter);
103
9
        }
104
9
    }
105
106
    // Remember this list block.
107
72.7k
    mrTxtImport.GetTextListHelper().PushListContext( this );
108
72.7k
    try
109
72.7k
    {
110
72.7k
        mxNumRules = XMLTextListsHelper::MakeNumRule(GetImport(), mxNumRules,
111
72.7k
            sParentListStyleName, msListStyleName,
112
72.7k
            mnLevel, &mbRestartNumbering, &mbSetDefaults );
113
72.7k
        if( !mxNumRules.is() )
114
0
            return;
115
116
72.7k
        if ( mnLevel != 0 ) // root <list> element
117
0
            return;
118
119
72.7k
        XMLTextListsHelper& rTextListsHelper( mrTxtImport.GetTextListHelper() );
120
        // Inconsistent behavior regarding lists (#i92811#)
121
72.7k
        OUString sListStyleDefaultListId;
122
72.7k
        {
123
72.7k
            uno::Reference< beans::XPropertySet > xNumRuleProps( mxNumRules, UNO_QUERY );
124
72.7k
            if ( xNumRuleProps.is() )
125
66.2k
            {
126
66.2k
                uno::Reference< beans::XPropertySetInfo > xNumRulePropSetInfo(
127
66.2k
                                            xNumRuleProps->getPropertySetInfo());
128
66.2k
                if (xNumRulePropSetInfo.is() &&
129
66.2k
                    xNumRulePropSetInfo->hasPropertyByName(
130
66.2k
                         s_PropNameDefaultListId))
131
66.2k
                {
132
66.2k
                    xNumRuleProps->getPropertyValue(s_PropNameDefaultListId)
133
66.2k
                        >>= sListStyleDefaultListId;
134
66.2k
                    SAL_WARN_IF( sListStyleDefaultListId.isEmpty(), "xmloff",
135
66.2k
                                "no default list id found at numbering rules instance. Serious defect." );
136
66.2k
                }
137
66.2k
            }
138
72.7k
        }
139
72.7k
        if ( msListId.isEmpty() )  // no text:id property found
140
72.7k
        {
141
72.7k
            sal_Int32 nUPD( 0 );
142
72.7k
            sal_Int32 nBuild( 0 );
143
72.7k
            const bool bBuildIdFound = GetImport().getBuildIds( nUPD, nBuild );
144
72.7k
            if ( rImport.IsTextDocInOOoFileFormat() ||
145
72.7k
                 ( bBuildIdFound && nUPD == 680 ) )
146
0
            {
147
                /* handling former documents written by OpenOffice.org:
148
                   use default list id of numbering rules instance, if existing
149
                   (#i92811#)
150
                */
151
0
                if ( !sListStyleDefaultListId.isEmpty() )
152
0
                {
153
0
                    msListId = sListStyleDefaultListId;
154
0
                    if ( !bIsContinueNumberingAttributePresent &&
155
0
                         !mbRestartNumbering &&
156
0
                         rTextListsHelper.IsListProcessed( msListId ) )
157
0
                    {
158
0
                        mbRestartNumbering = true;
159
0
                    }
160
0
                }
161
0
            }
162
72.7k
            if ( msListId.isEmpty() )
163
72.7k
            {
164
                // generate a new list id for the list
165
72.7k
                msListId = rTextListsHelper.GenerateNewListId();
166
72.7k
            }
167
72.7k
        }
168
169
72.7k
        if ( bIsContinueNumberingAttributePresent && !mbRestartNumbering &&
170
0
             msContinueListId.isEmpty() )
171
0
        {
172
0
            const OUString& Last( rTextListsHelper.GetLastProcessedListId() );
173
0
            if ( rTextListsHelper.GetListStyleOfLastProcessedList() == msListStyleName
174
0
                 && Last != msListId )
175
0
            {
176
0
                msContinueListId = Last;
177
0
            }
178
0
        }
179
180
72.7k
        bool bContinueNumbering = bIsContinueNumberingAttributePresent && !mbRestartNumbering;
181
72.7k
        if (msContinueListId.isEmpty() && bContinueNumbering && GetImport().IsMSO())
182
0
        {
183
            // No "continue list" id, but continue numbering was requested. Connect to the last list of
184
            // the same list style in the Word case, even if there was a different list in the meantime.
185
0
            msContinueListId = rTextListsHelper.GetLastIdOfStyleName(msListStyleName);
186
0
        }
187
188
72.7k
        if ( !msContinueListId.isEmpty() )
189
0
        {
190
0
            if ( !rTextListsHelper.IsListProcessed( msContinueListId ) )
191
0
            {
192
0
                msContinueListId.clear();
193
0
            }
194
0
            else
195
0
            {
196
                // search continue list chain for master list and
197
                // continue the master list.
198
0
                OUString sTmpStr =
199
0
                    rTextListsHelper.GetContinueListIdOfProcessedList( msContinueListId );
200
0
                while ( !sTmpStr.isEmpty() )
201
0
                {
202
0
                    msContinueListId = sTmpStr;
203
204
0
                    sTmpStr =
205
0
                        rTextListsHelper.GetContinueListIdOfProcessedList( msContinueListId );
206
0
                }
207
0
            }
208
0
        }
209
210
72.7k
        if ( !rTextListsHelper.IsListProcessed( msListId ) )
211
72.7k
        {
212
            // Inconsistent behavior regarding lists (#i92811#)
213
72.7k
            rTextListsHelper.KeepListAsProcessed(
214
72.7k
                msListId, msListStyleName, msContinueListId,
215
72.7k
                sListStyleDefaultListId );
216
72.7k
        }
217
72.7k
    }
218
72.7k
    catch (uno::Exception&)
219
72.7k
    {
220
        // pop ourselves if anything goes wrong to avoid use-after-free
221
0
        rTxtImp.GetTextListHelper().PopListContext();
222
0
        throw;
223
0
    }
224
72.7k
}
225
226
XMLTextListBlockContext::~XMLTextListBlockContext()
227
72.7k
{
228
72.7k
}
229
230
void XMLTextListBlockContext::endFastElement(sal_Int32 )
231
72.7k
{
232
    // Numbering has not to be restarted if it has been restarted within
233
    // a child list.
234
72.7k
    XMLTextListBlockContext *pParent = mxParentListBlock.get();
235
72.7k
    if( pParent )
236
0
    {
237
0
        pParent->mbRestartNumbering = mbRestartNumbering;
238
0
    }
239
240
    // Restore current list block.
241
72.7k
    mrTxtImport.GetTextListHelper().PopListContext();
242
243
    // Any paragraph following the list within the same list item must not
244
    // be numbered.
245
72.7k
    mrTxtImport.GetTextListHelper().SetListItem( nullptr );
246
72.7k
}
247
248
css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTextListBlockContext::createFastChildContext(
249
    sal_Int32 nElement,
250
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
251
580
{
252
580
    SvXMLImportContext *pContext = nullptr;
253
254
580
    bool bHeader = false;
255
580
    switch( nElement )
256
580
    {
257
0
    case XML_ELEMENT(TEXT, XML_LIST_HEADER):
258
0
        bHeader = true;
259
0
        [[fallthrough]];
260
0
    case XML_ELEMENT(TEXT, XML_LIST_ITEM):
261
0
        pContext = new XMLTextListItemContext( GetImport(), mrTxtImport,
262
0
                                              xAttrList, bHeader );
263
0
        break;
264
580
    default:
265
580
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
266
580
    }
267
268
580
    return pContext;
269
580
}
270
271
272
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */