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 .
23 #include <svl/zforlist.hxx>
26 #include "docpool.hxx"
27 #include "document.hxx"
29 #include "fprogressbar.hxx"
31 #include "patattr.hxx"
32 #include "scerrors.hxx"
33 #include "scitems.hxx"
34 #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 };
50 FltError
ScFormatFilterPluginImpl::ScImportDif( SvStream
& rIn
, ScDocument
* pDoc
, const ScAddress
& rInsPos
,
51 const rtl_TextEncoding eVon
, sal_uInt32 nDifOption
)
53 DifParser
aDifParser( rIn
, nDifOption
, *pDoc
, eVon
);
55 const bool bPlain
= aDifParser
.IsPlain();
57 SCTAB nBaseTab
= rInsPos
.Tab();
59 TOPIC eTopic
= T_UNKNOWN
;
60 bool bSyntErrWarn
= false;
61 bool bOverflowWarn
= false;
63 OUString
& aData
= aDifParser
.aData
;
68 ScfStreamProgressBar
aPrgrsBar( rIn
, pDoc
->GetDocumentShell() );
70 while( eTopic
!= T_DATA
&& eTopic
!= T_END
)
72 eTopic
= aDifParser
.GetNextTopic();
76 bData
= !aData
.isEmpty();
82 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 1 )
83 bSyntErrWarn
= sal_True
;
85 pDoc
->RenameTab( nBaseTab
, aData
);
90 if( aDifParser
.nVector
!= 0 )
96 if( aDifParser
.nVector
!= 0 )
102 if( aDifParser
.nVector
!= 0 || aDifParser
.nVal
!= 0 )
103 bSyntErrWarn
= sal_True
;
119 OSL_FAIL( "ScImportDif - missing enum" );
125 if( eTopic
== T_DATA
)
126 { // Ab hier kommen die Daten
127 SCCOL nBaseCol
= rInsPos
.Col();
129 SCCOL nColCnt
= SCCOL_MAX
;
130 SCROW nRowCnt
= rInsPos
.Row();
131 DifAttrCache
aAttrCache( bPlain
);
133 DATASET eAkt
= D_UNKNOWN
;
135 ScSetStringParam aStrParam
; // used to set string value without number detection.
136 aStrParam
.setTextInput();
138 while( eAkt
!= D_EOD
)
140 eAkt
= aDifParser
.GetNextDataset();
142 aPrgrsBar
.Progress();
143 ScAddress
aPos(nColCnt
, nRowCnt
, nBaseTab
);
148 if( nColCnt
< SCCOL_MAX
)
154 case D_NUMERIC
: // Numbercell
155 if( nColCnt
== SCCOL_MAX
)
158 if( ValidCol(nColCnt
) && ValidRow(nRowCnt
) )
160 pDoc
->EnsureTable(nBaseTab
);
162 if( DifParser::IsV( aData
.getStr() ) )
164 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
166 aAttrCache
.SetNumFormat( nColCnt
, nRowCnt
,
167 aDifParser
.nNumFormat
);
169 else if( aData
== pKeyTRUE
|| aData
== pKeyFALSE
)
171 pDoc
->SetValue(aPos
, aDifParser
.fVal
);
173 aAttrCache
.SetLogical( nColCnt
, nRowCnt
);
175 aAttrCache
.SetNumFormat( nColCnt
, nRowCnt
,
176 aDifParser
.nNumFormat
);
178 else if( aData
== pKeyNA
|| aData
== pKeyERROR
)
180 pDoc
->SetString(aPos
, aData
, &aStrParam
);
184 OUString aTmp
= "#IND:" + aData
+ "?";
185 pDoc
->SetString(aPos
, aTmp
, &aStrParam
);
189 bOverflowWarn
= true;
193 case D_STRING
: // Textcell
194 if( nColCnt
== SCCOL_MAX
)
197 if( ValidCol(nColCnt
) && ValidRow(nRowCnt
) )
199 if (!aData
.isEmpty())
201 pDoc
->EnsureTable(nBaseTab
);
202 pDoc
->SetTextCell(aPos
, aData
);
206 bOverflowWarn
= sal_True
;
215 OSL_FAIL( "ScImportDif - missing enum" );
219 aAttrCache
.Apply( *pDoc
, nBaseTab
);
225 //###############################################
226 // ACHTUNG: Hier fehlt noch die richtige Warnung!
227 return eERR_RNGOVRFLW
;
228 //###############################################
229 else if( bOverflowWarn
)
230 return eERR_RNGOVRFLW
;
236 DifParser::DifParser( SvStream
& rNewIn
, const sal_uInt32 nOption
, ScDocument
& rDoc
, rtl_TextEncoding e
) :
240 if ( rIn
.GetStreamCharSet() != eCharSet
)
242 OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
243 rIn
.SetStreamCharSet( eCharSet
);
245 rIn
.StartReadingUnicodeText( eCharSet
);
247 bPlain
= ( nOption
== SC_DIFOPT_PLAIN
);
250 pNumFormatter
= NULL
;
252 pNumFormatter
= rDoc
.GetFormatTable();
256 TOPIC
DifParser::GetNextTopic( void )
258 enum STATE
{ S_VectorVal
, S_Data
, S_END
, S_START
, S_UNKNOWN
, S_ERROR_L2
};
260 static const sal_Unicode pKeyLABEL
[] = { 'L', 'A', 'B', 'E', 'L', 0 };
261 static const sal_Unicode pKeyCOMMENT
[] = { 'C', 'O', 'M', 'M', 'E', 'N', 'T', 0 };
262 static const sal_Unicode pKeySIZE
[] = { 'S', 'I', 'Z', 'E', 0 };
263 static const sal_Unicode pKeyPERIODICITY
[] = { 'P', 'E', 'R', 'I', 'O', 'D', 'I', 'C', 'I', 'T', 'Y', 0 };
264 static const sal_Unicode pKeyMAJORSTART
[] = { 'M', 'A', 'J', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
265 static const sal_Unicode pKeyMINORSTART
[] = { 'M', 'I', 'N', 'O', 'R', 'S', 'T', 'A', 'R', 'T', 0 };
266 static const sal_Unicode pKeyTRUELENGTH
[] = { 'T', 'R', 'U', 'E', 'L', 'E', 'N', 'G', 'T', 'H', 0 };
267 static const sal_Unicode pKeyUINITS
[] = { 'U', 'I', 'N', 'I', 'T', 'S', 0 };
268 static const sal_Unicode pKeyDISPLAYUNITS
[] = { 'D', 'I', 'S', 'P', 'L', 'A', 'Y', 'U', 'N', 'I', 'T', 'S', 0 };
269 static const sal_Unicode pKeyUNKNOWN
[] = { 0 };
271 static const sal_Unicode
* ppKeys
[] =
283 pKeyTRUELENGTH
, // 10
289 static const TOPIC pTopics
[] =
312 TOPIC eRet
= T_UNKNOWN
;
316 if( !ReadNextLine( aLine
) )
326 const sal_Unicode
* pRef
;
328 sal_Bool bSearch
= sal_True
;
330 pRef
= ppKeys
[ nCnt
];
336 eRet
= pTopics
[ nCnt
];
342 pRef
= ppKeys
[ nCnt
];
356 const sal_Unicode
* pCur
= aLine
.getStr();
358 pCur
= ScanIntVal( pCur
, nVector
);
360 if( pCur
&& *pCur
== ',' )
363 ScanIntVal( pCur
, nVal
);
371 OSL_ENSURE( aLine
.getLength() >= 2,
372 "+GetNextTopic(): <String> ist zu kurz!" );
373 if( aLine
.getLength() > 2 )
374 aData
= aLine
.copy( 1, aLine
.getLength() - 2 );
380 OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
383 // 2 Zeilen ueberlesen
384 ReadNextLine( aLine
);
385 case S_ERROR_L2
: // Fehler in Line 2 aufgetreten
386 // eine Zeile ueberlesen
387 ReadNextLine( aLine
);
391 OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
399 static void lcl_DeEscapeQuotesDif( OUString
& rString
)
401 // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
402 // Single quote characters are left in place because older versions didn't
403 // escape quotes in strings (and Excel doesn't when using the clipboard).
404 // The quotes around the string are removed before this function is called.
406 rString
= rString
.replaceAll("\"\"", "\"");
409 // Determine if passed in string is numeric data and set fVal/nNumFormat if so
410 DATASET
DifParser::GetNumberDataset( const sal_Unicode
* pPossibleNumericData
)
412 DATASET eRet
= D_SYNT_ERROR
;
415 if( ScanFloatVal( pPossibleNumericData
) )
421 { // ...und zur Strafe mit'm Numberformatter...
422 OSL_ENSURE( pNumFormatter
, "-DifParser::GetNextDataset(): No Formatter, more fun!" );
423 OUString
aTestVal( pPossibleNumericData
);
424 sal_uInt32 nFormat
= 0;
426 if( pNumFormatter
->IsNumberFormat( aTestVal
, nFormat
, fTmpVal
) )
429 nNumFormat
= nFormat
;
438 bool DifParser::ReadNextLine( OUString
& rStr
)
440 if( aLookAheadLine
.isEmpty() )
442 return rIn
.ReadUniOrByteStringLine( rStr
, rIn
.GetStreamCharSet() );
446 rStr
= aLookAheadLine
;
447 aLookAheadLine
= OUString();
452 // Look ahead in the stream to determine if the next line is the first line of
453 // a valid data record structure
454 bool DifParser::LookAhead()
456 const sal_Unicode
* pAktBuffer
;
457 bool bValidStructure
= false;
459 OSL_ENSURE( aLookAheadLine
.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
460 rIn
.ReadUniOrByteStringLine( aLookAheadLine
, rIn
.GetStreamCharSet() );
462 pAktBuffer
= aLookAheadLine
.getStr();
464 switch( *pAktBuffer
)
466 case '-': // Special Datatype
469 if( Is1_0( pAktBuffer
) )
471 bValidStructure
= true;
474 case '0': // Numeric Data
476 if( *pAktBuffer
== ',' )
479 bValidStructure
= ( GetNumberDataset(pAktBuffer
) != D_SYNT_ERROR
);
482 case '1': // String Data
483 if( Is1_0( aLookAheadLine
.getStr() ) )
485 bValidStructure
= true;
489 return bValidStructure
;
492 DATASET
DifParser::GetNextDataset( void )
494 DATASET eRet
= D_UNKNOWN
;
496 const sal_Unicode
* pAktBuffer
;
498 ReadNextLine( aLine
);
500 pAktBuffer
= aLine
.getStr();
502 switch( *pAktBuffer
)
504 case '-': // Special Datatype
507 if( Is1_0( pAktBuffer
) )
509 ReadNextLine( aLine
);
510 if( IsBOT( aLine
.getStr() ) )
512 else if( IsEOD( aLine
.getStr() ) )
516 case '0': // Numeric Data
517 pAktBuffer
++; // Wert in fVal, 2. Zeile in aData
518 if( *pAktBuffer
== ',' )
521 eRet
= GetNumberDataset(pAktBuffer
);
523 ReadNextLine( aTmpLine
);
524 if ( eRet
== D_SYNT_ERROR
)
525 { // for broken records write "#ERR: data" to cell
526 OUStringBuffer
aTmp("#ERR: ");
527 aTmp
.append(pAktBuffer
).append(" (");
528 aTmp
.append(aTmpLine
).append(')');
529 aData
= aTmp
.makeStringAndClear();
538 case '1': // String Data
539 if( Is1_0( aLine
.getStr() ) )
541 ReadNextLine( aLine
);
542 sal_Int32 nLineLength
= aLine
.getLength();
543 const sal_Unicode
* pLine
= aLine
.getStr();
545 if( nLineLength
>= 1 && *pLine
== '"' )
547 // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
548 // A look ahead into the next line is needed in order to deal with
549 // multiline strings containing quotes
552 // Single line string
553 if( nLineLength
>= 2 && pLine
[nLineLength
- 1] == '"' )
555 aData
= aLine
.copy( 1, nLineLength
- 2 );
556 lcl_DeEscapeQuotesDif( aData
);
563 aData
= aLine
.copy( 1 );
564 bool bContinue
= true;
567 aData
= aData
+ "\n";
568 bContinue
= !rIn
.IsEof() && ReadNextLine( aLine
);
571 nLineLength
= aLine
.getLength();
572 if( nLineLength
>= 1 )
574 pLine
= aLine
.getStr();
575 bContinue
= !LookAhead();
578 aData
= aData
+ aLine
;
580 else if( pLine
[nLineLength
- 1] == '"' )
582 aData
= aData
+ aLine
.copy(0, nLineLength
-1 );
583 lcl_DeEscapeQuotesDif( aData
);
595 if( eRet
== D_UNKNOWN
)
596 ReadNextLine( aLine
);
605 const sal_Unicode
* DifParser::ScanIntVal( const sal_Unicode
* pStart
, sal_uInt32
& rRet
)
607 // eat leading whitespace, not specified, but seen in the wild
608 while (*pStart
== ' ' || *pStart
== '\t')
611 sal_Unicode cAkt
= *pStart
;
613 if( IsNumber( cAkt
) )
614 rRet
= ( sal_uInt32
) ( cAkt
- '0' );
621 while( IsNumber( cAkt
) && rRet
< ( 0xFFFFFFFF / 10 ) )
624 rRet
+= ( sal_uInt32
) ( cAkt
- '0' );
634 sal_Bool
DifParser::ScanFloatVal( const sal_Unicode
* pStart
)
636 sal_Bool bNeg
= false;
637 double fFracPos
= 1.0;
639 sal_Bool bExpNeg
= false;
640 sal_Bool bExpOverflow
= false;
641 static const sal_uInt16 nExpLimit
= 4096; // ACHTUNG: muss genauer ermittelt werden!
644 sal_Bool bRet
= false;
646 enum STATE
{ S_FIRST
, S_PRE
, S_POST
, S_EXP_FIRST
, S_EXP
, S_END
, S_FINDEND
};
650 double fNewVal
= 0.0;
658 if( IsNumber( cAkt
) )
661 fNewVal
+= cAkt
- '0';
686 if( IsNumber( cAkt
) )
689 fNewVal
+= cAkt
- '0';
704 case 0x00: // IsNumberEnding( cAkt )
705 bRet
= sal_True
; // no
712 if( IsNumber( cAkt
) )
714 fNewVal
+= fFracPos
* ( cAkt
- '0' );
725 case 0x00: // IsNumberEnding( cAkt )
726 bRet
= sal_True
; // no
733 if( IsNumber( cAkt
) )
735 if( nExp
< nExpLimit
)
738 nExp
+= ( sal_uInt16
) ( cAkt
- '0' );
757 if( IsNumber( cAkt
) )
759 if( nExp
< ( 0xFFFF / 10 ) )
762 nExp
+= ( sal_uInt16
) ( cAkt
- '0' );
766 bExpOverflow
= sal_True
;
772 bRet
= IsNumberEnding( cAkt
);
777 if( IsNumberEnding( cAkt
) )
779 bRet
= sal_True
; // damit sinnvoll weitergeparst werden kann
784 OSL_FAIL( "DifParser::ScanFloatVal - unexpected state" );
787 OSL_FAIL( "DifParser::ScanFloatVal - missing enum" );
795 return false; // ACHTUNG: hier muss noch differenziert werden
804 fNewVal
*= pow( 10.0, ( double ) nExp
);
811 DifColumn::DifColumn ()
816 void DifColumn::SetLogical( SCROW nRow
)
818 OSL_ENSURE( ValidRow(nRow
), "*DifColumn::SetLogical(): Row too big!" );
822 OSL_ENSURE( nRow
> 0, "*DifColumn::SetLogical(): more cannot be zero!" );
826 if( pAkt
->nEnd
== nRow
)
834 pAkt
->nStart
= pAkt
->nEnd
= nRow
;
836 aEntries
.push_back(pAkt
);
841 void DifColumn::SetNumFormat( SCROW nRow
, const sal_uInt32 nNumFormat
)
843 OSL_ENSURE( ValidRow(nRow
), "*DifColumn::SetNumFormat(): Row too big!" );
849 OSL_ENSURE( nRow
> 0,
850 "*DifColumn::SetNumFormat(): more cannot be zero!" );
851 OSL_ENSURE( nRow
> pAkt
->nEnd
,
852 "*DifColumn::SetNumFormat(): start from scratch?" );
854 if( pAkt
->nNumFormat
== nNumFormat
&& pAkt
->nEnd
== nRow
- 1 )
857 NewEntry( nRow
, nNumFormat
);
860 NewEntry(nRow
,nNumFormat
);
867 void DifColumn::NewEntry( const SCROW nPos
, const sal_uInt32 nNumFormat
)
870 pAkt
->nStart
= pAkt
->nEnd
= nPos
;
871 pAkt
->nNumFormat
= nNumFormat
;
873 aEntries
.push_back(pAkt
);
877 void DifColumn::Apply( ScDocument
& rDoc
, const SCCOL nCol
, const SCTAB nTab
, const ScPatternAttr
& rPattAttr
)
879 for (boost::ptr_vector
<ENTRY
>::const_iterator it
= aEntries
.begin(); it
!= aEntries
.end(); ++it
)
880 rDoc
.ApplyPatternAreaTab( nCol
, it
->nStart
, nCol
, it
->nEnd
, nTab
, rPattAttr
);
884 void DifColumn::Apply( ScDocument
& rDoc
, const SCCOL nCol
, const SCTAB nTab
)
886 ScPatternAttr
aAttr( rDoc
.GetPool() );
887 SfxItemSet
&rItemSet
= aAttr
.GetItemSet();
889 for (boost::ptr_vector
<ENTRY
>::const_iterator it
= aEntries
.begin(); it
!= aEntries
.end(); ++it
)
891 OSL_ENSURE( it
->nNumFormat
> 0,
892 "+DifColumn::Apply(): Numberformat darf hier nicht 0 sein!" );
894 rItemSet
.Put( SfxUInt32Item( ATTR_VALUE_FORMAT
, it
->nNumFormat
) );
896 rDoc
.ApplyPatternAreaTab( nCol
, it
->nStart
, nCol
, it
->nEnd
, nTab
, aAttr
);
898 rItemSet
.ClearItem();
903 DifAttrCache::DifAttrCache( const sal_Bool bNewPlain
)
906 ppCols
= new DifColumn
*[ MAXCOL
+ 1 ];
907 for( SCCOL nCnt
= 0 ; nCnt
<= MAXCOL
; nCnt
++ )
908 ppCols
[ nCnt
] = NULL
;
912 DifAttrCache::~DifAttrCache()
914 for( SCCOL nCnt
= 0 ; nCnt
<= MAXCOL
; nCnt
++ )
917 delete ppCols
[ nCnt
];
923 void DifAttrCache::SetLogical( const SCCOL nCol
, const SCROW nRow
)
925 OSL_ENSURE( ValidCol(nCol
), "-DifAttrCache::SetLogical(): Col too big!" );
926 OSL_ENSURE( bPlain
, "*DifAttrCache::SetLogical(): has to be Plain!" );
928 if( !ppCols
[ nCol
] )
929 ppCols
[ nCol
] = new DifColumn
;
931 ppCols
[ nCol
]->SetLogical( nRow
);
934 void DifAttrCache::SetNumFormat( const SCCOL nCol
, const SCROW nRow
, const sal_uInt32 nNumFormat
)
936 OSL_ENSURE( ValidCol(nCol
), "-DifAttrCache::SetNumFormat(): Col too big!" );
937 OSL_ENSURE( !bPlain
, "*DifAttrCache::SetNumFormat(): should not be Plain!" );
939 if( !ppCols
[ nCol
] )
940 ppCols
[ nCol
] = new DifColumn
;
942 ppCols
[ nCol
]->SetNumFormat( nRow
, nNumFormat
);
946 void DifAttrCache::Apply( ScDocument
& rDoc
, SCTAB nTab
)
950 ScPatternAttr
* pPatt
= NULL
;
952 for( SCCOL nCol
= 0 ; nCol
<= MAXCOL
; nCol
++ )
958 pPatt
= new ScPatternAttr( rDoc
.GetPool() );
959 pPatt
->GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT
,
960 rDoc
.GetFormatTable()->GetStandardFormat( NUMBERFORMAT_LOGICAL
) ) );
963 ppCols
[ nCol
]->Apply( rDoc
, nCol
, nTab
, *pPatt
);
972 for( SCCOL nCol
= 0 ; nCol
<= MAXCOL
; nCol
++ )
975 ppCols
[ nCol
]->Apply( rDoc
, nCol
, nTab
);
982 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */