Coverage Report

Created: 2025-12-31 10:39

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
93.7k
:   SvXMLImportContext( rImport )
44
93.7k
,   mrTxtImport( rTxtImp )
45
93.7k
,   mnLevel( 0 )
46
93.7k
,   mbRestartNumbering( false )
47
93.7k
,   mbSetDefaults( false )
48
93.7k
{
49
93.7k
    static constexpr OUString s_PropNameDefaultListId = u"DefaultListId"_ustr;
50
93.7k
    {
51
        // get the parent list block context (if any); this is a bit ugly...
52
93.7k
        XMLTextListBlockContext * pLB(nullptr);
53
93.7k
        XMLTextListItemContext  * pLI(nullptr);
54
93.7k
        XMLNumberedParaContext  * pNP(nullptr);
55
93.7k
        rTxtImp.GetTextListHelper().ListContextTop(pLB, pLI, pNP);
56
93.7k
        mxParentListBlock = pLB;
57
93.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
93.7k
    OUString sParentListStyleName;
61
93.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
93.7k
    bool bIsContinueNumberingAttributePresent( false );
76
93.7k
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
77
1
    {
78
1
        switch( aIter.getToken() )
79
1
        {
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
1
        default:
102
1
            XMLOFF_WARN_UNKNOWN("xmloff", aIter);
103
1
        }
104
1
    }
105
106
    // Remember this list block.
107
93.7k
    mrTxtImport.GetTextListHelper().PushListContext( this );
108
93.7k
    try
109
93.7k
    {
110
93.7k
        mxNumRules = XMLTextListsHelper::MakeNumRule(GetImport(), mxNumRules,
111
93.7k
            sParentListStyleName, msListStyleName,
112
93.7k
            mnLevel, &mbRestartNumbering, &mbSetDefaults );
113
93.7k
        if( !mxNumRules.is() )
114
0
            return;
115
116
93.7k
        if ( mnLevel != 0 ) // root <list> element
117
0
            return;
118
119
93.7k
        XMLTextListsHelper& rTextListsHelper( mrTxtImport.GetTextListHelper() );
120
        // Inconsistent behavior regarding lists (#i92811#)
121
93.7k
        OUString sListStyleDefaultListId;
122
93.7k
        {
123
93.7k
            uno::Reference< beans::XPropertySet > xNumRuleProps( mxNumRules, UNO_QUERY );
124
93.7k
            if ( xNumRuleProps.is() )
125
90.8k
            {
126
90.8k
                uno::Reference< beans::XPropertySetInfo > xNumRulePropSetInfo(
127
90.8k
                                            xNumRuleProps->getPropertySetInfo());
128
90.8k
                if (xNumRulePropSetInfo.is() &&
129
90.8k
                    xNumRulePropSetInfo->hasPropertyByName(
130
90.8k
                         s_PropNameDefaultListId))
131
90.8k
                {
132
90.8k
                    xNumRuleProps->getPropertyValue(s_PropNameDefaultListId)
133
90.8k
                        >>= sListStyleDefaultListId;
134
90.8k
                    SAL_WARN_IF( sListStyleDefaultListId.isEmpty(), "xmloff",
135
90.8k
                                "no default list id found at numbering rules instance. Serious defect." );
136
90.8k
                }
137
90.8k
            }
138
93.7k
        }
139
93.7k
        if ( msListId.isEmpty() )  // no text:id property found
140
93.7k
        {
141
93.7k
            sal_Int32 nUPD( 0 );
142
93.7k
            sal_Int32 nBuild( 0 );
143
93.7k
            const bool bBuildIdFound = GetImport().getBuildIds( nUPD, nBuild );
144
93.7k
            if ( rImport.IsTextDocInOOoFileFormat() ||
145
93.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
93.7k
            if ( msListId.isEmpty() )
163
93.7k
            {
164
                // generate a new list id for the list
165
93.7k
                msListId = rTextListsHelper.GenerateNewListId();
166
93.7k
            }
167
93.7k
        }
168
169
93.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
93.7k
        bool bContinueNumbering = bIsContinueNumberingAttributePresent && !mbRestartNumbering;
181
93.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
93.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
93.7k
        if ( !rTextListsHelper.IsListProcessed( msListId ) )
211
93.7k
        {
212
            // Inconsistent behavior regarding lists (#i92811#)
213
93.7k
            rTextListsHelper.KeepListAsProcessed(
214
93.7k
                msListId, msListStyleName, msContinueListId,
215
93.7k
                sListStyleDefaultListId );
216
93.7k
        }
217
93.7k
    }
218
93.7k
    catch (uno::Exception&)
219
93.7k
    {
220
        // pop ourselves if anything goes wrong to avoid use-after-free
221
0
        rTxtImp.GetTextListHelper().PopListContext();
222
0
        throw;
223
0
    }
224
93.7k
}
225
226
XMLTextListBlockContext::~XMLTextListBlockContext()
227
93.7k
{
228
93.7k
}
229
230
void XMLTextListBlockContext::endFastElement(sal_Int32 )
231
93.6k
{
232
    // Numbering has not to be restarted if it has been restarted within
233
    // a child list.
234
93.6k
    XMLTextListBlockContext *pParent = mxParentListBlock.get();
235
93.6k
    if( pParent )
236
0
    {
237
0
        pParent->mbRestartNumbering = mbRestartNumbering;
238
0
    }
239
240
    // Restore current list block.
241
93.6k
    mrTxtImport.GetTextListHelper().PopListContext();
242
243
    // Any paragraph following the list within the same list item must not
244
    // be numbered.
245
93.6k
    mrTxtImport.GetTextListHelper().SetListItem( nullptr );
246
93.6k
}
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
462
{
252
462
    SvXMLImportContext *pContext = nullptr;
253
254
462
    bool bHeader = false;
255
462
    switch( nElement )
256
462
    {
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
462
    default:
265
462
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
266
462
    }
267
268
462
    return pContext;
269
462
}
270
271
272
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */