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 <flat/ETable.hxx>
21 #include <com/sun/star/sdbc/ColumnValue.hpp>
22 #include <com/sun/star/sdbc/DataType.hpp>
23 #include <com/sun/star/sdbc/XRow.hpp>
24 #include <com/sun/star/ucb/XContentAccess.hpp>
25 #include <flat/EConnection.hxx>
26 #include <flat/EColumns.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <rtl/math.hxx>
29 #include <sal/log.hxx>
30 #include <tools/solar.h>
31 #include <tools/urlobj.hxx>
32 #include <cppuhelper/queryinterface.hxx>
33 #include <cppuhelper/typeprovider.hxx>
34 #include <comphelper/numbers.hxx>
35 #include <comphelper/servicehelper.hxx>
36 #include <com/sun/star/util/NumberFormat.hpp>
37 #include <com/sun/star/util/NumberFormatter.hpp>
38 #include <com/sun/star/util/NumberFormatsSupplier.hpp>
39 #include <i18nlangtag/languagetag.hxx>
40 #include <connectivity/dbconversion.hxx>
41 #include <connectivity/sdbcx/VColumn.hxx>
42 #include <file/quotedstring.hxx>
43 #include <file/FDriver.hxx>
44 #include <unotools/syslocale.hxx>
45 #include <unotools/charclass.hxx>
46 
47 using namespace ::comphelper;
48 using namespace connectivity;
49 using namespace connectivity::flat;
50 using namespace connectivity::file;
51 using namespace ::cppu;
52 using namespace ::com::sun::star::uno;
53 using namespace ::com::sun::star::ucb;
54 using namespace ::com::sun::star::beans;
55 using namespace ::com::sun::star::sdbcx;
56 using namespace ::com::sun::star::sdbc;
57 using namespace ::com::sun::star::container;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::util;
60 using std::vector;
61 using std::lower_bound;
62 
63 
fillColumns(const css::lang::Locale & _aLocale)64 void OFlatTable::fillColumns(const css::lang::Locale& _aLocale)
65 {
66     m_bNeedToReadLine = true; // we overwrite m_aCurrentLine, seek the stream, ...
67     m_pFileStream->Seek(0);
68     m_aCurrentLine = QuotedTokenizedString();
69     bool bRead = true;
70 
71     const OFlatConnection* const pConnection = getFlatConnection();
72     const bool bHasHeaderLine = pConnection->isHeaderLine();
73 
74     QuotedTokenizedString aHeaderLine;
75     TRowPositionInFile rowPos(0, 0);
76     sal_Int32 rowNum(0);
77     if ( bHasHeaderLine )
78     {
79         bRead = readLine(&rowPos.second, &rowPos.first, true);
80         if(bRead)
81             aHeaderLine = m_aCurrentLine;
82     }
83     setRowPos(rowNum++, rowPos);
84 
85     // read first row
86     if(bRead)
87     {
88         bRead = readLine(&rowPos.second, &rowPos.first);
89         if(bRead)
90             setRowPos(rowNum++, rowPos);
91     }
92 
93     if ( !bHasHeaderLine || !aHeaderLine.Len())
94     {
95         // use first non-empty row as headerline because we need the number of columns
96         while(bRead && m_aCurrentLine.Len() == 0)
97         {
98             bRead = readLine(&rowPos.second, &rowPos.first);
99             if(bRead)
100                 setRowPos(rowNum++, rowPos);
101         }
102         aHeaderLine = m_aCurrentLine;
103     }
104     // column count
105     const sal_Int32 nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter);
106 
107     if(!m_aColumns.is())
108         m_aColumns = new OSQLColumns();
109     else
110         m_aColumns->clear();
111 
112     m_aTypes.clear();
113     m_aPrecisions.clear();
114     m_aScales.clear();
115     // reserve some space
116     m_aColumns->reserve(nFieldCount+1);
117     m_aTypes.assign(nFieldCount+1,DataType::SQLNULL);
118     m_aPrecisions.assign(nFieldCount+1,-1);
119     m_aScales.assign(nFieldCount+1,-1);
120 
121     const bool bCase = m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers();
122     CharClass aCharClass( pConnection->getDriver()->getComponentContext(), LanguageTag( _aLocale));
123     // read description
124     const sal_Unicode cDecimalDelimiter  = pConnection->getDecimalDelimiter();
125     const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
126     ::comphelper::UStringMixEqual aCase(bCase);
127     vector<OUString> aColumnNames;
128     vector<OUString> aTypeNames;
129     aTypeNames.resize(nFieldCount);
130     const sal_Int32 nMaxRowsToScan = pConnection->getMaxRowsToScan();
131     sal_Int32 nRowCount = 0;
132 
133     do
134     {
135         sal_Int32 nStartPosHeaderLine = 0; // use for efficient way to get the tokens
136         sal_Int32 nStartPosFirstLine = 0; // use for efficient way to get the tokens
137         sal_Int32 nStartPosFirstLine2 = 0;
138         for( sal_Int32 i = 0; i < nFieldCount; i++ )
139         {
140             if ( nRowCount == 0)
141             {
142                 OUString aColumnName;
143                 if ( bHasHeaderLine )
144                 {
145                     aColumnName = aHeaderLine.GetTokenSpecial(nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter);
146                 }
147                 if ( aColumnName.isEmpty() )
148                 {
149                     aColumnName = "C" + OUString::number(i+1);
150                 }
151                 aColumnNames.push_back(aColumnName);
152             }
153             if(bRead)
154             {
155                 impl_fillColumnInfo_nothrow(m_aCurrentLine, nStartPosFirstLine, nStartPosFirstLine2,
156                                             m_aTypes[i], m_aPrecisions[i], m_aScales[i], aTypeNames[i],
157                                             cDecimalDelimiter, cThousandDelimiter, aCharClass);
158             }
159         }
160         ++nRowCount;
161         bRead = readLine(&rowPos.second, &rowPos.first);
162         if(bRead)
163             setRowPos(rowNum++, rowPos);
164     }
165     while(nRowCount < nMaxRowsToScan && bRead);
166 
167     for( sal_Int32 i = 0; i < nFieldCount; i++ )
168     {
169         // check if the columname already exists
170         OUString aAlias(aColumnNames[i]);
171         OSQLColumns::const_iterator aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase);
172         sal_Int32 nExprCnt = 0;
173         while(aFind != m_aColumns->end())
174         {
175             aAlias = aColumnNames[i] + OUString::number(++nExprCnt);
176             aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase);
177         }
178 
179         rtl::Reference<sdbcx::OColumn> pColumn = new sdbcx::OColumn(aAlias,aTypeNames[i],OUString(),OUString(),
180                                                 ColumnValue::NULLABLE,
181                                                 m_aPrecisions[i],
182                                                 m_aScales[i],
183                                                 m_aTypes[i],
184                                                 false,
185                                                 false,
186                                                 false,
187                                                 bCase,
188                                                 m_CatalogName, getSchema(), getName());
189         m_aColumns->push_back(pColumn);
190     }
191 
192     m_pFileStream->Seek(m_aRowPosToFilePos[0].second);
193 }
194 
impl_fillColumnInfo_nothrow(QuotedTokenizedString const & aFirstLine,sal_Int32 & nStartPosFirstLine,sal_Int32 & nStartPosFirstLine2,sal_Int32 & io_nType,sal_Int32 & io_nPrecisions,sal_Int32 & io_nScales,OUString & o_sTypeName,const sal_Unicode cDecimalDelimiter,const sal_Unicode cThousandDelimiter,const CharClass & aCharClass)195 void OFlatTable::impl_fillColumnInfo_nothrow(QuotedTokenizedString const & aFirstLine, sal_Int32& nStartPosFirstLine, sal_Int32& nStartPosFirstLine2,
196                                              sal_Int32& io_nType, sal_Int32& io_nPrecisions, sal_Int32& io_nScales, OUString& o_sTypeName,
197                                              const sal_Unicode cDecimalDelimiter, const sal_Unicode cThousandDelimiter, const CharClass&  aCharClass)
198 {
199     if ( io_nType != DataType::VARCHAR )
200     {
201         bool bNumeric = io_nType == DataType::SQLNULL || io_nType == DataType::DOUBLE || io_nType == DataType::DECIMAL || io_nType == DataType::INTEGER;
202         sal_uLong  nIndex = 0;
203 
204         if ( bNumeric )
205         {
206             // first without fielddelimiter
207             OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
208             if (aField.isEmpty() ||
209                 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
210             {
211                 bNumeric = false;
212                 if ( m_cStringDelimiter != '\0' )
213                     aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
214                 else
215                     nStartPosFirstLine2 = nStartPosFirstLine;
216             }
217             else
218             {
219                 OUString aField2;
220                 if ( m_cStringDelimiter != '\0' )
221                     aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
222                 else
223                     aField2 = aField;
224 
225                 if (aField2.isEmpty())
226                 {
227                     bNumeric = false;
228                 }
229                 else
230                 {
231                     bNumeric = true;
232                     sal_Int32 nDot = 0;
233                     sal_Int32 nDecimalDelCount = 0;
234                     sal_Int32 nSpaceCount = 0;
235                     for( sal_Int32 j = 0; j < aField2.getLength(); j++ )
236                     {
237                         const sal_Unicode c = aField2[j];
238                         if ( j == nSpaceCount && m_cFieldDelimiter != 32 && c == 32 )
239                         {
240                             ++nSpaceCount;
241                             continue;
242                         }
243                         // just digits, decimal- and thousands-delimiter?
244                         if ( ( !cDecimalDelimiter  || c != cDecimalDelimiter )  &&
245                              ( !cThousandDelimiter || c != cThousandDelimiter ) &&
246                             !aCharClass.isDigit(aField2,j)                      &&
247                             ( j != 0 || (c != '+' && c != '-' ) ) )
248                         {
249                             bNumeric = false;
250                             break;
251                         }
252                         if (cDecimalDelimiter && c == cDecimalDelimiter)
253                         {
254                             io_nPrecisions = 15; // we have a decimal value
255                             io_nScales = 2;
256                             ++nDecimalDelCount;
257                         } // if (cDecimalDelimiter && c == cDecimalDelimiter)
258                         if ( c == '.' )
259                             ++nDot;
260                     }
261 
262                     if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number
263                         bNumeric = false;
264                     if (bNumeric && cThousandDelimiter)
265                     {
266                         // Is the delimiter correct?
267                         const OUString aValue = aField2.getToken(0,cDecimalDelimiter);
268                         for( sal_Int32 j = aValue.getLength() - 4; j >= 0; j -= 4)
269                         {
270                             const sal_Unicode c = aValue[j];
271                             // just digits, decimal- and thousands-delimiter?
272                             if (c == cThousandDelimiter && j)
273                                 continue;
274                             else
275                             {
276                                 bNumeric = false;
277                                 break;
278                             }
279                         }
280                     }
281 
282                     // now also check for a date field
283                     if (!bNumeric)
284                     {
285                         try
286                         {
287                             nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
288                         }
289                         catch(Exception&)
290                         {
291                         }
292                     }
293                 }
294             }
295         }
296         else if ( io_nType == DataType::DATE || io_nType == DataType::TIMESTAMP || io_nType == DataType::TIME)
297         {
298             OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
299             if (aField.isEmpty() ||
300                 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
301             {
302             }
303             else
304             {
305                 OUString aField2;
306                 if ( m_cStringDelimiter != '\0' )
307                     aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
308                 else
309                     aField2 = aField;
310                 if (!aField2.isEmpty() )
311                 {
312                     try
313                     {
314                         nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
315                     }
316                     catch(Exception&)
317                     {
318                     }
319                 }
320             }
321         }
322 
323         if (bNumeric)
324         {
325             if (cDecimalDelimiter)
326             {
327                 if(io_nPrecisions)
328                 {
329                     io_nType = DataType::DECIMAL;
330                     o_sTypeName = "DECIMAL";
331                 }
332                 else
333                 {
334                     io_nType = DataType::DOUBLE;
335                     o_sTypeName = "DOUBLE";
336                 }
337             }
338             else
339             {
340                 io_nType = DataType::INTEGER;
341                 io_nPrecisions = 0;
342                 io_nScales = 0;
343             }
344         }
345         else
346         {
347             switch (comphelper::getNumberFormatType(m_xNumberFormatter,nIndex))
348             {
349                 case css::util::NumberFormat::DATE:
350                     io_nType = DataType::DATE;
351                     o_sTypeName = "DATE";
352                     break;
353                 case css::util::NumberFormat::DATETIME:
354                     io_nType = DataType::TIMESTAMP;
355                     o_sTypeName = "TIMESTAMP";
356                     break;
357                 case css::util::NumberFormat::TIME:
358                     io_nType = DataType::TIME;
359                     o_sTypeName = "TIME";
360                     break;
361                 default:
362                     io_nType = DataType::VARCHAR;
363                     io_nPrecisions = 0; // nyi: Data can be longer!
364                     io_nScales = 0;
365                     o_sTypeName = "VARCHAR";
366             };
367         }
368     }
369     else
370     {
371         OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
372         if (aField.isEmpty() ||
373                 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
374         {
375             if ( m_cStringDelimiter != '\0' )
376                 aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
377             else
378                 nStartPosFirstLine2 = nStartPosFirstLine;
379         }
380         else
381         {
382             if ( m_cStringDelimiter != '\0' )
383                 aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
384         }
385     }
386 }
387 
OFlatTable(sdbcx::OCollection * _pTables,OFlatConnection * _pConnection,const OUString & Name,const OUString & Type,const OUString & Description,const OUString & SchemaName,const OUString & CatalogName)388 OFlatTable::OFlatTable(sdbcx::OCollection* _pTables,OFlatConnection* _pConnection,
389                     const OUString& Name,
390                     const OUString& Type,
391                     const OUString& Description ,
392                     const OUString& SchemaName,
393                     const OUString& CatalogName
394                 ) : OFlatTable_BASE(_pTables,_pConnection,Name,
395                                   Type,
396                                   Description,
397                                   SchemaName,
398                                   CatalogName)
399     ,m_nRowPos(0)
400     ,m_nMaxRowCount(0)
401     ,m_cStringDelimiter(_pConnection->getStringDelimiter())
402     ,m_cFieldDelimiter(_pConnection->getFieldDelimiter())
403     ,m_bNeedToReadLine(false)
404 {
405 
406 }
407 
construct()408 void OFlatTable::construct()
409 {
410     SvtSysLocale aLocale;
411     css::lang::Locale aAppLocale(aLocale.GetLanguageTag().getLocale());
412 
413     Reference< XNumberFormatsSupplier > xSupplier = NumberFormatsSupplier::createWithLocale( m_pConnection->getDriver()->getComponentContext(), aAppLocale );
414     m_xNumberFormatter.set( NumberFormatter::create( m_pConnection->getDriver()->getComponentContext()), UNO_QUERY_THROW);
415     m_xNumberFormatter->attachNumberFormatsSupplier(xSupplier);
416     Reference<XPropertySet> xProp = xSupplier->getNumberFormatSettings();
417     xProp->getPropertyValue("NullDate") >>= m_aNullDate;
418 
419     INetURLObject aURL;
420     aURL.SetURL(getEntry());
421 
422     if(aURL.getExtension() != m_pConnection->getExtension())
423         aURL.setExtension(m_pConnection->getExtension());
424 
425     OUString aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
426 
427     m_pFileStream = createStream_simpleError( aFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
428 
429     if(!m_pFileStream)
430         m_pFileStream = createStream_simpleError( aFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
431 
432     if(!m_pFileStream)
433         return;
434 
435     sal_uInt64 const nSize = m_pFileStream->remainingSize();
436 
437     // Buffersize is dependent on the file-size
438     m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 :
439                                 nSize > 100000  ? 16384 :
440                                 nSize > 10000   ? 4096  : 1024);
441 
442     fillColumns(aAppLocale);
443 
444     refreshColumns();
445 }
446 
getEntry() const447 OUString OFlatTable::getEntry() const
448 {
449     OUString sURL;
450     try
451     {
452         Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet();
453         Reference< XRow> xRow(xDir,UNO_QUERY);
454         OUString sName;
455         OUString sExt;
456 
457         INetURLObject aURL;
458         xDir->beforeFirst();
459         while(xDir->next())
460         {
461             sName = xRow->getString(1);
462             aURL.SetSmartProtocol(INetProtocol::File);
463             OUString sUrl = m_pConnection->getURL() + "/" + sName;
464             aURL.SetSmartURL( sUrl );
465 
466             // cut the extension
467             sExt = aURL.getExtension();
468 
469             // name and extension have to coincide
470             if ( m_pConnection->matchesExtension( sExt ) )
471             {
472                 if ( !sExt.isEmpty() )
473                     sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength()+1, OUString());
474                 if ( sName == m_Name )
475                 {
476                     Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
477                     sURL = xContentAccess->queryContentIdentifierString();
478                     break;
479                 }
480             }
481         }
482         xDir->beforeFirst(); // move back to before first record
483     }
484     catch(const Exception&)
485     {
486         OSL_ASSERT(false);
487     }
488     return sURL;
489 }
490 
refreshColumns()491 void OFlatTable::refreshColumns()
492 {
493     ::osl::MutexGuard aGuard( m_aMutex );
494 
495     ::std::vector< OUString> aVector;
496     aVector.reserve(m_aColumns->size());
497 
498     for (auto const& column : *m_aColumns)
499         aVector.push_back(Reference< XNamed>(column,UNO_QUERY_THROW)->getName());
500 
501     if(m_xColumns)
502         m_xColumns->reFill(aVector);
503     else
504         m_xColumns = new OFlatColumns(this,m_aMutex,aVector);
505 }
506 
507 
disposing()508 void SAL_CALL OFlatTable::disposing()
509 {
510     OFileTable::disposing();
511     ::osl::MutexGuard aGuard(m_aMutex);
512     m_aColumns = nullptr;
513 }
514 
getTypes()515 Sequence< Type > SAL_CALL OFlatTable::getTypes(  )
516 {
517     Sequence< Type > aTypes = OTable_TYPEDEF::getTypes();
518     vector<Type> aOwnTypes;
519     aOwnTypes.reserve(aTypes.getLength());
520     const Type* pBegin = aTypes.getConstArray();
521     const Type* pEnd = pBegin + aTypes.getLength();
522     for(;pBegin != pEnd;++pBegin)
523     {
524         if(!(*pBegin == cppu::UnoType<XKeysSupplier>::get()||
525             *pBegin == cppu::UnoType<XRename>::get()||
526             *pBegin == cppu::UnoType<XIndexesSupplier>::get()||
527             *pBegin == cppu::UnoType<XAlterTable>::get()||
528             *pBegin == cppu::UnoType<XDataDescriptorFactory>::get()))
529         {
530             aOwnTypes.push_back(*pBegin);
531         }
532     }
533     return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size());
534 }
535 
536 
queryInterface(const Type & rType)537 Any SAL_CALL OFlatTable::queryInterface( const Type & rType )
538 {
539     if( rType == cppu::UnoType<XKeysSupplier>::get()||
540         rType == cppu::UnoType<XIndexesSupplier>::get()||
541         rType == cppu::UnoType<XRename>::get()||
542         rType == cppu::UnoType<XAlterTable>::get()||
543         rType == cppu::UnoType<XDataDescriptorFactory>::get())
544         return Any();
545 
546     Any aRet = OTable_TYPEDEF::queryInterface(rType);
547     return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this));
548 }
549 
550 
getUnoTunnelId()551 Sequence< sal_Int8 > OFlatTable::getUnoTunnelId()
552 {
553     static ::cppu::OImplementationId implId;
554 
555     return implId.getImplementationId();
556 }
557 
558 // css::lang::XUnoTunnel
559 
getSomething(const Sequence<sal_Int8> & rId)560 sal_Int64 OFlatTable::getSomething( const Sequence< sal_Int8 > & rId )
561 {
562     return (isUnoTunnelId<OFlatTable>(rId))
563                 ? reinterpret_cast< sal_Int64 >( this )
564                 : OFlatTable_BASE::getSomething(rId);
565 }
566 
fetchRow(OValueRefRow & _rRow,const OSQLColumns & _rCols,bool bRetrieveData)567 bool OFlatTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData)
568 {
569     *(*_rRow)[0] = m_nFilePos;
570 
571     if (!bRetrieveData)
572         return true;
573 
574     bool result = false;
575     if ( m_bNeedToReadLine )
576     {
577         m_pFileStream->Seek(m_nFilePos);
578         TRowPositionInFile rowPos(0, 0);
579         if(readLine(&rowPos.second, &rowPos.first))
580         {
581             setRowPos(m_nRowPos, rowPos);
582             m_bNeedToReadLine = false;
583             result = true;
584         }
585         // else let run through so that we set _rRow to all NULL
586     }
587 
588     const OFlatConnection * const pConnection = getFlatConnection();
589     const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
590     const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
591     // Fields:
592     sal_Int32 nStartPos = 0;
593     OSQLColumns::const_iterator aIter = _rCols.begin();
594     OSQLColumns::const_iterator aEnd = _rCols.end();
595     const OValueRefVector::size_type nCount = _rRow->size();
596     for (OValueRefVector::size_type i = 1;
597          aIter != aEnd && i < nCount;
598          ++aIter, i++)
599     {
600         OUString aStr = m_aCurrentLine.GetTokenSpecial(nStartPos,m_cFieldDelimiter,m_cStringDelimiter);
601 
602         if (aStr.isEmpty())
603         {
604             (*_rRow)[i]->setNull();
605         }
606         else
607         {
608             sal_Int32 nType   = m_aTypes[i-1];
609             switch(nType)
610             {
611                 case DataType::TIMESTAMP:
612                 case DataType::DATE:
613                 case DataType::TIME:
614                 {
615                     try
616                     {
617                         double nRes = m_xNumberFormatter->convertStringToNumber(css::util::NumberFormat::ALL,aStr);
618 
619                         switch(nType)
620                         {
621                             case DataType::DATE:
622                                 *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes,m_aNullDate));
623                                 break;
624                             case DataType::TIMESTAMP:
625                                 *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes,m_aNullDate));
626                                 break;
627                             default:
628                                 *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes));
629                         }
630                     }
631                     catch(Exception&)
632                     {
633                         (*_rRow)[i]->setNull();
634                     }
635                 }   break;
636                 case DataType::DOUBLE:
637                 case DataType::INTEGER:
638                 case DataType::DECIMAL:
639                 case DataType::NUMERIC:
640                 {
641 
642                     OUString aStrConverted;
643                     if ( DataType::INTEGER != nType )
644                     {
645                         OSL_ENSURE((cDecimalDelimiter && nType != DataType::INTEGER) ||
646                                    (!cDecimalDelimiter && nType == DataType::INTEGER),
647                                    "Wrong type");
648 
649                         OUStringBuffer aBuf(aStr.getLength());
650                         // convert to Standard-Notation (DecimalPOINT without thousands-comma):
651                         for (sal_Int32 j = 0; j < aStr.getLength(); ++j)
652                         {
653                             const sal_Unicode cChar = aStr[j];
654                             if (cDecimalDelimiter && cChar == cDecimalDelimiter)
655                                 aBuf.append('.');
656                             else if ( cChar == '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
657                                 continue;
658                             else if (cThousandDelimiter && cChar == cThousandDelimiter)
659                             {
660                                 // leave out
661                             }
662                             else
663                                 aBuf.append(cChar);
664                         } // for (j = 0; j < aStr.(); ++j)
665                         aStrConverted = aBuf.makeStringAndClear();
666                     } // if ( DataType::INTEGER != nType )
667                     else
668                     {
669                         if ( cThousandDelimiter )
670                             aStrConverted = aStr.replaceAll(OUStringChar(cThousandDelimiter), "");
671                         else
672                             aStrConverted = aStr;
673                     }
674                     const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',',');
675 
676                     // #99178# OJ
677                     if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType )
678                         *(*_rRow)[i] = OUString(OUString::number(nVal));
679                     else
680                         *(*_rRow)[i] = nVal;
681                 } break;
682 
683                 default:
684                 {
685                     // Copy Value as String in Row-Variable
686                     *(*_rRow)[i] = ORowSetValue(aStr);
687                 }
688                 break;
689             } // switch(nType)
690             (*_rRow)[i]->setTypeKind(nType);
691         }
692     }
693     return result;
694 }
695 
696 
refreshHeader()697 void OFlatTable::refreshHeader()
698 {
699     SAL_INFO( "connectivity.flat", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
700 }
701 
702 
703 namespace
704 {
705     template< typename Tp, typename Te> struct RangeBefore
706     {
operator ()__anon11b63ccd0111::RangeBefore707         bool operator() (const Tp &p, const Te &e)
708         {
709             assert(p.first <= p.second);
710             return p.second <= e;
711         }
712     };
713 }
714 
seekRow(IResultSetHelper::Movement eCursorPosition,sal_Int32 nOffset,sal_Int32 & nCurPos)715 bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
716 {
717     OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!");
718 
719 
720     switch(eCursorPosition)
721     {
722         case IResultSetHelper::FIRST:
723             m_nRowPos = 0;
724             [[fallthrough]];
725         case IResultSetHelper::NEXT:
726             {
727                 assert(m_nRowPos >= 0);
728                 if(m_nMaxRowCount != 0 && m_nRowPos > m_nMaxRowCount)
729                     return false;
730                 ++m_nRowPos;
731                 if(m_aRowPosToFilePos.size() > o3tl::make_unsigned(m_nRowPos))
732                 {
733                     m_bNeedToReadLine = true;
734                     m_nFilePos  = m_aRowPosToFilePos[m_nRowPos].first;
735                     nCurPos     = m_aRowPosToFilePos[m_nRowPos].second;
736                 }
737                 else
738                 {
739                     assert(m_aRowPosToFilePos.size() == static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
740                     const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
741                     // Our ResultSet is allowed to disagree with us only
742                     // on the position of the first line
743                     // (because of the special case of the header...)
744                     assert(m_nRowPos == 1 || nCurPos == lastRowPos.second);
745 
746                     m_nFilePos = lastRowPos.second;
747                     m_pFileStream->Seek(m_nFilePos);
748 
749                     TRowPositionInFile newRowPos;
750                     if(!readLine(&newRowPos.second, &newRowPos.first))
751                     {
752                         m_nMaxRowCount = m_nRowPos - 1;
753                         return false;
754                     }
755 
756                     nCurPos = newRowPos.second;
757                     setRowPos(m_nRowPos, newRowPos);
758                 }
759             }
760 
761             break;
762         case IResultSetHelper::PRIOR:
763             assert(m_nRowPos >= 0);
764 
765             if(m_nRowPos == 0)
766                 return false;
767 
768             --m_nRowPos;
769             {
770                 assert (m_nRowPos >= 0);
771                 assert(m_aRowPosToFilePos.size() >= o3tl::make_unsigned(m_nRowPos));
772                 const TRowPositionInFile &aPositions(m_aRowPosToFilePos[m_nRowPos]);
773                 m_nFilePos = aPositions.first;
774                 nCurPos = aPositions.second;
775                 m_bNeedToReadLine = true;
776             }
777 
778             break;
779         case IResultSetHelper::LAST:
780             if (m_nMaxRowCount == 0)
781             {
782                 while(seekRow(IResultSetHelper::NEXT, 1, nCurPos)) ; // run through after last row
783             }
784             // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
785             return seekRow(IResultSetHelper::ABSOLUTE1, m_nMaxRowCount, nCurPos);
786         case IResultSetHelper::RELATIVE1:
787             {
788                 const sal_Int32 nNewRowPos = m_nRowPos + nOffset;
789                 if (nNewRowPos < 0)
790                     return false;
791                 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
792                 return seekRow(IResultSetHelper::ABSOLUTE1, nNewRowPos, nCurPos);
793             }
794         case IResultSetHelper::ABSOLUTE1:
795             {
796                 if(nOffset < 0)
797                 {
798                     if (m_nMaxRowCount == 0)
799                     {
800                         if (!seekRow(IResultSetHelper::LAST, 0, nCurPos))
801                             return false;
802                     }
803                     // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
804                     nOffset = m_nMaxRowCount + nOffset;
805                 }
806                 if(nOffset < 0)
807                 {
808                     seekRow(IResultSetHelper::ABSOLUTE1, 0, nCurPos);
809                     return false;
810                 }
811                 if(m_nMaxRowCount && nOffset > m_nMaxRowCount)
812                 {
813                     m_nRowPos = m_nMaxRowCount + 1;
814                     const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
815                     m_nFilePos = lastRowPos.second;
816                     nCurPos = lastRowPos.second;
817                     return false;
818                 }
819 
820                 assert(m_nRowPos >=0);
821                 assert(m_aRowPosToFilePos.size() > o3tl::make_unsigned(m_nRowPos));
822                 assert(nOffset >= 0);
823                 if(m_aRowPosToFilePos.size() > o3tl::make_unsigned(nOffset))
824                 {
825                     m_nFilePos  = m_aRowPosToFilePos[nOffset].first;
826                     nCurPos     = m_aRowPosToFilePos[nOffset].second;
827                     m_nRowPos   = nOffset;
828                     m_bNeedToReadLine = true;
829                 }
830                 else
831                 {
832                     assert(m_nRowPos < nOffset);
833                     while(m_nRowPos < nOffset)
834                     {
835                         if(!seekRow(IResultSetHelper::NEXT, 1, nCurPos))
836                             return false;
837                     }
838                     assert(m_nRowPos == nOffset);
839                 }
840             }
841 
842             break;
843         case IResultSetHelper::BOOKMARK:
844             {
845                 vector< TRowPositionInFile >::const_iterator aFind = lower_bound(m_aRowPosToFilePos.begin(),
846                                                                                  m_aRowPosToFilePos.end(),
847                                                                                  nOffset,
848                                                                                  RangeBefore< TRowPositionInFile, sal_Int32 >());
849 
850                 if(aFind == m_aRowPosToFilePos.end() || aFind->first != nOffset)
851                     //invalid bookmark
852                     return false;
853 
854                 m_bNeedToReadLine = true;
855                 m_nFilePos  = aFind->first;
856                 nCurPos     = aFind->second;
857                 m_nRowPos = aFind - m_aRowPosToFilePos.begin();
858                 break;
859             }
860     }
861 
862     return true;
863 }
864 
865 
readLine(sal_Int32 * const pEndPos,sal_Int32 * const pStartPos,const bool nonEmpty)866 bool OFlatTable::readLine(sal_Int32 * const pEndPos, sal_Int32 * const pStartPos, const bool nonEmpty)
867 {
868     const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
869     m_aCurrentLine = QuotedTokenizedString();
870     do
871     {
872         if (pStartPos)
873             *pStartPos = static_cast<sal_Int32>(m_pFileStream->Tell());
874         m_pFileStream->ReadByteStringLine(m_aCurrentLine, nEncoding);
875         if (m_pFileStream->eof())
876             return false;
877 
878         QuotedTokenizedString sLine = m_aCurrentLine; // check if the string continues on next line
879         sal_Int32 nLastOffset = 0;
880         bool isQuoted = false;
881         bool isFieldStarting = true;
882         while (true)
883         {
884             bool wasQuote = false;
885             const sal_Unicode *p = sLine.GetString().getStr() + nLastOffset;
886             while (*p)
887             {
888                 if (isQuoted)
889                 {
890                     if (*p == m_cStringDelimiter)
891                         wasQuote = !wasQuote;
892                     else
893                     {
894                         if (wasQuote)
895                         {
896                             wasQuote = false;
897                             isQuoted = false;
898                             if (*p == m_cFieldDelimiter)
899                                 isFieldStarting = true;
900                         }
901                     }
902                 }
903                 else
904                 {
905                     if (isFieldStarting)
906                     {
907                         isFieldStarting = false;
908                         if (*p == m_cStringDelimiter)
909                             isQuoted = true;
910                         else if (*p == m_cFieldDelimiter)
911                             isFieldStarting = true;
912                     }
913                     else if (*p == m_cFieldDelimiter)
914                         isFieldStarting = true;
915                 }
916                 ++p;
917             }
918 
919             if (wasQuote)
920                 isQuoted = false;
921 
922             if (isQuoted)
923             {
924                 nLastOffset = sLine.Len();
925                 m_pFileStream->ReadByteStringLine(sLine,nEncoding);
926                 if ( !m_pFileStream->eof() )
927                 {
928                     OUString aStr = m_aCurrentLine.GetString() + "\n" + sLine.GetString();
929                     m_aCurrentLine.SetString(aStr);
930                     sLine = m_aCurrentLine;
931                 }
932                 else
933                     break;
934             }
935             else
936                 break;
937         }
938     }
939     while(nonEmpty && m_aCurrentLine.Len() == 0);
940 
941     if(pEndPos)
942         *pEndPos = static_cast<sal_Int32>(m_pFileStream->Tell());
943     return true;
944 }
945 
946 
setRowPos(const vector<TRowPositionInFile>::size_type rowNum,const TRowPositionInFile & rowPos)947 void OFlatTable::setRowPos(const vector<TRowPositionInFile>::size_type rowNum, const TRowPositionInFile &rowPos)
948 {
949     assert(m_aRowPosToFilePos.size() >= rowNum);
950     if(m_aRowPosToFilePos.size() == rowNum)
951         m_aRowPosToFilePos.push_back(rowPos);
952     else
953     {
954         SAL_WARN_IF(m_aRowPosToFilePos[rowNum] != rowPos,
955                     "connectivity.flat",
956                     "Setting position for row " << rowNum << " to (" << rowPos.first << ", " << rowPos.second << "), "
957                     "but already had different position (" << m_aRowPosToFilePos[rowNum].first << ", " << m_aRowPosToFilePos[rowNum].second << ")");
958         m_aRowPosToFilePos[rowNum] = rowPos;
959     }
960 }
961 
962 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
963