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/zforlist.hxx>
21 #include <tools/stream.hxx>
22 #include <osl/diagnose.h>
24 #include <docpool.hxx>
25 #include <document.hxx>
26 #include <fprogressbar.hxx>
28 #include <patattr.hxx>
29 #include <scerrors.hxx>
30 #include <scitems.hxx>
31 #include <stringutil.hxx>
35 const sal_Unicode pKeyTABLE
[] = { 'T', 'A', 'B', 'L', 'E', 0 };
36 const sal_Unicode pKeyVECTORS
[] = { 'V', 'E', 'C', 'T', 'O', 'R', 'S', 0 };
37 const sal_Unicode pKeyTUPLES
[] = { 'T', 'U', 'P', 'L', 'E', 'S', 0 };
38 const sal_Unicode pKeyDATA
[] = { 'D', 'A', 'T', 'A', 0 };
39 const sal_Unicode pKeyBOT
[] = { 'B', 'O', 'T', 0 };
40 const sal_Unicode pKeyEOD
[] = { 'E', 'O', 'D', 0 };
41 const sal_Unicode pKeyERROR
[] = { 'E', 'R', 'R', 'O', 'R', 0 };
42 const sal_Unicode pKeyTRUE
[] = { 'T', 'R', 'U', 'E', 0 };
43 const sal_Unicode pKeyFALSE
[] = { 'F', 'A', 'L', 'S', 'E', 0 };
44 const sal_Unicode pKeyNA
[] = { 'N', 'A', 0 };
45 const sal_Unicode pKeyV
[] = { 'V', 0 };
46 const sal_Unicode pKey1_0
[] = { '1', ',', '0', 0 };
48 ErrCode
ScFormatFilterPluginImpl::ScImportDif(SvStream
& rIn
, ScDocument
* pDoc
, const ScAddress
& rInsPos
,
49 const rtl_TextEncoding eVon
)
51 DifParser
aDifParser( rIn
, *pDoc
, eVon
);
53 SCTAB nBaseTab
= rInsPos
.Tab();
55 TOPIC eTopic
= T_UNKNOWN
;
56 bool bSyntErrWarn
= false;
57 bool bOverflowWarn
= false;
59 OUStringBuffer
& rData
= aDifParser
.m_aData
;
63 ScfStreamProgressBar
aPrgrsBar( rIn
, pDoc
->GetDocumentShell() );
65 while( eTopic
!= T_DATA
&& eTopic
!= T_END
)
67 eTopic
= aDifParser
.GetNextTopic();
71 const bool bData
= !rData
.isEmpty();
77 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 1 )
80 pDoc
->RenameTab(nBaseTab
, rData
.toString());
85 if( aDifParser
.nVector
!= 0 )
91 if( aDifParser
.nVector
!= 0 )
97 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 0 )
114 OSL_FAIL( "ScImportDif - missing enum" );
119 if( eTopic
== T_DATA
)
120 { // data starts here
121 SCCOL nBaseCol
= rInsPos
.Col();
123 SCCOL nColCnt
= SCCOL_MAX
;
124 SCROW nRowCnt
= rInsPos
.Row();
125 DifAttrCache aAttrCache
;
127 DATASET eCurrent
= D_UNKNOWN
;
129 ScSetStringParam aStrParam
; // used to set string value without number detection.
130 aStrParam
.setTextInput();
132 while( eCurrent
!= D_EOD
)
134 eCurrent
= aDifParser
.GetNextDataset();
136 aPrgrsBar
.Progress();
137 ScAddress
aPos(nColCnt
, nRowCnt
, nBaseTab
);
139 OUString aData
= rData
.toString();
144 if( nColCnt
< SCCOL_MAX
)
150 case D_NUMERIC
: // Number cell
151 if( nColCnt
== SCCOL_MAX
)
154 if( ValidCol(nColCnt
) && ValidRow(nRowCnt
) )
156 pDoc
->EnsureTable(nBaseTab
);
158 if( DifParser::IsV( aData
.getStr() ) )
160 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
161 aAttrCache
.SetNumFormat( nColCnt
, nRowCnt
,
162 aDifParser
.nNumFormat
);
164 else if( aData
== pKeyTRUE
|| aData
== pKeyFALSE
)
166 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
167 aAttrCache
.SetNumFormat( nColCnt
, nRowCnt
,
168 aDifParser
.nNumFormat
);
170 else if( aData
== pKeyNA
|| aData
== pKeyERROR
)
172 pDoc
->SetString(aPos
, aData
, &aStrParam
);
176 OUString aTmp
= "#IND:" + aData
+ "?";
177 pDoc
->SetString(aPos
, aTmp
, &aStrParam
);
181 bOverflowWarn
= true;
185 case D_STRING
: // Text cell
186 if( nColCnt
== SCCOL_MAX
)
189 if( ValidCol(nColCnt
) && ValidRow(nRowCnt
) )
191 if (!aData
.isEmpty())
193 pDoc
->EnsureTable(nBaseTab
);
194 pDoc
->SetTextCell(aPos
, aData
);
198 bOverflowWarn
= true;
207 OSL_FAIL( "ScImportDif - missing enum" );
211 aAttrCache
.Apply( *pDoc
, nBaseTab
);
214 return SCERR_IMPORT_FORMAT
;
218 // FIXME: Add proper warning!
219 return SCWARN_IMPORT_RANGE_OVERFLOW
;
221 else if( bOverflowWarn
)
222 return SCWARN_IMPORT_RANGE_OVERFLOW
;
227 DifParser::DifParser( SvStream
& rNewIn
, const ScDocument
& rDoc
, rtl_TextEncoding eCharSet
)
232 , pNumFormatter(rDoc
.GetFormatTable())
235 if ( rIn
.GetStreamCharSet() != eCharSet
)
237 OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
238 rIn
.SetStreamCharSet( eCharSet
);
240 rIn
.StartReadingUnicodeText( eCharSet
);
243 TOPIC
DifParser::GetNextTopic()
245 enum STATE
{ S_VectorVal
, S_Data
, S_END
, S_START
, S_UNKNOWN
, S_ERROR_L2
};
247 static const sal_Unicode pKeyLABEL
[] = { 'L', 'A', 'B', 'E', 'L', 0 };
248 static const sal_Unicode pKeyCOMMENT
[] = { 'C', 'O', 'M', 'M', 'E', 'N', 'T', 0 };
249 static const sal_Unicode pKeySIZE
[] = { 'S', 'I', 'Z', 'E', 0 };
250 static const sal_Unicode pKeyPERIODICITY
[] = { 'P', 'E', 'R', 'I', 'O', 'D', 'I', 'C', 'I', 'T', 'Y', 0 };
251 static const sal_Unicode pKeyMAJORSTART
[] = { 'M', 'A', 'J', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
252 static const sal_Unicode pKeyMINORSTART
[] = { 'M', 'I', 'N', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
253 static const sal_Unicode pKeyTRUELENGTH
[] = { 'T', 'R', 'U', 'E', 'L', 'E', 'N', 'G', 'T', 'H', 0 };
254 static const sal_Unicode pKeyUINITS
[] = { 'U', 'I', 'N', 'I', 'T', 'S', 0 };
255 static const sal_Unicode pKeyDISPLAYUNITS
[] = { 'D', 'I', 'S', 'P', 'L', 'A', 'Y', 'U', 'N', 'I', 'T', 'S', 0 };
256 static const sal_Unicode pKeyUNKNOWN
[] = { 0 };
258 static const sal_Unicode
* ppKeys
[] =
270 pKeyTRUELENGTH
, // 10
276 static const TOPIC pTopics
[] =
299 TOPIC eRet
= T_UNKNOWN
;
303 if( !ReadNextLine( aLine
) )
313 const sal_Unicode
* pRef
;
317 pRef
= ppKeys
[ nCnt
];
323 eRet
= pTopics
[ nCnt
];
329 pRef
= ppKeys
[ nCnt
];
343 const sal_Unicode
* pCur
= aLine
.getStr();
345 pCur
= ScanIntVal( pCur
, nVector
);
347 if( pCur
&& *pCur
== ',' )
350 ScanIntVal( pCur
, nVal
);
358 OSL_ENSURE( aLine
.getLength() >= 2,
359 "+GetNextTopic(): <String> is too short!" );
360 if( aLine
.getLength() > 2 )
361 m_aData
.append(aLine
.copy(1, aLine
.getLength() - 2));
367 OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
371 ReadNextLine( aLine
);
373 case S_ERROR_L2
: // error happened in line 2
375 ReadNextLine( aLine
);
379 OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
386 static void lcl_DeEscapeQuotesDif(OUStringBuffer
& rString
)
388 // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
389 // Single quote characters are left in place because older versions didn't
390 // escape quotes in strings (and Excel doesn't when using the clipboard).
391 // The quotes around the string are removed before this function is called.
393 rString
= rString
.toString().replaceAll("\"\"", "\"");
396 // Determine if passed in string is numeric data and set fVal/nNumFormat if so
397 DATASET
DifParser::GetNumberDataset( const sal_Unicode
* pPossibleNumericData
)
399 DATASET eRet
= D_SYNT_ERROR
;
401 OSL_ENSURE( pNumFormatter
, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
402 OUString
aTestVal( pPossibleNumericData
);
403 sal_uInt32 nFormat
= 0;
405 if( pNumFormatter
->IsNumberFormat( aTestVal
, nFormat
, fTmpVal
) )
408 nNumFormat
= nFormat
;
417 bool DifParser::ReadNextLine( OUString
& rStr
)
419 if( aLookAheadLine
.isEmpty() )
421 return rIn
.ReadUniOrByteStringLine( rStr
, rIn
.GetStreamCharSet() );
425 rStr
= aLookAheadLine
;
426 aLookAheadLine
.clear();
431 // Look ahead in the stream to determine if the next line is the first line of
432 // a valid data record structure
433 bool DifParser::LookAhead()
435 const sal_Unicode
* pCurrentBuffer
;
436 bool bValidStructure
= false;
438 OSL_ENSURE( aLookAheadLine
.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
439 rIn
.ReadUniOrByteStringLine( aLookAheadLine
, rIn
.GetStreamCharSet() );
441 pCurrentBuffer
= aLookAheadLine
.getStr();
443 switch( *pCurrentBuffer
)
445 case '-': // Special Datatype
448 if( Is1_0( pCurrentBuffer
) )
450 bValidStructure
= true;
453 case '0': // Numeric Data
455 if( *pCurrentBuffer
== ',' )
458 bValidStructure
= ( GetNumberDataset(pCurrentBuffer
) != D_SYNT_ERROR
);
461 case '1': // String Data
462 if( Is1_0( aLookAheadLine
.getStr() ) )
464 bValidStructure
= true;
468 return bValidStructure
;
471 DATASET
DifParser::GetNextDataset()
473 DATASET eRet
= D_UNKNOWN
;
475 const sal_Unicode
* pCurrentBuffer
;
477 ReadNextLine( aLine
);
479 pCurrentBuffer
= aLine
.getStr();
481 switch( *pCurrentBuffer
)
483 case '-': // Special Datatype
486 if( Is1_0( pCurrentBuffer
) )
488 ReadNextLine( aLine
);
489 if( IsBOT( aLine
.getStr() ) )
491 else if( IsEOD( aLine
.getStr() ) )
495 case '0': // Numeric Data
496 pCurrentBuffer
++; // value in fVal, 2. line in m_aData
497 if( *pCurrentBuffer
== ',' )
500 eRet
= GetNumberDataset(pCurrentBuffer
);
502 ReadNextLine( aTmpLine
);
503 if ( eRet
== D_SYNT_ERROR
)
504 { // for broken records write "#ERR: data" to cell
506 m_aData
.append(pCurrentBuffer
).append(" (");
507 m_aData
.append(aTmpLine
).append(')');
516 case '1': // String Data
517 if( Is1_0( aLine
.getStr() ) )
519 ReadNextLine( aLine
);
520 sal_Int32 nLineLength
= aLine
.getLength();
521 const sal_Unicode
* pLine
= aLine
.getStr();
523 if( nLineLength
>= 1 && *pLine
== '"' )
525 // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
526 // A look ahead into the next line is needed in order to deal with
527 // multiline strings containing quotes
530 // Single line string
531 if( nLineLength
>= 2 && pLine
[nLineLength
- 1] == '"' )
533 m_aData
= aLine
.copy( 1, nLineLength
- 2 );
534 lcl_DeEscapeQuotesDif(m_aData
);
541 m_aData
= aLine
.copy( 1 );
542 bool bContinue
= true;
545 m_aData
.append("\n");
546 bContinue
= !rIn
.eof() && ReadNextLine( aLine
);
549 nLineLength
= aLine
.getLength();
550 if( nLineLength
>= 1 )
552 pLine
= aLine
.getStr();
553 bContinue
= !LookAhead();
556 m_aData
.append(aLine
);
558 else if( pLine
[nLineLength
- 1] == '"' )
560 m_aData
.append(aLine
.copy(0, nLineLength
-1));
561 lcl_DeEscapeQuotesDif(m_aData
);
573 if( eRet
== D_UNKNOWN
)
574 ReadNextLine( aLine
);
582 const sal_Unicode
* DifParser::ScanIntVal( const sal_Unicode
* pStart
, sal_uInt32
& rRet
)
584 // eat leading whitespace, not specified, but seen in the wild
585 while (*pStart
== ' ' || *pStart
== '\t')
588 sal_Unicode cCurrent
= *pStart
;
590 if( IsNumber( cCurrent
) )
591 rRet
= static_cast<sal_uInt32
>( cCurrent
- '0' );
598 while( IsNumber( cCurrent
) && rRet
< ( 0xFFFFFFFF / 10 ) )
601 rRet
+= static_cast<sal_uInt32
>( cCurrent
- '0' );
610 DifColumn::DifColumn ()
615 void DifColumn::SetNumFormat( SCROW nRow
, const sal_uInt32 nNumFormat
)
617 OSL_ENSURE( ValidRow(nRow
), "*DifColumn::SetNumFormat(): Row too big!" );
623 OSL_ENSURE( nRow
> 0,
624 "*DifColumn::SetNumFormat(): more cannot be zero!" );
625 OSL_ENSURE( nRow
> mpCurrent
->nEnd
,
626 "*DifColumn::SetNumFormat(): start from scratch?" );
628 if( mpCurrent
->nNumFormat
== nNumFormat
&& mpCurrent
->nEnd
== nRow
- 1 )
629 mpCurrent
->nEnd
= nRow
;
631 NewEntry( nRow
, nNumFormat
);
634 NewEntry(nRow
,nNumFormat
);
640 void DifColumn::NewEntry( const SCROW nPos
, const sal_uInt32 nNumFormat
)
642 maEntries
.emplace_back();
643 mpCurrent
= &maEntries
.back();
644 mpCurrent
->nStart
= mpCurrent
->nEnd
= nPos
;
645 mpCurrent
->nNumFormat
= nNumFormat
;
649 void DifColumn::Apply( ScDocument
& rDoc
, const SCCOL nCol
, const SCTAB nTab
)
651 ScPatternAttr
aAttr( rDoc
.GetPool() );
652 SfxItemSet
&rItemSet
= aAttr
.GetItemSet();
654 for (const auto& rEntry
: maEntries
)
656 OSL_ENSURE( rEntry
.nNumFormat
> 0,
657 "+DifColumn::Apply(): Number format must not be 0!" );
659 rItemSet
.Put( SfxUInt32Item( ATTR_VALUE_FORMAT
, rEntry
.nNumFormat
) );
661 rDoc
.ApplyPatternAreaTab( nCol
, rEntry
.nStart
, nCol
, rEntry
.nEnd
, nTab
, aAttr
);
663 rItemSet
.ClearItem();
667 DifAttrCache::DifAttrCache()
671 DifAttrCache::~DifAttrCache()
675 void DifAttrCache::SetNumFormat( const SCCOL nCol
, const SCROW nRow
, const sal_uInt32 nNumFormat
)
677 OSL_ENSURE( ValidCol(nCol
), "-DifAttrCache::SetNumFormat(): Col too big!" );
679 if( !maColMap
.count(nCol
) )
680 maColMap
[ nCol
].reset( new DifColumn
);
682 maColMap
[ nCol
]->SetNumFormat( nRow
, nNumFormat
);
685 void DifAttrCache::Apply( ScDocument
& rDoc
, SCTAB nTab
)
687 for( SCCOL nCol
: rDoc
.GetColumnsRange(nTab
, 0, rDoc
.MaxCol()) )
689 if( maColMap
.count(nCol
) )
690 maColMap
[ nCol
]->Apply( rDoc
, nCol
, nTab
);
694 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */