Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/config/StoredChapterNumbering.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 <uinums.hxx>
11
12
#include <cppuhelper/implbase.hxx>
13
14
#include <com/sun/star/container/XIndexReplace.hpp>
15
#include <com/sun/star/container/XNamed.hpp>
16
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
17
#include <com/sun/star/util/MeasureUnit.hpp>
18
#include <com/sun/star/xml/sax/Writer.hpp>
19
20
#include <comphelper/processfactory.hxx>
21
22
#include <unotools/streamwrap.hxx>
23
24
#include <xmloff/xmlnamespace.hxx>
25
#include <xmloff/xmltoken.hxx>
26
#include <xmloff/namespacemap.hxx>
27
#include <xmloff/xmlexp.hxx>
28
#include <xmloff/xmlnume.hxx>
29
#include <xmloff/xmlimp.hxx>
30
#include <xmloff/xmlictxt.hxx>
31
#include <xmloff/xmlnumi.hxx>
32
33
#include <vcl/svapp.hxx>
34
#include <comphelper/diagnose_ex.hxx>
35
36
#include <unosett.hxx>
37
#include <names.hxx>
38
39
40
using namespace ::com::sun::star;
41
using namespace ::xmloff::token;
42
43
namespace sw {
44
45
class StoredChapterNumberingRules
46
    : public ::cppu::WeakImplHelper<container::XNamed,container::XIndexReplace>
47
{
48
private:
49
    // TODO in case this ever becomes accessible via API need an invalidate
50
    SwChapterNumRules & m_rNumRules;
51
    sal_uInt16 const m_nIndex;
52
53
    SwNumRulesWithName * GetOrCreateRules()
54
0
    {
55
0
        SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex));
56
0
        if (!pRules)
57
0
        {
58
0
            m_rNumRules.CreateEmptyNumRule(m_nIndex);
59
0
            pRules = m_rNumRules.GetRules(m_nIndex);
60
0
            assert(pRules);
61
0
        }
62
0
        return const_cast<SwNumRulesWithName*>(pRules);
63
0
    }
64
65
public:
66
    StoredChapterNumberingRules(
67
            SwChapterNumRules & rNumRules, sal_uInt16 const nIndex)
68
0
        : m_rNumRules(rNumRules)
69
0
        , m_nIndex(nIndex)
70
0
    {
71
0
        assert(m_nIndex < SwChapterNumRules::nMaxRules);
72
0
    }
73
74
    // XNamed
75
    virtual OUString SAL_CALL getName() override
76
0
    {
77
0
        SolarMutexGuard g;
78
0
        SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex));
79
0
        if (!pRules)
80
0
        {
81
0
            return OUString();
82
0
        }
83
0
        return pRules->GetName().toString();
84
0
    }
85
86
    virtual void SAL_CALL setName(OUString const& rName) override
87
0
    {
88
0
        SolarMutexGuard g;
89
0
        SwNumRulesWithName *const pRules(GetOrCreateRules());
90
0
        pRules->SetName(UIName(rName));
91
0
    }
92
93
    // XElementAccess
94
    virtual uno::Type SAL_CALL getElementType() override
95
0
    {
96
0
        return ::cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
97
0
    }
98
99
    virtual ::sal_Bool SAL_CALL hasElements() override
100
0
    {
101
0
        return true;
102
0
    }
103
104
    // XIndexAccess
105
    virtual sal_Int32 SAL_CALL getCount() override
106
0
    {
107
0
        return MAXLEVEL;
108
0
    }
109
110
    virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override
111
0
    {
112
0
        if (nIndex < 0 || MAXLEVEL <= nIndex)
113
0
            throw lang::IndexOutOfBoundsException();
114
115
0
        SolarMutexGuard g;
116
0
        SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex));
117
0
        if (!pRules)
118
0
        {
119
0
            return uno::Any();
120
0
        }
121
0
        SwNumFormat const* pNumFormat(nullptr);
122
0
        UIName const* pCharStyleName(nullptr);
123
0
        pRules->GetNumFormat(nIndex, pNumFormat, pCharStyleName);
124
0
        if (!pNumFormat)
125
0
        {   // the dialog only fills in those levels that are non-default
126
0
            return uno::Any(); // the export will ignore this level, yay
127
0
        }
128
0
        assert(pCharStyleName);
129
0
        ProgName dummy; // pass in empty HeadingStyleName - can't import anyway
130
0
        uno::Sequence<beans::PropertyValue> const ret(
131
0
            SwXNumberingRules::GetPropertiesForNumFormat(
132
0
                *pNumFormat, *pCharStyleName, &dummy, u""_ustr));
133
0
        return uno::Any(ret);
134
0
    }
135
136
    // XIndexReplace
137
    virtual void SAL_CALL replaceByIndex(
138
            sal_Int32 nIndex, uno::Any const& rElement) override
139
0
    {
140
0
        if (nIndex < 0 || MAXLEVEL <= nIndex)
141
0
            throw lang::IndexOutOfBoundsException();
142
0
        uno::Sequence<beans::PropertyValue> props;
143
0
        if (!(rElement >>= props))
144
0
            throw lang::IllegalArgumentException(u"invalid type"_ustr,
145
0
                    getXWeak(), 1);
146
147
0
        SolarMutexGuard g;
148
0
        SwNumFormat aNumberFormat;
149
0
        UIName charStyleName;
150
0
        SwXNumberingRules::SetPropertiesToNumFormat(
151
0
            aNumberFormat,
152
0
            charStyleName,
153
0
            nullptr, nullptr, nullptr, nullptr, nullptr,
154
0
            props);
155
0
        SwNumRulesWithName *const pRules(GetOrCreateRules());
156
0
        pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName);
157
0
    }
158
};
159
160
namespace {
161
162
class StoredChapterNumberingExport
163
    : public SvXMLExport
164
{
165
public:
166
    StoredChapterNumberingExport(
167
            uno::Reference<uno::XComponentContext> const& xContext,
168
            OUString const& rFileName,
169
            uno::Reference<xml::sax::XDocumentHandler> const& xHandler)
170
0
        : SvXMLExport(xContext, u"sw::StoredChapterNumberingExport"_ustr, rFileName,
171
0
            util::MeasureUnit::CM, xHandler)
172
0
    {
173
0
        GetNamespaceMap_().Add(GetXMLToken(XML_NP_OFFICE),
174
0
                               GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE);
175
0
        GetNamespaceMap_().Add(GetXMLToken(XML_NP_TEXT),
176
0
                               GetXMLToken(XML_N_TEXT), XML_NAMESPACE_TEXT);
177
0
        GetNamespaceMap_().Add(GetXMLToken(XML_NP_STYLE),
178
0
                               GetXMLToken(XML_N_STYLE), XML_NAMESPACE_STYLE);
179
0
        GetNamespaceMap_().Add(GetXMLToken(XML_NP_FO),
180
0
                               GetXMLToken(XML_N_FO), XML_NAMESPACE_FO);
181
0
        GetNamespaceMap_().Add(GetXMLToken(XML_NP_SVG),
182
0
                               GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG);
183
0
    }
184
185
0
    virtual void ExportAutoStyles_() override {}
186
0
    virtual void ExportMasterStyles_() override {}
187
0
    virtual void ExportContent_() override {}
188
189
    void ExportRule(SvxXMLNumRuleExport & rExport,
190
            uno::Reference<container::XIndexReplace> const& xRule)
191
0
    {
192
0
        uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY);
193
0
        OUString const name(xNamed->getName());
194
0
        bool bEncoded(false);
195
0
        AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
196
0
                      EncodeStyleName(name, &bEncoded) );
197
0
        if (bEncoded)
198
0
        {
199
0
            AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name);
200
0
        }
201
202
0
        SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT,
203
0
                                  XML_OUTLINE_STYLE, true, true );
204
0
        rExport.exportLevelStyles(xRule, true);
205
0
    }
206
207
    void ExportRules(
208
            std::set<UIName> const& rCharStyles,
209
            std::vector<uno::Reference<container::XIndexReplace>> const& rRules)
210
0
    {
211
0
        GetDocHandler()->startDocument();
212
213
0
        AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_OFFICE),
214
0
                      GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_OFFICE));
215
0
        AddAttribute( GetNamespaceMap_().GetAttrNameByKey (XML_NAMESPACE_TEXT),
216
0
                      GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_TEXT));
217
0
        AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_STYLE),
218
0
                      GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_STYLE));
219
0
        AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_FO),
220
0
                      GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_FO));
221
0
        AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_SVG),
222
0
                      GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_SVG));
223
224
0
        {
225
            // let's just have an office:styles as a dummy root
226
0
            SvXMLElementExport styles(*this,
227
0
                    XML_NAMESPACE_OFFICE, XML_STYLES, true, true);
228
229
            // horrible hack for char styles to get display-name mapping
230
0
            for (const auto& rCharStyle : rCharStyles)
231
0
            {
232
0
                AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TEXT );
233
0
                bool bEncoded(false);
234
0
                AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
235
0
                              EncodeStyleName(rCharStyle.toString(), &bEncoded) );
236
0
                if (bEncoded)
237
0
                {
238
0
                    AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCharStyle.toString());
239
0
                }
240
241
0
                SvXMLElementExport style(*this,
242
0
                        XML_NAMESPACE_STYLE, XML_STYLE, true, true);
243
0
            }
244
245
0
            SvxXMLNumRuleExport numRuleExport(*this);
246
247
0
            for (const auto& rRule : rRules)
248
0
            {
249
0
                ExportRule(numRuleExport, rRule);
250
0
            }
251
0
        }
252
253
0
        GetDocHandler()->endDocument();
254
0
    }
255
};
256
257
/** Dummy import context for style:style element that can just read the
258
    attributes needed to map name to display-name.
259
    Unfortunately the "real" context for this depends on some other things.
260
    The mapping is necessary to import the text:style-name attribute
261
    of the text:outline-level-style element.
262
 */
263
class StoredChapterNumberingDummyStyleContext
264
    : public SvXMLImportContext
265
{
266
public:
267
    StoredChapterNumberingDummyStyleContext(
268
            SvXMLImport & rImport,
269
            uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
270
0
        : SvXMLImportContext(rImport)
271
0
    {
272
0
        OUString name;
273
0
        OUString displayName;
274
0
        XmlStyleFamily nFamily(XmlStyleFamily::DATA_STYLE);
275
276
0
        for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
277
0
            if (aIter.getToken() == (XML_NAMESPACE_STYLE | XML_FAMILY))
278
0
            {
279
0
                if (IsXMLToken(aIter, XML_TEXT))
280
0
                    nFamily = XmlStyleFamily::TEXT_TEXT;
281
0
                else if (IsXMLToken(aIter, XML_NAME))
282
0
                    name = aIter.toString();
283
0
                else if (IsXMLToken(aIter, XML_DISPLAY_NAME))
284
0
                    displayName = aIter.toString();
285
0
                else
286
0
                    SAL_WARN("xmloff", "unknown value for style:family=" << aIter.toString());
287
0
            }
288
0
            else
289
0
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
290
291
0
        if (nFamily != XmlStyleFamily::DATA_STYLE && !name.isEmpty() && !displayName.isEmpty())
292
0
        {
293
0
            rImport.AddStyleDisplayName(nFamily, name, displayName);
294
0
        }
295
0
    }
296
};
297
298
class StoredChapterNumberingImport;
299
300
class StoredChapterNumberingRootContext
301
    : public SvXMLImportContext
302
{
303
private:
304
    SwChapterNumRules & m_rNumRules;
305
    size_t m_nCounter;
306
    std::vector<rtl::Reference<SvxXMLListStyleContext>> m_Contexts;
307
308
public:
309
    StoredChapterNumberingRootContext(
310
            SwChapterNumRules & rNumRules, SvXMLImport & rImport)
311
0
        : SvXMLImportContext(rImport)
312
0
        , m_rNumRules(rNumRules)
313
0
        , m_nCounter(0)
314
0
    {
315
0
    }
316
317
    virtual void SAL_CALL endFastElement(sal_Int32 /*Element*/) override
318
0
    {
319
0
        assert(m_Contexts.size() <= SwChapterNumRules::nMaxRules);
320
0
        for (auto iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter)
321
0
        {
322
0
            uno::Reference<container::XIndexReplace> const xRule(
323
0
                new sw::StoredChapterNumberingRules(m_rNumRules,
324
0
                    iter - m_Contexts.begin()));
325
0
            (*iter)->FillUnoNumRule(xRule);
326
            // TODO: xmloff's outline-style import seems to ignore this???
327
0
            uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY);
328
0
            xNamed->setName((*iter)->GetDisplayName());
329
0
        }
330
0
    }
331
332
    virtual css::uno::Reference<XFastContextHandler> SAL_CALL createFastChildContext(
333
                sal_Int32 Element,
334
                const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override
335
0
    {
336
0
        if (Element == XML_ELEMENT(STYLE, XML_STYLE))
337
0
        {
338
0
            return new StoredChapterNumberingDummyStyleContext(GetImport(), xAttrList);
339
0
        }
340
0
        else if (Element == XML_ELEMENT(TEXT, XML_OUTLINE_STYLE))
341
0
        {
342
0
            ++m_nCounter;
343
0
            if (m_nCounter <= SwChapterNumRules::nMaxRules)
344
0
            {
345
0
                SvxXMLListStyleContext *const pContext(
346
0
                    new SvxXMLListStyleContext(GetImport(), true));
347
0
                m_Contexts.emplace_back(pContext);
348
0
                return pContext;
349
0
            }
350
0
        }
351
352
0
        return nullptr;
353
0
    }
354
};
355
356
class StoredChapterNumberingImport
357
    : public SvXMLImport
358
{
359
private:
360
    SwChapterNumRules & m_rNumRules;
361
362
public:
363
    StoredChapterNumberingImport(
364
            uno::Reference<uno::XComponentContext> const& xContext,
365
            SwChapterNumRules & rNumRules)
366
0
        : SvXMLImport(xContext, u"sw::StoredChapterNumberingImport"_ustr, SvXMLImportFlags::ALL)
367
0
        , m_rNumRules(rNumRules)
368
0
    {
369
0
    }
370
371
    virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element,
372
        const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ ) override
373
0
    {
374
0
        if (Element == XML_ELEMENT(OFFICE, XML_STYLES))
375
0
            return new StoredChapterNumberingRootContext(m_rNumRules, *this);
376
0
        return nullptr;
377
0
    }
378
};
379
380
}
381
382
void ExportStoredChapterNumberingRules(SwChapterNumRules & rRules,
383
        SvStream & rStream, OUString const& rFileName)
384
0
{
385
0
    uno::Reference<uno::XComponentContext> const& xContext(
386
0
            ::comphelper::getProcessComponentContext());
387
388
0
    uno::Reference<io::XOutputStream> const xOutStream(
389
0
            new ::utl::OOutputStreamWrapper(rStream));
390
391
0
    uno::Reference<xml::sax::XWriter> const xWriter(
392
0
            xml::sax::Writer::create(xContext));
393
394
0
    xWriter->setOutputStream(xOutStream);
395
396
0
    rtl::Reference<StoredChapterNumberingExport> exp(new StoredChapterNumberingExport(xContext, rFileName, xWriter));
397
398
    // if style name contains a space then name != display-name
399
    // ... and the import needs to map from name to display-name then!
400
0
    std::set<UIName> charStyles;
401
0
    std::vector<uno::Reference<container::XIndexReplace>> numRules;
402
0
    for (size_t i = 0; i < SwChapterNumRules::nMaxRules; ++i)
403
0
    {
404
0
        if (SwNumRulesWithName const* pRule = rRules.GetRules(i))
405
0
        {
406
0
            for (size_t j = 0; j < MAXLEVEL; ++j)
407
0
            {
408
0
                SwNumFormat const* pDummy(nullptr);
409
0
                UIName const* pCharStyleName(nullptr);
410
0
                pRule->GetNumFormat(j, pDummy, pCharStyleName);
411
0
                if (pCharStyleName && !pCharStyleName->isEmpty())
412
0
                {
413
0
                    charStyles.insert(*pCharStyleName);
414
0
                }
415
0
            }
416
0
            numRules.push_back(new StoredChapterNumberingRules(rRules, i));
417
0
        }
418
0
    }
419
420
0
    try
421
0
    {
422
0
        exp->ExportRules(charStyles, numRules);
423
0
    }
424
0
    catch (uno::Exception const&)
425
0
    {
426
0
        TOOLS_WARN_EXCEPTION("sw.ui", "ExportStoredChapterNumberingRules");
427
0
    }
428
0
}
429
430
void ImportStoredChapterNumberingRules(SwChapterNumRules & rRules,
431
        SvStream & rStream, OUString const& rFileName)
432
0
{
433
0
    uno::Reference<uno::XComponentContext> const& xContext(
434
0
            ::comphelper::getProcessComponentContext());
435
436
0
    uno::Reference<io::XInputStream> const xInStream(
437
0
            new ::utl::OInputStreamWrapper(rStream));
438
439
0
    rtl::Reference<StoredChapterNumberingImport> const xImport(new StoredChapterNumberingImport(xContext, rRules));
440
441
0
    xml::sax::InputSource const source(xInStream, u""_ustr, u""_ustr, rFileName);
442
443
0
    try
444
0
    {
445
0
        xImport->parseStream(source);
446
0
    }
447
0
    catch (uno::Exception const&)
448
0
    {
449
0
        TOOLS_WARN_EXCEPTION("sw.ui", "ImportStoredChapterNumberingRules");
450
0
    }
451
0
}
452
453
} // namespace sw
454
455
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */