tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / filter / dif / difimp.cxx
blobd26b7276a3de9b9bb536167c39878841048ab8c8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svl/numformat.hxx>
21 #include <tools/stream.hxx>
22 #include <osl/diagnose.h>
23 #include <dif.hxx>
24 #include <docpool.hxx>
25 #include <document.hxx>
26 #include <docsh.hxx>
27 #include <fprogressbar.hxx>
28 #include <ftools.hxx>
29 #include <patattr.hxx>
30 #include <scerrors.hxx>
31 #include <scitems.hxx>
32 #include <stringutil.hxx>
33 #include <table.hxx>
34 #include <memory>
36 const std::u16string_view pKeyTABLE = u"TABLE";
37 const std::u16string_view pKeyVECTORS = u"VECTORS";
38 const std::u16string_view pKeyTUPLES = u"TUPLES";
39 const std::u16string_view pKeyDATA = u"DATA";
40 const std::u16string_view pKeyBOT = u"BOT";
41 const std::u16string_view pKeyEOD = u"EOD";
43 ErrCode ScFormatFilterPluginImpl::ScImportDif(SvStream& rIn, ScDocument* pDoc, const ScAddress& rInsPos,
44 const rtl_TextEncoding eVon )
46 DifParser aDifParser( rIn, *pDoc, eVon );
48 SCTAB nBaseTab = rInsPos.Tab();
50 TOPIC eTopic = T_UNKNOWN;
51 bool bSyntErrWarn = false;
52 bool bOverflowWarn = false;
54 OUStringBuffer& rData = aDifParser.m_aData;
56 rIn.Seek( 0 );
58 ScfStreamProgressBar aPrgrsBar( rIn, pDoc->GetDocumentShell() );
60 while( eTopic != T_DATA && eTopic != T_END )
62 eTopic = aDifParser.GetNextTopic();
64 aPrgrsBar.Progress();
66 const bool bData = !rData.isEmpty();
68 switch( eTopic )
70 case T_TABLE:
72 if( aDifParser.nVector != 0 || aDifParser.nVal != 1 )
73 bSyntErrWarn = true;
74 if( bData )
75 pDoc->RenameTab(nBaseTab, rData.toString());
77 break;
78 case T_VECTORS:
79 case T_TUPLES:
81 if( aDifParser.nVector != 0 )
82 bSyntErrWarn = true;
84 break;
85 case T_DATA:
87 if( aDifParser.nVector != 0 || aDifParser.nVal != 0 )
88 bSyntErrWarn = true;
90 break;
91 case T_LABEL:
92 case T_COMMENT:
93 case T_SIZE:
94 case T_PERIODICITY:
95 case T_MAJORSTART:
96 case T_MINORSTART:
97 case T_TRUELENGTH:
98 case T_UINITS:
99 case T_DISPLAYUNITS:
100 case T_END:
101 case T_UNKNOWN:
102 break;
103 default:
104 OSL_FAIL( "ScImportDif - missing enum" );
109 if( eTopic == T_DATA )
110 { // data starts here
111 SCCOL nBaseCol = rInsPos.Col();
113 SCCOL nColCnt = SCCOL_MAX;
114 SCROW nRowCnt = rInsPos.Row();
115 DifAttrCache aAttrCache;
117 DATASET eCurrent = D_UNKNOWN;
119 ScSetStringParam aStrParam; // used to set string value without number detection.
120 aStrParam.setTextInput();
122 while( eCurrent != D_EOD )
124 eCurrent = aDifParser.GetNextDataset();
126 aPrgrsBar.Progress();
127 ScAddress aPos(nColCnt, nRowCnt, nBaseTab);
128 const OUString aData = rData.makeStringAndClear();
130 switch( eCurrent )
132 case D_BOT:
133 if( nColCnt < SCCOL_MAX )
134 nRowCnt++;
135 nColCnt = nBaseCol;
136 break;
137 case D_EOD:
138 break;
139 case D_NUMERIC: // Number cell
140 if( nColCnt == SCCOL_MAX )
141 nColCnt = nBaseCol;
143 if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
145 pDoc->EnsureTable(nBaseTab);
147 if( DifParser::IsV( aData.getStr() ) )
149 pDoc->SetValue(aPos, aDifParser.fVal);
150 aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
151 aDifParser.nNumFormat );
153 else if( aData == "TRUE" || aData == "FALSE" )
155 pDoc->SetValue(aPos, aDifParser.fVal);
156 aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
157 aDifParser.nNumFormat );
159 else if( aData == "NA" || aData == "ERROR" )
161 pDoc->SetString(aPos, aData, &aStrParam);
163 else
165 OUString aTmp = "#IND:" + aData + "?";
166 pDoc->SetString(aPos, aTmp, &aStrParam);
169 else
170 bOverflowWarn = true;
172 nColCnt++;
173 break;
174 case D_STRING: // Text cell
175 if( nColCnt == SCCOL_MAX )
176 nColCnt = nBaseCol;
178 if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
180 if (!aData.isEmpty())
182 pDoc->EnsureTable(nBaseTab);
183 pDoc->SetTextCell(aPos, aData);
186 else
187 bOverflowWarn = true;
189 nColCnt++;
190 break;
191 case D_UNKNOWN:
192 break;
193 case D_SYNT_ERROR:
194 break;
195 default:
196 OSL_FAIL( "ScImportDif - missing enum" );
200 aAttrCache.Apply( *pDoc, nBaseTab );
202 else
203 return SCERR_IMPORT_FORMAT;
205 if( bSyntErrWarn )
207 // FIXME: Add proper warning!
208 return SCWARN_IMPORT_RANGE_OVERFLOW;
210 else if( bOverflowWarn )
211 return SCWARN_IMPORT_RANGE_OVERFLOW;
212 else
213 return ERRCODE_NONE;
216 DifParser::DifParser( SvStream& rNewIn, const ScDocument& rDoc, rtl_TextEncoding eCharSet )
217 : fVal(0.0)
218 , nVector(0)
219 , nVal(0)
220 , nNumFormat(0)
221 , pNumFormatter(rDoc.GetFormatTable())
222 , rIn(rNewIn)
224 if ( rIn.GetStreamCharSet() != eCharSet )
226 OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
227 rIn.SetStreamCharSet( eCharSet );
229 rIn.StartReadingUnicodeText( eCharSet );
232 TOPIC DifParser::GetNextTopic()
234 enum STATE { S_VectorVal, S_Data, S_END, S_START, S_UNKNOWN, S_ERROR_L2 };
236 static const std::u16string_view ppKeys[] =
238 pKeyTABLE, // 0
239 pKeyVECTORS,
240 pKeyTUPLES,
241 pKeyDATA,
242 u"LABEL",
243 u"COMMENT", // 5
244 u"SIZE",
245 u"PERIODICITY",
246 u"MAJORSTART",
247 u"MINORSTART",
248 u"TRUELENGTH", // 10
249 u"UINITS",
250 u"DISPLAYUNITS",
251 u"" // 13
254 static const TOPIC pTopics[] =
256 T_TABLE, // 0
257 T_VECTORS,
258 T_TUPLES,
259 T_DATA,
260 T_LABEL,
261 T_COMMENT, // 5
262 T_SIZE,
263 T_PERIODICITY,
264 T_MAJORSTART,
265 T_MINORSTART,
266 T_TRUELENGTH, // 10
267 T_UINITS,
268 T_DISPLAYUNITS,
269 T_UNKNOWN // 13
272 STATE eS = S_START;
273 OUString aLine;
275 nVector = 0;
276 nVal = 0;
277 TOPIC eRet = T_UNKNOWN;
279 while( eS != S_END )
281 if( !ReadNextLine( aLine ) )
283 eS = S_END;
284 eRet = T_END;
287 switch( eS )
289 case S_START:
291 const std::u16string_view* pRef;
292 sal_uInt16 nCnt = 0;
293 bool bSearch = true;
295 pRef = &ppKeys[ nCnt ];
297 while( bSearch )
299 if( aLine == *pRef )
301 eRet = pTopics[ nCnt ];
302 bSearch = false;
304 else
306 nCnt++;
307 pRef = &ppKeys[ nCnt ];
308 if( pRef->empty() )
309 bSearch = false;
313 if( !pRef->empty() )
314 eS = S_VectorVal;
315 else
316 eS = S_UNKNOWN;
318 break;
319 case S_VectorVal:
321 const sal_Unicode* pCur = aLine.getStr();
323 pCur = ScanIntVal( pCur, nVector );
325 if( pCur && *pCur == ',' )
327 pCur++;
328 ScanIntVal( pCur, nVal );
329 eS = S_Data;
331 else
332 eS = S_ERROR_L2;
334 break;
335 case S_Data:
336 OSL_ENSURE( aLine.getLength() >= 2,
337 "+GetNextTopic(): <String> is too short!" );
338 if( aLine.getLength() > 2 )
339 m_aData.append(aLine.subView(1, aLine.getLength() - 2));
340 else
341 m_aData.truncate();
342 eS = S_END;
343 break;
344 case S_END:
345 OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
346 break;
347 case S_UNKNOWN:
348 // skip 2 lines
349 ReadNextLine( aLine );
350 [[fallthrough]];
351 case S_ERROR_L2: // error happened in line 2
352 // skip 1 line
353 ReadNextLine( aLine );
354 eS = S_END;
355 break;
356 default:
357 OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
361 return eRet;
364 static void lcl_DeEscapeQuotesDif(OUStringBuffer& rString)
366 // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
367 // Single quote characters are left in place because older versions didn't
368 // escape quotes in strings (and Excel doesn't when using the clipboard).
369 // The quotes around the string are removed before this function is called.
371 rString = rString.makeStringAndClear().replaceAll("\"\"", "\"");
374 // Determine if passed in string is numeric data and set fVal/nNumFormat if so
375 DATASET DifParser::GetNumberDataset( const sal_Unicode* pPossibleNumericData )
377 DATASET eRet = D_SYNT_ERROR;
379 OSL_ENSURE( pNumFormatter, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
380 OUString aTestVal( pPossibleNumericData );
381 sal_uInt32 nFormat = 0;
382 double fTmpVal;
383 if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
385 fVal = fTmpVal;
386 nNumFormat = nFormat;
387 eRet = D_NUMERIC;
389 else
390 eRet = D_SYNT_ERROR;
392 return eRet;
395 bool DifParser::ReadNextLine( OUString& rStr )
397 if( aLookAheadLine.isEmpty() )
399 return rIn.ReadUniOrByteStringLine( rStr, rIn.GetStreamCharSet() );
401 else
403 rStr = aLookAheadLine;
404 aLookAheadLine.clear();
405 return true;
409 // Look ahead in the stream to determine if the next line is the first line of
410 // a valid data record structure
411 bool DifParser::LookAhead()
413 const sal_Unicode* pCurrentBuffer;
414 bool bValidStructure = false;
416 OSL_ENSURE( aLookAheadLine.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
417 rIn.ReadUniOrByteStringLine( aLookAheadLine, rIn.GetStreamCharSet() );
419 pCurrentBuffer = aLookAheadLine.getStr();
421 switch( *pCurrentBuffer )
423 case '-': // Special Datatype
424 pCurrentBuffer++;
426 if( Is1_0( pCurrentBuffer ) )
428 bValidStructure = true;
430 break;
431 case '0': // Numeric Data
432 pCurrentBuffer++;
433 if( *pCurrentBuffer == ',' )
435 pCurrentBuffer++;
436 bValidStructure = ( GetNumberDataset(pCurrentBuffer) != D_SYNT_ERROR );
438 break;
439 case '1': // String Data
440 if( Is1_0( aLookAheadLine.getStr() ) )
442 bValidStructure = true;
444 break;
446 return bValidStructure;
449 DATASET DifParser::GetNextDataset()
451 DATASET eRet = D_UNKNOWN;
452 OUString aLine;
453 const sal_Unicode* pCurrentBuffer;
455 ReadNextLine( aLine );
457 pCurrentBuffer = aLine.getStr();
459 switch( *pCurrentBuffer )
461 case '-': // Special Datatype
462 pCurrentBuffer++;
464 if( Is1_0( pCurrentBuffer ) )
466 ReadNextLine( aLine );
467 if( IsBOT( aLine.getStr() ) )
468 eRet = D_BOT;
469 else if( IsEOD( aLine.getStr() ) )
470 eRet = D_EOD;
472 break;
473 case '0': // Numeric Data
474 pCurrentBuffer++; // value in fVal, 2. line in m_aData
475 if( *pCurrentBuffer == ',' )
477 pCurrentBuffer++;
478 eRet = GetNumberDataset(pCurrentBuffer);
479 OUString aTmpLine;
480 ReadNextLine( aTmpLine );
481 if ( eRet == D_SYNT_ERROR )
482 { // for broken records write "#ERR: data" to cell
483 m_aData = OUString::Concat("#ERR: ") + pCurrentBuffer + " (" + aTmpLine + ")";
484 eRet = D_STRING;
486 else
488 m_aData = aTmpLine;
491 break;
492 case '1': // String Data
493 if( Is1_0( aLine.getStr() ) )
495 ReadNextLine( aLine );
496 sal_Int32 nLineLength = aLine.getLength();
497 const sal_Unicode* pLine = aLine.getStr();
499 if( nLineLength >= 1 && *pLine == '"' )
501 // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
502 // A look ahead into the next line is needed in order to deal with
503 // multiline strings containing quotes
504 if( LookAhead() )
506 // Single line string
507 if( nLineLength >= 2 && pLine[nLineLength - 1] == '"' )
509 m_aData = aLine.subView( 1, nLineLength - 2 );
510 lcl_DeEscapeQuotesDif(m_aData);
511 eRet = D_STRING;
514 else
516 // Multiline string
517 m_aData = aLine.subView( 1 );
518 bool bContinue = true;
519 while ( bContinue )
521 m_aData.append("\n");
522 bContinue = !rIn.eof() && ReadNextLine( aLine );
523 if( bContinue )
525 nLineLength = aLine.getLength();
526 if( nLineLength >= 1 )
528 pLine = aLine.getStr();
529 bContinue = !LookAhead();
530 if( bContinue )
532 m_aData.append(aLine);
534 else if( pLine[nLineLength - 1] == '"' )
536 m_aData.append(aLine.subView(0, nLineLength -1));
537 lcl_DeEscapeQuotesDif(m_aData);
538 eRet = D_STRING;
546 break;
549 if( eRet == D_UNKNOWN )
550 ReadNextLine( aLine );
552 if( rIn.eof() )
553 eRet = D_EOD;
555 return eRet;
558 const sal_Unicode* DifParser::ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet )
560 // eat leading whitespace, not specified, but seen in the wild
561 while (*pStart == ' ' || *pStart == '\t')
562 ++pStart;
564 sal_Unicode cCurrent = *pStart;
566 if( IsNumber( cCurrent ) )
567 rRet = static_cast<sal_uInt32>( cCurrent - '0' );
568 else
569 return nullptr;
571 pStart++;
572 cCurrent = *pStart;
574 while( IsNumber( cCurrent ) && rRet < ( 0xFFFFFFFF / 10 ) )
576 rRet *= 10;
577 rRet += static_cast<sal_uInt32>( cCurrent - '0' );
579 pStart++;
580 cCurrent = *pStart;
583 return pStart;
586 DifColumn::DifColumn ()
587 : mpCurrent(nullptr)
591 void DifColumn::SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat )
593 OSL_ENSURE( pDoc->ValidRow(nRow), "*DifColumn::SetNumFormat(): Row too big!" );
595 if( nNumFormat > 0 )
597 if(mpCurrent)
599 OSL_ENSURE( nRow > 0,
600 "*DifColumn::SetNumFormat(): more cannot be zero!" );
601 OSL_ENSURE( nRow > mpCurrent->nEnd,
602 "*DifColumn::SetNumFormat(): start from scratch?" );
604 if( mpCurrent->nNumFormat == nNumFormat && mpCurrent->nEnd == nRow - 1 )
605 mpCurrent->nEnd = nRow;
606 else
607 NewEntry( nRow, nNumFormat );
609 else
610 NewEntry(nRow,nNumFormat );
612 else
613 mpCurrent = nullptr;
616 void DifColumn::NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat )
618 maEntries.emplace_back();
619 mpCurrent = &maEntries.back();
620 mpCurrent->nStart = mpCurrent->nEnd = nPos;
621 mpCurrent->nNumFormat = nNumFormat;
625 void DifColumn::Apply( ScDocument& rDoc, const SCCOL nCol, const SCTAB nTab )
627 ScPatternAttr aAttr(rDoc.getCellAttributeHelper());
628 SfxItemSet &rItemSet = aAttr.GetItemSet();
630 for (const auto& rEntry : maEntries)
632 OSL_ENSURE( rEntry.nNumFormat > 0,
633 "+DifColumn::Apply(): Number format must not be 0!" );
635 rItemSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, rEntry.nNumFormat ) );
637 rDoc.ApplyPatternAreaTab( nCol, rEntry.nStart, nCol, rEntry.nEnd, nTab, aAttr );
639 rItemSet.ClearItem();
643 DifAttrCache::DifAttrCache()
647 DifAttrCache::~DifAttrCache()
651 void DifAttrCache::SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat )
653 OSL_ENSURE( pDoc->ValidCol(nCol), "-DifAttrCache::SetNumFormat(): Col too big!" );
655 if( !maColMap.count(nCol) )
656 maColMap[ nCol ].reset( new DifColumn );
658 maColMap[ nCol ]->SetNumFormat( pDoc, nRow, nNumFormat );
661 void DifAttrCache::Apply( ScDocument& rDoc, SCTAB nTab )
663 for( SCCOL nCol : rDoc.GetWritableColumnsRange(nTab, 0, rDoc.MaxCol()) )
665 if( maColMap.count(nCol) )
666 maColMap[ nCol ]->Apply( rDoc, nCol, nTab );
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */