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