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 <svl/zforlist.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 <fprogressbar.hxx>
27 #include <ftools.hxx>
28 #include <patattr.hxx>
29 #include <scerrors.hxx>
30 #include <scitems.hxx>
31 #include <stringutil.hxx>
32 #include <table.hxx>
33 #include <memory>
34 
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 };
47 
ScImportDif(SvStream & rIn,ScDocument * pDoc,const ScAddress & rInsPos,const rtl_TextEncoding eVon)48 ErrCode ScFormatFilterPluginImpl::ScImportDif(SvStream& rIn, ScDocument* pDoc, const ScAddress& rInsPos,
49                         const rtl_TextEncoding eVon )
50 {
51     DifParser   aDifParser( rIn, *pDoc, eVon );
52 
53     SCTAB       nBaseTab = rInsPos.Tab();
54 
55     TOPIC       eTopic = T_UNKNOWN;
56     bool        bSyntErrWarn = false;
57     bool        bOverflowWarn = false;
58 
59     OUStringBuffer& rData = aDifParser.m_aData;
60 
61     rIn.Seek( 0 );
62 
63     ScfStreamProgressBar aPrgrsBar( rIn, pDoc->GetDocumentShell() );
64 
65     while( eTopic != T_DATA && eTopic != T_END )
66     {
67         eTopic = aDifParser.GetNextTopic();
68 
69         aPrgrsBar.Progress();
70 
71         const bool bData = !rData.isEmpty();
72 
73         switch( eTopic )
74         {
75             case T_TABLE:
76             {
77                 if( aDifParser.nVector != 0 || aDifParser.nVal != 1 )
78                     bSyntErrWarn = true;
79                 if( bData )
80                     pDoc->RenameTab(nBaseTab, rData.toString());
81             }
82                 break;
83             case T_VECTORS:
84             {
85                 if( aDifParser.nVector != 0 )
86                     bSyntErrWarn = true;
87             }
88                 break;
89             case T_TUPLES:
90             {
91                 if( aDifParser.nVector != 0 )
92                     bSyntErrWarn = true;
93             }
94                 break;
95             case T_DATA:
96             {
97                 if( aDifParser.nVector != 0 || aDifParser.nVal != 0 )
98                     bSyntErrWarn = true;
99             }
100                 break;
101             case T_LABEL:
102             case T_COMMENT:
103             case T_SIZE:
104             case T_PERIODICITY:
105             case T_MAJORSTART:
106             case T_MINORSTART:
107             case T_TRUELENGTH:
108             case T_UINITS:
109             case T_DISPLAYUNITS:
110             case T_END:
111             case T_UNKNOWN:
112                 break;
113             default:
114                 OSL_FAIL( "ScImportDif - missing enum" );
115         }
116 
117     }
118 
119     if( eTopic == T_DATA )
120     {   // data starts here
121         SCCOL               nBaseCol = rInsPos.Col();
122 
123         SCCOL               nColCnt = SCCOL_MAX;
124         SCROW               nRowCnt = rInsPos.Row();
125         DifAttrCache        aAttrCache;
126 
127         DATASET             eCurrent = D_UNKNOWN;
128 
129         ScSetStringParam aStrParam; // used to set string value without number detection.
130         aStrParam.setTextInput();
131 
132         while( eCurrent != D_EOD )
133         {
134             eCurrent = aDifParser.GetNextDataset();
135 
136             aPrgrsBar.Progress();
137             ScAddress aPos(nColCnt, nRowCnt, nBaseTab);
138 
139             OUString aData = rData.toString();
140 
141             switch( eCurrent )
142             {
143                 case D_BOT:
144                     if( nColCnt < SCCOL_MAX )
145                         nRowCnt++;
146                     nColCnt = nBaseCol;
147                     break;
148                 case D_EOD:
149                     break;
150                 case D_NUMERIC:                 // Number cell
151                     if( nColCnt == SCCOL_MAX )
152                         nColCnt = nBaseCol;
153 
154                     if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
155                     {
156                         pDoc->EnsureTable(nBaseTab);
157 
158                         if( DifParser::IsV( aData.getStr() ) )
159                         {
160                             pDoc->SetValue(aPos, aDifParser.fVal);
161                             aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
162                                     aDifParser.nNumFormat );
163                         }
164                         else if( aData == pKeyTRUE || aData == pKeyFALSE )
165                         {
166                             pDoc->SetValue(aPos, aDifParser.fVal);
167                             aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
168                                 aDifParser.nNumFormat );
169                         }
170                         else if( aData == pKeyNA || aData == pKeyERROR  )
171                         {
172                             pDoc->SetString(aPos, aData, &aStrParam);
173                         }
174                         else
175                         {
176                             OUString aTmp = "#IND:" + aData + "?";
177                             pDoc->SetString(aPos, aTmp, &aStrParam);
178                         }
179                     }
180                     else
181                         bOverflowWarn = true;
182 
183                     nColCnt++;
184                     break;
185                 case D_STRING:                  // Text cell
186                     if( nColCnt == SCCOL_MAX )
187                         nColCnt = nBaseCol;
188 
189                     if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
190                     {
191                         if (!aData.isEmpty())
192                         {
193                             pDoc->EnsureTable(nBaseTab);
194                             pDoc->SetTextCell(aPos, aData);
195                         }
196                     }
197                     else
198                         bOverflowWarn = true;
199 
200                     nColCnt++;
201                     break;
202                 case D_UNKNOWN:
203                     break;
204                 case D_SYNT_ERROR:
205                     break;
206                 default:
207                     OSL_FAIL( "ScImportDif - missing enum" );
208             }
209         }
210 
211         aAttrCache.Apply( *pDoc, nBaseTab );
212     }
213     else
214         return SCERR_IMPORT_FORMAT;
215 
216     if( bSyntErrWarn )
217 
218         // FIXME: Add proper warning!
219         return SCWARN_IMPORT_RANGE_OVERFLOW;
220 
221     else if( bOverflowWarn )
222         return SCWARN_IMPORT_RANGE_OVERFLOW;
223     else
224         return ERRCODE_NONE;
225 }
226 
DifParser(SvStream & rNewIn,const ScDocument & rDoc,rtl_TextEncoding eCharSet)227 DifParser::DifParser( SvStream& rNewIn, const ScDocument& rDoc, rtl_TextEncoding eCharSet )
228     : fVal(0.0)
229     , nVector(0)
230     , nVal(0)
231     , nNumFormat(0)
232     , pNumFormatter(rDoc.GetFormatTable())
233     , rIn(rNewIn)
234 {
235     if ( rIn.GetStreamCharSet() != eCharSet )
236     {
237         OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
238         rIn.SetStreamCharSet( eCharSet );
239     }
240     rIn.StartReadingUnicodeText( eCharSet );
241 }
242 
GetNextTopic()243 TOPIC DifParser::GetNextTopic()
244 {
245     enum STATE { S_VectorVal, S_Data, S_END, S_START, S_UNKNOWN, S_ERROR_L2 };
246 
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 };
257 
258     static const sal_Unicode*   ppKeys[] =
259     {
260         pKeyTABLE,              // 0
261         pKeyVECTORS,
262         pKeyTUPLES,
263         pKeyDATA,
264         pKeyLABEL,
265         pKeyCOMMENT,            // 5
266         pKeySIZE,
267         pKeyPERIODICITY,
268         pKeyMAJORSTART,
269         pKeyMINORSTART,
270         pKeyTRUELENGTH,         // 10
271         pKeyUINITS,
272         pKeyDISPLAYUNITS,
273         pKeyUNKNOWN             // 13
274     };
275 
276     static const TOPIC      pTopics[] =
277     {
278         T_TABLE,                // 0
279         T_VECTORS,
280         T_TUPLES,
281         T_DATA,
282         T_LABEL,
283         T_COMMENT,              // 5
284         T_SIZE,
285         T_PERIODICITY,
286         T_MAJORSTART,
287         T_MINORSTART,
288         T_TRUELENGTH,           // 10
289         T_UINITS,
290         T_DISPLAYUNITS,
291         T_UNKNOWN               // 13
292     };
293 
294     STATE                   eS = S_START;
295     OUString           aLine;
296 
297     nVector = 0;
298     nVal = 0;
299     TOPIC eRet = T_UNKNOWN;
300 
301     while( eS != S_END )
302     {
303         if( !ReadNextLine( aLine ) )
304         {
305             eS = S_END;
306             eRet = T_END;
307         }
308 
309         switch( eS )
310         {
311             case S_START:
312             {
313                 const sal_Unicode*  pRef;
314                 sal_uInt16          nCnt = 0;
315                 bool            bSearch = true;
316 
317                 pRef = ppKeys[ nCnt ];
318 
319                 while( bSearch )
320                 {
321                     if( aLine == pRef )
322                     {
323                         eRet = pTopics[ nCnt ];
324                         bSearch = false;
325                     }
326                     else
327                     {
328                         nCnt++;
329                         pRef = ppKeys[ nCnt ];
330                         if( !*pRef )
331                             bSearch = false;
332                     }
333                 }
334 
335                 if( *pRef )
336                     eS = S_VectorVal;
337                 else
338                     eS = S_UNKNOWN;
339             }
340                 break;
341             case S_VectorVal:
342             {
343                 const sal_Unicode*      pCur = aLine.getStr();
344 
345                 pCur = ScanIntVal( pCur, nVector );
346 
347                 if( pCur && *pCur == ',' )
348                 {
349                     pCur++;
350                     ScanIntVal( pCur, nVal );
351                     eS = S_Data;
352                 }
353                 else
354                     eS = S_ERROR_L2;
355             }
356                 break;
357             case S_Data:
358                 OSL_ENSURE( aLine.getLength() >= 2,
359                     "+GetNextTopic(): <String> is too short!" );
360                 if( aLine.getLength() > 2 )
361                     m_aData.append(aLine.subView(1, aLine.getLength() - 2));
362                 else
363                     m_aData.truncate();
364                 eS = S_END;
365                 break;
366             case S_END:
367                 OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
368                 break;
369             case S_UNKNOWN:
370                 // skip 2 lines
371                 ReadNextLine( aLine );
372                 [[fallthrough]];
373             case S_ERROR_L2:                // error happened in line 2
374                 // skip 1 line
375                 ReadNextLine( aLine );
376                 eS = S_END;
377                 break;
378             default:
379                 OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
380         }
381     }
382 
383     return eRet;
384 }
385 
lcl_DeEscapeQuotesDif(OUStringBuffer & rString)386 static void lcl_DeEscapeQuotesDif(OUStringBuffer& rString)
387 {
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.
392 
393     rString = rString.toString().replaceAll("\"\"", "\"");
394 }
395 
396 // Determine if passed in string is numeric data and set fVal/nNumFormat if so
GetNumberDataset(const sal_Unicode * pPossibleNumericData)397 DATASET DifParser::GetNumberDataset( const sal_Unicode* pPossibleNumericData )
398 {
399     DATASET eRet = D_SYNT_ERROR;
400 
401     OSL_ENSURE( pNumFormatter, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
402     OUString aTestVal( pPossibleNumericData );
403     sal_uInt32 nFormat = 0;
404     double fTmpVal;
405     if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
406     {
407         fVal = fTmpVal;
408         nNumFormat = nFormat;
409         eRet = D_NUMERIC;
410     }
411     else
412         eRet = D_SYNT_ERROR;
413 
414     return eRet;
415 }
416 
ReadNextLine(OUString & rStr)417 bool DifParser::ReadNextLine( OUString& rStr )
418 {
419     if( aLookAheadLine.isEmpty() )
420     {
421         return rIn.ReadUniOrByteStringLine( rStr, rIn.GetStreamCharSet() );
422     }
423     else
424     {
425         rStr = aLookAheadLine;
426         aLookAheadLine.clear();
427         return true;
428     }
429 }
430 
431 // Look ahead in the stream to determine if the next line is the first line of
432 // a valid data record structure
LookAhead()433 bool DifParser::LookAhead()
434 {
435     const sal_Unicode* pCurrentBuffer;
436     bool bValidStructure = false;
437 
438     OSL_ENSURE( aLookAheadLine.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
439     rIn.ReadUniOrByteStringLine( aLookAheadLine, rIn.GetStreamCharSet() );
440 
441     pCurrentBuffer = aLookAheadLine.getStr();
442 
443     switch( *pCurrentBuffer )
444     {
445         case '-':                   // Special Datatype
446             pCurrentBuffer++;
447 
448             if( Is1_0( pCurrentBuffer ) )
449             {
450                 bValidStructure = true;
451             }
452             break;
453         case '0':                   // Numeric Data
454             pCurrentBuffer++;
455             if( *pCurrentBuffer == ',' )
456             {
457                 pCurrentBuffer++;
458                 bValidStructure = ( GetNumberDataset(pCurrentBuffer) != D_SYNT_ERROR );
459             }
460             break;
461         case '1':                   // String Data
462             if( Is1_0( aLookAheadLine.getStr() ) )
463             {
464                 bValidStructure = true;
465             }
466             break;
467     }
468     return bValidStructure;
469 }
470 
GetNextDataset()471 DATASET DifParser::GetNextDataset()
472 {
473     DATASET eRet = D_UNKNOWN;
474     OUString aLine;
475     const sal_Unicode* pCurrentBuffer;
476 
477     ReadNextLine( aLine );
478 
479     pCurrentBuffer = aLine.getStr();
480 
481     switch( *pCurrentBuffer )
482     {
483         case '-':                   // Special Datatype
484             pCurrentBuffer++;
485 
486             if( Is1_0( pCurrentBuffer ) )
487             {
488                 ReadNextLine( aLine );
489                 if( IsBOT( aLine.getStr() ) )
490                     eRet = D_BOT;
491                 else if( IsEOD( aLine.getStr() ) )
492                     eRet = D_EOD;
493             }
494             break;
495         case '0':                   // Numeric Data
496             pCurrentBuffer++;       // value in fVal, 2. line in m_aData
497             if( *pCurrentBuffer == ',' )
498             {
499                 pCurrentBuffer++;
500                 eRet = GetNumberDataset(pCurrentBuffer);
501                 OUString aTmpLine;
502                 ReadNextLine( aTmpLine );
503                 if ( eRet == D_SYNT_ERROR )
504                 {   // for broken records write "#ERR: data" to cell
505                     m_aData = OUString::Concat("#ERR: ") + pCurrentBuffer + " (" +  aTmpLine + ")";
506                     eRet = D_STRING;
507                 }
508                 else
509                 {
510                     m_aData = aTmpLine;
511                 }
512             }
513             break;
514         case '1':                   // String Data
515             if( Is1_0( aLine.getStr() ) )
516             {
517                 ReadNextLine( aLine );
518                 sal_Int32 nLineLength = aLine.getLength();
519                 const sal_Unicode* pLine = aLine.getStr();
520 
521                 if( nLineLength >= 1 && *pLine == '"' )
522                 {
523                     // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
524                     // A look ahead into the next line is needed in order to deal with
525                     // multiline strings containing quotes
526                     if( LookAhead() )
527                     {
528                         // Single line string
529                         if( nLineLength >= 2 && pLine[nLineLength - 1] == '"' )
530                         {
531                             m_aData = aLine.subView( 1, nLineLength - 2 );
532                             lcl_DeEscapeQuotesDif(m_aData);
533                             eRet = D_STRING;
534                         }
535                     }
536                     else
537                     {
538                         // Multiline string
539                         m_aData = aLine.subView( 1 );
540                         bool bContinue = true;
541                         while ( bContinue )
542                         {
543                             m_aData.append("\n");
544                             bContinue = !rIn.eof() && ReadNextLine( aLine );
545                             if( bContinue )
546                             {
547                                 nLineLength = aLine.getLength();
548                                 if( nLineLength >= 1 )
549                                 {
550                                     pLine = aLine.getStr();
551                                     bContinue = !LookAhead();
552                                     if( bContinue )
553                                     {
554                                         m_aData.append(aLine);
555                                     }
556                                     else if( pLine[nLineLength - 1] == '"' )
557                                     {
558                                         m_aData.append(aLine.subView(0, nLineLength -1));
559                                         lcl_DeEscapeQuotesDif(m_aData);
560                                         eRet = D_STRING;
561                                     }
562                                 }
563                             }
564                         }
565                     }
566                 }
567             }
568             break;
569     }
570 
571     if( eRet == D_UNKNOWN )
572         ReadNextLine( aLine );
573 
574     if( rIn.eof() )
575         eRet = D_EOD;
576 
577     return eRet;
578 }
579 
ScanIntVal(const sal_Unicode * pStart,sal_uInt32 & rRet)580 const sal_Unicode* DifParser::ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet )
581 {
582     // eat leading whitespace, not specified, but seen in the wild
583     while (*pStart == ' ' || *pStart == '\t')
584         ++pStart;
585 
586     sal_Unicode     cCurrent = *pStart;
587 
588     if( IsNumber( cCurrent ) )
589         rRet = static_cast<sal_uInt32>( cCurrent - '0' );
590     else
591         return nullptr;
592 
593     pStart++;
594     cCurrent = *pStart;
595 
596     while( IsNumber( cCurrent ) && rRet < ( 0xFFFFFFFF / 10 ) )
597     {
598         rRet *= 10;
599         rRet += static_cast<sal_uInt32>( cCurrent - '0' );
600 
601         pStart++;
602         cCurrent = *pStart;
603     }
604 
605     return pStart;
606 }
607 
DifColumn()608 DifColumn::DifColumn ()
609     : mpCurrent(nullptr)
610 {
611 }
612 
SetNumFormat(const ScDocument * pDoc,SCROW nRow,const sal_uInt32 nNumFormat)613 void DifColumn::SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat )
614 {
615     OSL_ENSURE( pDoc->ValidRow(nRow), "*DifColumn::SetNumFormat(): Row too big!" );
616 
617     if( nNumFormat > 0 )
618     {
619         if(mpCurrent)
620         {
621             OSL_ENSURE( nRow > 0,
622                 "*DifColumn::SetNumFormat(): more cannot be zero!" );
623             OSL_ENSURE( nRow > mpCurrent->nEnd,
624                 "*DifColumn::SetNumFormat(): start from scratch?" );
625 
626             if( mpCurrent->nNumFormat == nNumFormat && mpCurrent->nEnd == nRow - 1 )
627                 mpCurrent->nEnd = nRow;
628             else
629                 NewEntry( nRow, nNumFormat );
630         }
631         else
632             NewEntry(nRow,nNumFormat );
633     }
634     else
635         mpCurrent = nullptr;
636 }
637 
NewEntry(const SCROW nPos,const sal_uInt32 nNumFormat)638 void DifColumn::NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat )
639 {
640     maEntries.emplace_back();
641     mpCurrent = &maEntries.back();
642     mpCurrent->nStart = mpCurrent->nEnd = nPos;
643     mpCurrent->nNumFormat = nNumFormat;
644 
645 }
646 
Apply(ScDocument & rDoc,const SCCOL nCol,const SCTAB nTab)647 void DifColumn::Apply( ScDocument& rDoc, const SCCOL nCol, const SCTAB nTab )
648 {
649     ScPatternAttr aAttr( rDoc.GetPool() );
650     SfxItemSet &rItemSet = aAttr.GetItemSet();
651 
652     for (const auto& rEntry : maEntries)
653     {
654         OSL_ENSURE( rEntry.nNumFormat > 0,
655             "+DifColumn::Apply(): Number format must not be 0!" );
656 
657         rItemSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, rEntry.nNumFormat ) );
658 
659         rDoc.ApplyPatternAreaTab( nCol, rEntry.nStart, nCol, rEntry.nEnd, nTab, aAttr );
660 
661         rItemSet.ClearItem();
662     }
663 }
664 
DifAttrCache()665 DifAttrCache::DifAttrCache()
666 {
667 }
668 
~DifAttrCache()669 DifAttrCache::~DifAttrCache()
670 {
671 }
672 
SetNumFormat(const ScDocument * pDoc,const SCCOL nCol,const SCROW nRow,const sal_uInt32 nNumFormat)673 void DifAttrCache::SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat )
674 {
675     OSL_ENSURE( pDoc->ValidCol(nCol), "-DifAttrCache::SetNumFormat(): Col too big!" );
676 
677     if( !maColMap.count(nCol) )
678         maColMap[ nCol ].reset( new DifColumn );
679 
680     maColMap[ nCol ]->SetNumFormat( pDoc, nRow, nNumFormat );
681 }
682 
Apply(ScDocument & rDoc,SCTAB nTab)683 void DifAttrCache::Apply( ScDocument& rDoc, SCTAB nTab )
684 {
685     for( SCCOL nCol : rDoc.GetColumnsRange(nTab, 0, rDoc.MaxCol()) )
686     {
687         if( maColMap.count(nCol) )
688             maColMap[ nCol ]->Apply( rDoc, nCol, nTab );
689     }
690 }
691 
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
693