1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <svl/zforlist.hxx>
22 #include <tools/stream.hxx>
23 #include <osl/diagnose.h>
25 #include <docpool.hxx>
26 #include <document.hxx>
27 #include <fprogressbar.hxx>
29 #include <patattr.hxx>
30 #include <scerrors.hxx>
31 #include <scitems.hxx>
32 #include <stringutil.hxx>
36 const sal_Unicode pKeyTABLE
[] = { 'T', 'A', 'B', 'L', 'E', 0 };
37 const sal_Unicode pKeyVECTORS
[] = { 'V', 'E', 'C', 'T', 'O', 'R', 'S', 0 };
38 const sal_Unicode pKeyTUPLES
[] = { 'T', 'U', 'P', 'L', 'E', 'S', 0 };
39 const sal_Unicode pKeyDATA
[] = { 'D', 'A', 'T', 'A', 0 };
40 const sal_Unicode pKeyBOT
[] = { 'B', 'O', 'T', 0 };
41 const sal_Unicode pKeyEOD
[] = { 'E', 'O', 'D', 0 };
42 const sal_Unicode pKeyERROR
[] = { 'E', 'R', 'R', 'O', 'R', 0 };
43 const sal_Unicode pKeyTRUE
[] = { 'T', 'R', 'U', 'E', 0 };
44 const sal_Unicode pKeyFALSE
[] = { 'F', 'A', 'L', 'S', 'E', 0 };
45 const sal_Unicode pKeyNA
[] = { 'N', 'A', 0 };
46 const sal_Unicode pKeyV
[] = { 'V', 0 };
47 const sal_Unicode pKey1_0
[] = { '1', ',', '0', 0 };
49 ErrCode
ScFormatFilterPluginImpl::ScImportDif(SvStream
& rIn
, ScDocument
* pDoc
, const ScAddress
& rInsPos
,
50 const rtl_TextEncoding eVon
)
52 DifParser
aDifParser( rIn
, *pDoc
, eVon
);
54 SCTAB nBaseTab
= rInsPos
.Tab();
56 TOPIC eTopic
= T_UNKNOWN
;
57 bool bSyntErrWarn
= false;
58 bool bOverflowWarn
= false;
60 OUStringBuffer
& rData
= aDifParser
.m_aData
;
64 ScfStreamProgressBar
aPrgrsBar( rIn
, pDoc
->GetDocumentShell() );
66 while( eTopic
!= T_DATA
&& eTopic
!= T_END
)
68 eTopic
= aDifParser
.GetNextTopic();
72 const bool bData
= !rData
.isEmpty();
78 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 1 )
81 pDoc
->RenameTab(nBaseTab
, rData
.toString());
86 if( aDifParser
.nVector
!= 0 )
92 if( aDifParser
.nVector
!= 0 )
98 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 0 )
115 OSL_FAIL( "ScImportDif - missing enum" );
120 if( eTopic
== T_DATA
)
121 { // data starts here
122 SCCOL nBaseCol
= rInsPos
.Col();
124 SCCOL nColCnt
= SCCOL_MAX
;
125 SCROW nRowCnt
= rInsPos
.Row();
126 DifAttrCache aAttrCache
;
128 DATASET eCurrent
= D_UNKNOWN
;
130 ScSetStringParam aStrParam
; // used to set string value without number detection.
131 aStrParam
.setTextInput();
133 while( eCurrent
!= D_EOD
)
135 eCurrent
= aDifParser
.GetNextDataset();
137 aPrgrsBar
.Progress();
138 ScAddress
aPos(nColCnt
, nRowCnt
, nBaseTab
);
140 OUString aData
= rData
.toString();
145 if( nColCnt
< SCCOL_MAX
)
151 case D_NUMERIC
: // Number cell
152 if( nColCnt
== SCCOL_MAX
)
155 if( pDoc
->ValidCol(nColCnt
) && pDoc
->ValidRow(nRowCnt
) )
157 pDoc
->EnsureTable(nBaseTab
);
159 if( DifParser::IsV( aData
.getStr() ) )
161 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
162 aAttrCache
.SetNumFormat( pDoc
, nColCnt
, nRowCnt
,
163 aDifParser
.nNumFormat
);
165 else if( aData
== pKeyTRUE
|| aData
== pKeyFALSE
)
167 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
168 aAttrCache
.SetNumFormat( pDoc
, nColCnt
, nRowCnt
,
169 aDifParser
.nNumFormat
);
171 else if( aData
== pKeyNA
|| aData
== pKeyERROR
)
173 pDoc
->SetString(aPos
, aData
, &aStrParam
);
177 OUString aTmp
= "#IND:" + aData
+ "?";
178 pDoc
->SetString(aPos
, aTmp
, &aStrParam
);
182 bOverflowWarn
= true;
186 case D_STRING
: // Text cell
187 if( nColCnt
== SCCOL_MAX
)
190 if( pDoc
->ValidCol(nColCnt
) && pDoc
->ValidRow(nRowCnt
) )
192 if (!aData
.isEmpty())
194 pDoc
->EnsureTable(nBaseTab
);
195 pDoc
->SetTextCell(aPos
, aData
);
199 bOverflowWarn
= true;
208 OSL_FAIL( "ScImportDif - missing enum" );
212 aAttrCache
.Apply( *pDoc
, nBaseTab
);
215 return SCERR_IMPORT_FORMAT
;
219 // FIXME: Add proper warning!
220 return SCWARN_IMPORT_RANGE_OVERFLOW
;
222 else if( bOverflowWarn
)
223 return SCWARN_IMPORT_RANGE_OVERFLOW
;
228 DifParser::DifParser( SvStream
& rNewIn
, const ScDocument
& rDoc
, rtl_TextEncoding eCharSet
)
233 , pNumFormatter(rDoc
.GetFormatTable())
236 if ( rIn
.GetStreamCharSet() != eCharSet
)
238 OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
239 rIn
.SetStreamCharSet( eCharSet
);
241 rIn
.StartReadingUnicodeText( eCharSet
);
244 TOPIC
DifParser::GetNextTopic()
246 enum STATE
{ S_VectorVal
, S_Data
, S_END
, S_START
, S_UNKNOWN
, S_ERROR_L2
};
248 static const sal_Unicode pKeyLABEL
[] = { 'L', 'A', 'B', 'E', 'L', 0 };
249 static const sal_Unicode pKeyCOMMENT
[] = { 'C', 'O', 'M', 'M', 'E', 'N', 'T', 0 };
250 static const sal_Unicode pKeySIZE
[] = { 'S', 'I', 'Z', 'E', 0 };
251 static const sal_Unicode pKeyPERIODICITY
[] = { 'P', 'E', 'R', 'I', 'O', 'D', 'I', 'C', 'I', 'T', 'Y', 0 };
252 static const sal_Unicode pKeyMAJORSTART
[] = { 'M', 'A', 'J', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
253 static const sal_Unicode pKeyMINORSTART
[] = { 'M', 'I', 'N', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
254 static const sal_Unicode pKeyTRUELENGTH
[] = { 'T', 'R', 'U', 'E', 'L', 'E', 'N', 'G', 'T', 'H', 0 };
255 static const sal_Unicode pKeyUINITS
[] = { 'U', 'I', 'N', 'I', 'T', 'S', 0 };
256 static const sal_Unicode pKeyDISPLAYUNITS
[] = { 'D', 'I', 'S', 'P', 'L', 'A', 'Y', 'U', 'N', 'I', 'T', 'S', 0 };
257 static const sal_Unicode pKeyUNKNOWN
[] = { 0 };
259 static const sal_Unicode
* ppKeys
[] =
271 pKeyTRUELENGTH
, // 10
277 static const TOPIC pTopics
[] =
300 TOPIC eRet
= T_UNKNOWN
;
304 if( !ReadNextLine( aLine
) )
314 const sal_Unicode
* pRef
;
318 pRef
= ppKeys
[ nCnt
];
324 eRet
= pTopics
[ nCnt
];
330 pRef
= ppKeys
[ nCnt
];
344 const sal_Unicode
* pCur
= aLine
.getStr();
346 pCur
= ScanIntVal( pCur
, nVector
);
348 if( pCur
&& *pCur
== ',' )
351 ScanIntVal( pCur
, nVal
);
359 OSL_ENSURE( aLine
.getLength() >= 2,
360 "+GetNextTopic(): <String> is too short!" );
361 if( aLine
.getLength() > 2 )
362 m_aData
.append(aLine
.subView(1, aLine
.getLength() - 2));
368 OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
372 ReadNextLine( aLine
);
374 case S_ERROR_L2
: // error happened in line 2
376 ReadNextLine( aLine
);
380 OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
387 static void lcl_DeEscapeQuotesDif(OUStringBuffer
& rString
)
389 // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
390 // Single quote characters are left in place because older versions didn't
391 // escape quotes in strings (and Excel doesn't when using the clipboard).
392 // The quotes around the string are removed before this function is called.
394 rString
= rString
.toString().replaceAll("\"\"", "\"");
397 // Determine if passed in string is numeric data and set fVal/nNumFormat if so
398 DATASET
DifParser::GetNumberDataset( const sal_Unicode
* pPossibleNumericData
)
400 DATASET eRet
= D_SYNT_ERROR
;
402 OSL_ENSURE( pNumFormatter
, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
403 OUString
aTestVal( pPossibleNumericData
);
404 sal_uInt32 nFormat
= 0;
406 if( pNumFormatter
->IsNumberFormat( aTestVal
, nFormat
, fTmpVal
) )
409 nNumFormat
= nFormat
;
418 bool DifParser::ReadNextLine( OUString
& rStr
)
420 if( aLookAheadLine
.isEmpty() )
422 return rIn
.ReadUniOrByteStringLine( rStr
, rIn
.GetStreamCharSet() );
426 rStr
= aLookAheadLine
;
427 aLookAheadLine
.clear();
432 // Look ahead in the stream to determine if the next line is the first line of
433 // a valid data record structure
434 bool DifParser::LookAhead()
436 const sal_Unicode
* pCurrentBuffer
;
437 bool bValidStructure
= false;
439 OSL_ENSURE( aLookAheadLine
.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
440 rIn
.ReadUniOrByteStringLine( aLookAheadLine
, rIn
.GetStreamCharSet() );
442 pCurrentBuffer
= aLookAheadLine
.getStr();
444 switch( *pCurrentBuffer
)
446 case '-': // Special Datatype
449 if( Is1_0( pCurrentBuffer
) )
451 bValidStructure
= true;
454 case '0': // Numeric Data
456 if( *pCurrentBuffer
== ',' )
459 bValidStructure
= ( GetNumberDataset(pCurrentBuffer
) != D_SYNT_ERROR
);
462 case '1': // String Data
463 if( Is1_0( aLookAheadLine
.getStr() ) )
465 bValidStructure
= true;
469 return bValidStructure
;
472 DATASET
DifParser::GetNextDataset()
474 DATASET eRet
= D_UNKNOWN
;
476 const sal_Unicode
* pCurrentBuffer
;
478 ReadNextLine( aLine
);
480 pCurrentBuffer
= aLine
.getStr();
482 switch( *pCurrentBuffer
)
484 case '-': // Special Datatype
487 if( Is1_0( pCurrentBuffer
) )
489 ReadNextLine( aLine
);
490 if( IsBOT( aLine
.getStr() ) )
492 else if( IsEOD( aLine
.getStr() ) )
496 case '0': // Numeric Data
497 pCurrentBuffer
++; // value in fVal, 2. line in m_aData
498 if( *pCurrentBuffer
== ',' )
501 eRet
= GetNumberDataset(pCurrentBuffer
);
503 ReadNextLine( aTmpLine
);
504 if ( eRet
== D_SYNT_ERROR
)
505 { // for broken records write "#ERR: data" to cell
506 m_aData
= OUString::Concat("#ERR: ") + pCurrentBuffer
+ " (" + aTmpLine
+ ")";
515 case '1': // String Data
516 if( Is1_0( aLine
.getStr() ) )
518 ReadNextLine( aLine
);
519 sal_Int32 nLineLength
= aLine
.getLength();
520 const sal_Unicode
* pLine
= aLine
.getStr();
522 if( nLineLength
>= 1 && *pLine
== '"' )
524 // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
525 // A look ahead into the next line is needed in order to deal with
526 // multiline strings containing quotes
529 // Single line string
530 if( nLineLength
>= 2 && pLine
[nLineLength
- 1] == '"' )
532 m_aData
= aLine
.subView( 1, nLineLength
- 2 );
533 lcl_DeEscapeQuotesDif(m_aData
);
540 m_aData
= aLine
.subView( 1 );
541 bool bContinue
= true;
544 m_aData
.append("\n");
545 bContinue
= !rIn
.eof() && ReadNextLine( aLine
);
548 nLineLength
= aLine
.getLength();
549 if( nLineLength
>= 1 )
551 pLine
= aLine
.getStr();
552 bContinue
= !LookAhead();
555 m_aData
.append(aLine
);
557 else if( pLine
[nLineLength
- 1] == '"' )
559 m_aData
.append(aLine
.subView(0, nLineLength
-1));
560 lcl_DeEscapeQuotesDif(m_aData
);
572 if( eRet
== D_UNKNOWN
)
573 ReadNextLine( aLine
);
581 const sal_Unicode
* DifParser::ScanIntVal( const sal_Unicode
* pStart
, sal_uInt32
& rRet
)
583 // eat leading whitespace, not specified, but seen in the wild
584 while (*pStart
== ' ' || *pStart
== '\t')
587 sal_Unicode cCurrent
= *pStart
;
589 if( IsNumber( cCurrent
) )
590 rRet
= static_cast<sal_uInt32
>( cCurrent
- '0' );
597 while( IsNumber( cCurrent
) && rRet
< ( 0xFFFFFFFF / 10 ) )
600 rRet
+= static_cast<sal_uInt32
>( cCurrent
- '0' );
609 DifColumn::DifColumn ()
614 void DifColumn::SetNumFormat( const ScDocument
* pDoc
, SCROW nRow
, const sal_uInt32 nNumFormat
)
616 OSL_ENSURE( pDoc
->ValidRow(nRow
), "*DifColumn::SetNumFormat(): Row too big!" );
622 OSL_ENSURE( nRow
> 0,
623 "*DifColumn::SetNumFormat(): more cannot be zero!" );
624 OSL_ENSURE( nRow
> mpCurrent
->nEnd
,
625 "*DifColumn::SetNumFormat(): start from scratch?" );
627 if( mpCurrent
->nNumFormat
== nNumFormat
&& mpCurrent
->nEnd
== nRow
- 1 )
628 mpCurrent
->nEnd
= nRow
;
630 NewEntry( nRow
, nNumFormat
);
633 NewEntry(nRow
,nNumFormat
);
639 void DifColumn::NewEntry( const SCROW nPos
, const sal_uInt32 nNumFormat
)
641 maEntries
.emplace_back();
642 mpCurrent
= &maEntries
.back();
643 mpCurrent
->nStart
= mpCurrent
->nEnd
= nPos
;
644 mpCurrent
->nNumFormat
= nNumFormat
;
648 void DifColumn::Apply( ScDocument
& rDoc
, const SCCOL nCol
, const SCTAB nTab
)
650 ScPatternAttr
aAttr( rDoc
.GetPool() );
651 SfxItemSet
&rItemSet
= aAttr
.GetItemSet();
653 for (const auto& rEntry
: maEntries
)
655 OSL_ENSURE( rEntry
.nNumFormat
> 0,
656 "+DifColumn::Apply(): Number format must not be 0!" );
658 rItemSet
.Put( SfxUInt32Item( ATTR_VALUE_FORMAT
, rEntry
.nNumFormat
) );
660 rDoc
.ApplyPatternAreaTab( nCol
, rEntry
.nStart
, nCol
, rEntry
.nEnd
, nTab
, aAttr
);
662 rItemSet
.ClearItem();
666 DifAttrCache::DifAttrCache()
670 DifAttrCache::~DifAttrCache()
674 void DifAttrCache::SetNumFormat( const ScDocument
* pDoc
, const SCCOL nCol
, const SCROW nRow
, const sal_uInt32 nNumFormat
)
676 OSL_ENSURE( pDoc
->ValidCol(nCol
), "-DifAttrCache::SetNumFormat(): Col too big!" );
678 if( !maColMap
.count(nCol
) )
679 maColMap
[ nCol
].reset( new DifColumn
);
681 maColMap
[ nCol
]->SetNumFormat( pDoc
, nRow
, nNumFormat
);
684 void DifAttrCache::Apply( ScDocument
& rDoc
, SCTAB nTab
)
686 for( SCCOL nCol
: rDoc
.GetWritableColumnsRange(nTab
, 0, rDoc
.MaxCol()) )
688 if( maColMap
.count(nCol
) )
689 maColMap
[ nCol
]->Apply( rDoc
, nCol
, nTab
);
693 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */