Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/dbgui/asciiopt.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 <global.hxx>
21
#include <asciiopt.hxx>
22
#include <comphelper/string.hxx>
23
#include <osl/thread.h>
24
#include <o3tl/string_view.hxx>
25
#include <sfx2/objsh.hxx>
26
#include <tools/stream.hxx>
27
28
constexpr std::u16string_view pStrFix = u"FIX";
29
constexpr std::u16string_view pStrMrg = u"MRG";
30
constexpr std::u16string_view pStrDet = u"DETECT";
31
32
ScAsciiOptions::ScAsciiOptions() :
33
0
    bFixedLen       ( false ),
34
0
    aFieldSeps      ( OUString(';') ),
35
0
    bMergeFieldSeps ( false ),
36
0
    bRemoveSpace    ( false ),
37
0
    bQuotedFieldAsText(false),
38
0
    bDetectSpecialNumber(false),
39
0
    bDetectScientificNumber(true),
40
0
    bEvaluateFormulas(true),
41
0
    bSkipEmptyCells(false),
42
0
    bSaveAsShown(true),
43
0
    bSaveFormulas(false),
44
0
    bIncludeBOM(false),
45
0
    cTextSep        ( cDefaultTextSep ),
46
0
    eCharSet        ( osl_getThreadTextEncoding() ),
47
0
    eLang           ( LANGUAGE_SYSTEM ),
48
0
    bCharSetSystem  ( false ),
49
0
    nStartRow       ( 1 )
50
0
{
51
0
}
52
53
void ScAsciiOptions::SetColumnInfo( const ScCsvExpDataVec& rDataVec )
54
0
{
55
0
    sal_uInt16 nInfoCount = static_cast< sal_uInt16 >( rDataVec.size() );
56
0
    mvColStart.resize(nInfoCount);
57
0
    mvColFormat.resize(nInfoCount);
58
0
    for( sal_uInt16 nIx = 0; nIx < nInfoCount; ++nIx )
59
0
    {
60
0
        mvColStart[ nIx ] = rDataVec[ nIx ].mnIndex;
61
0
        mvColFormat[ nIx ] = rDataVec[ nIx ].mnType;
62
0
    }
63
0
}
64
65
static OUString lcl_decodeSepString( std::u16string_view rSepNums, bool & o_bMergeFieldSeps )
66
0
{
67
0
    if ( rSepNums.empty() )
68
0
        return OUString();
69
70
0
    OUStringBuffer aFieldSeps;
71
0
    sal_Int32 nPos = 0;
72
0
    do
73
0
    {
74
0
        const std::u16string_view aCode = o3tl::getToken(rSepNums, 0, '/', nPos );
75
0
        if ( aCode == pStrMrg )
76
0
            o_bMergeFieldSeps = true;
77
0
        else
78
0
        {
79
0
            sal_Int32 nVal = o3tl::toInt32(aCode);
80
0
            if ( nVal )
81
0
                aFieldSeps.append(sal_Unicode(nVal));
82
0
        }
83
0
    }
84
0
    while ( nPos >= 0 );
85
86
0
    return aFieldSeps.makeStringAndClear();
87
0
}
88
89
// The options string must not contain semicolons (because of the pick list),
90
// use comma as separator.
91
92
void ScAsciiOptions::ReadFromString( std::u16string_view rString, SvStream* pStream4Detect )
93
0
{
94
0
    sal_Int32 nPos = rString.empty() ? -1 : 0;
95
0
    bool bDetectSep = false;
96
97
    // Token 0: Field separator.
98
0
    if ( nPos >= 0 )
99
0
    {
100
0
        bFixedLen = bMergeFieldSeps = false;
101
102
0
        const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
103
0
        if ( aToken == pStrDet)
104
0
            bDetectSep = true;
105
0
        else
106
0
        {
107
0
            if ( aToken == pStrFix )
108
0
                bFixedLen = true;
109
0
            aFieldSeps = lcl_decodeSepString( aToken, bMergeFieldSeps);
110
0
        }
111
0
    }
112
113
    // Token 1: Text separator.
114
0
    if ( nPos >= 0 )
115
0
    {
116
0
        const sal_Int32 nVal = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
117
0
        cTextSep = static_cast<sal_Unicode>(nVal);
118
0
    }
119
120
    // Token 2: Text encoding.
121
0
    if ( nPos >= 0 )
122
0
    {
123
0
        const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
124
0
        bool bDetectCharSet = aToken == pStrDet;
125
0
        if ( bDetectCharSet && pStream4Detect )
126
0
        {
127
0
            pStream4Detect->DetectEncoding();
128
0
            eCharSet = pStream4Detect->GetStreamEncoding();
129
0
        }
130
0
        else if (!bDetectCharSet)
131
0
            eCharSet = ScGlobal::GetCharsetValue( aToken );
132
0
    }
133
134
0
    if (bDetectSep && pStream4Detect)
135
0
        SfxObjectShell::DetectCsvSeparators(*pStream4Detect, eCharSet, aFieldSeps, cTextSep);
136
137
    // Token 3: Number of start row.
138
0
    if ( nPos >= 0 )
139
0
    {
140
0
        nStartRow = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
141
0
    }
142
143
    // Token 4: Column info.
144
0
    if ( nPos >= 0 )
145
0
    {
146
0
        const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
147
0
        const sal_Int32 nInfoCount = comphelper::string::getTokenCount(aToken, '/')/2;
148
0
        mvColStart.resize(nInfoCount);
149
0
        mvColFormat.resize(nInfoCount);
150
0
        sal_Int32 nP = 0;
151
0
        for (sal_Int32 nInfo=0; nInfo<nInfoCount; ++nInfo)
152
0
        {
153
0
            mvColStart[nInfo]  = o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP));
154
0
            mvColFormat[nInfo] = static_cast<sal_uInt8>(o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP)));
155
0
        }
156
0
    }
157
158
    // Token 5: Language.
159
0
    if (nPos >= 0)
160
0
    {
161
0
        eLang = static_cast<LanguageType>(o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos)));
162
0
    }
163
164
    // Token 6: Import quoted field as text.
165
0
    if (nPos >= 0)
166
0
    {
167
0
        bQuotedFieldAsText = o3tl::getToken(rString, 0, ',', nPos) == u"true";
168
0
    }
169
170
    // Token 7: Detect special numbers.
171
0
    if (nPos >= 0)
172
0
    {
173
0
        bDetectSpecialNumber = o3tl::getToken(rString, 0, ',', nPos) == u"true";
174
0
    }
175
0
    else
176
0
        bDetectSpecialNumber = true;    // default of versions that didn't add the parameter
177
178
    // Token 8: used for "Save as shown" in export options
179
0
    if ( nPos >= 0 )
180
0
    {
181
0
        bSaveAsShown = o3tl::getToken(rString, 0, ',', nPos) == u"true";
182
0
    }
183
0
    else
184
0
        bSaveAsShown = true;    // default value
185
186
    // Token 9: used for "Save cell formulas" in export options
187
0
    if ( nPos >= 0 )
188
0
    {
189
0
        bSaveFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
190
0
    }
191
0
    else
192
0
        bSaveFormulas = false;
193
194
    // Token 10: Boolean for Trim spaces.
195
0
    if (nPos >= 0)
196
0
    {
197
0
        bRemoveSpace = o3tl::getToken(rString, 0, ',', nPos) == u"true";
198
0
    }
199
0
    else
200
0
        bRemoveSpace = false;
201
202
    // Token 11: sheet to export for --convert-to csv
203
    // Does not need to be evaluated here but may be present.
204
0
    if (nPos >= 0)
205
0
    {
206
0
        o3tl::getToken(rString, 0, ',', nPos);
207
0
    }
208
209
    // Token 12: evaluate formulas.
210
0
    if (nPos >= 0)
211
0
    {
212
        // If present, defaults to "false".
213
0
        bEvaluateFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
214
0
    }
215
0
    else
216
0
        bEvaluateFormulas = true;   // default of versions that didn't add the parameter
217
218
    // Token 13: include BOM.
219
0
    bIncludeBOM = nPos >= 0 && o3tl::getToken(rString, 0, ',', nPos) == u"true";
220
221
    // Token 14: Detect scientific numbers.
222
0
    if (nPos >= 0)
223
0
    {
224
0
        bDetectScientificNumber = o3tl::getToken(rString, 0, ',', nPos) == u"true";
225
0
    }
226
0
    else
227
0
        bDetectScientificNumber = true;    // default of versions that didn't add the parameter
228
229
0
}
230
231
OUString ScAsciiOptions::WriteToString() const
232
0
{
233
0
    OUStringBuffer aOutStr;
234
235
    // Token 0: Field separator.
236
0
    if ( bFixedLen )
237
0
        aOutStr.append(pStrFix);
238
0
    else if ( aFieldSeps.isEmpty() )
239
0
        aOutStr.append("0");
240
0
    else
241
0
    {
242
0
        sal_Int32 nLen = aFieldSeps.getLength();
243
0
        for (sal_Int32 i=0; i<nLen; i++)
244
0
        {
245
0
            if (i)
246
0
                aOutStr.append("/");
247
0
            aOutStr.append(OUString::number(aFieldSeps[i]));
248
0
        }
249
0
        if ( bMergeFieldSeps )
250
0
        {
251
0
            aOutStr.append(OUString::Concat("/") + pStrMrg);
252
0
        }
253
0
    }
254
255
    // Token 1: Text Quote character.
256
0
    aOutStr.append("," + OUString::number(cTextSep) + ",");
257
258
    //Token 2: Text encoding.
259
0
    if ( bCharSetSystem )           // force "SYSTEM"
260
0
        aOutStr.append(ScGlobal::GetCharsetString( RTL_TEXTENCODING_DONTKNOW ));
261
0
    else
262
0
        aOutStr.append(ScGlobal::GetCharsetString( eCharSet ));
263
264
    //Token 3: Number of start row.
265
0
    aOutStr.append("," + OUString::number(nStartRow) + ",");
266
267
    //Token 4: Column info.
268
0
    for (size_t nInfo=0; nInfo<mvColStart.size(); nInfo++)
269
0
    {
270
0
        if (nInfo)
271
0
            aOutStr.append("/");
272
0
        aOutStr.append(OUString::number(mvColStart[nInfo]) +
273
0
                "/" +
274
0
               OUString::number(mvColFormat[nInfo]));
275
0
    }
276
277
    // #i112025# the options string is used in macros and linked sheets,
278
    // so new options must be added at the end, to remain compatible
279
    // Always keep in sync with ScImportOptions.
280
281
0
    aOutStr.append("," +
282
               // Token 5: Language
283
0
               OUString::number(static_cast<sal_uInt16>(eLang)) + "," +
284
               // Token 6: Import quoted field as text.
285
0
               OUString::boolean( bQuotedFieldAsText ) + "," +
286
               // Token 7: Detect special numbers.
287
0
               OUString::boolean( bDetectSpecialNumber ) + "," +
288
               // Token 8: used for "Save as shown" in export options
289
0
               OUString::boolean( bSaveAsShown ) +"," +
290
               // Token 9: used for "Save cell formulas" in export options
291
0
               OUString::boolean( bSaveFormulas ) + "," +
292
               // Token 10: Trim Space
293
0
               OUString::boolean( bRemoveSpace ) +
294
               // Token 11: sheet to export, always 0 for current sheet
295
0
               ",0," +
296
               // Token 12: evaluate formulas in import
297
0
               OUString::boolean( bEvaluateFormulas ) + "," +
298
               // Token 13: include BOM
299
0
               OUString::boolean(bIncludeBOM) + "," +
300
               // Token 14: Detect scientific numbers.
301
0
               OUString::boolean( bDetectScientificNumber )
302
0
            );
303
0
    return aOutStr.makeStringAndClear();
304
0
}
305
306
// static
307
sal_Unicode ScAsciiOptions::GetWeightedFieldSep( const OUString & rFieldSeps, bool bDecodeNumbers )
308
0
{
309
0
    bool bMergeFieldSeps = false;
310
0
    OUString aFieldSeps( bDecodeNumbers ? lcl_decodeSepString( rFieldSeps, bMergeFieldSeps) : rFieldSeps);
311
0
    if (aFieldSeps.isEmpty())
312
0
    {
313
0
        return 0;
314
0
    }
315
0
    else if (aFieldSeps.getLength() == 1)
316
0
        return aFieldSeps[0];
317
0
    else
318
0
    {
319
        // There can be only one separator for output. See also fdo#53449
320
0
        if (aFieldSeps.indexOf(',') != -1)
321
0
            return ',';
322
0
        else if (aFieldSeps.indexOf('\t') != -1)
323
0
            return '\t';
324
0
        else if (aFieldSeps.indexOf(';') != -1)
325
0
            return ';';
326
0
        else if (aFieldSeps.indexOf(' ') != -1)
327
0
            return ' ';
328
0
        else
329
0
            return aFieldSeps[0];
330
0
    }
331
0
}
332
333
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */