build fix
[LibreOffice.git] / connectivity / source / drivers / flat / ETable.cxx
blob54b93c825dee51ac83058ffb9baba4c6b4068546
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ctype.h>
21 #include "flat/ETable.hxx"
22 #include <com/sun/star/sdbc/ColumnValue.hpp>
23 #include <com/sun/star/sdbc/DataType.hpp>
24 #include <com/sun/star/ucb/XContentAccess.hpp>
25 #include <svl/converter.hxx>
26 #include "flat/EConnection.hxx"
27 #include "flat/EColumns.hxx"
28 #include <osl/thread.h>
29 #include <svl/zforlist.hxx>
30 #include <rtl/math.hxx>
31 #include <cppuhelper/queryinterface.hxx>
32 #include <comphelper/extract.hxx>
33 #include <comphelper/numbers.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/sequence.hxx>
36 #include <comphelper/string.hxx>
37 #include <comphelper/types.hxx>
38 #include "flat/EDriver.hxx"
39 #include <com/sun/star/util/NumberFormat.hpp>
40 #include <com/sun/star/util/NumberFormatter.hpp>
41 #include <com/sun/star/util/NumberFormatsSupplier.hpp>
42 #include <unotools/configmgr.hxx>
43 #include <i18nlangtag/languagetag.hxx>
44 #include <connectivity/dbconversion.hxx>
45 #include "file/quotedstring.hxx"
46 #include <unotools/syslocale.hxx>
48 using namespace ::comphelper;
49 using namespace connectivity;
50 using namespace connectivity::flat;
51 using namespace connectivity::file;
52 using namespace ::cppu;
53 using namespace utl;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::ucb;
56 using namespace ::com::sun::star::beans;
57 using namespace ::com::sun::star::sdbcx;
58 using namespace ::com::sun::star::sdbc;
59 using namespace ::com::sun::star::container;
60 using namespace ::com::sun::star::lang;
61 using namespace ::com::sun::star::util;
62 using std::vector;
63 using std::lower_bound;
66 void OFlatTable::fillColumns(const css::lang::Locale& _aLocale)
68 m_bNeedToReadLine = true; // we overwrite m_aCurrentLine, seek the stream, ...
69 m_pFileStream->Seek(0);
70 m_aCurrentLine = QuotedTokenizedString();
71 bool bRead = true;
73 const OFlatConnection* const pConnection = getFlatConnection();
74 const bool bHasHeaderLine = pConnection->isHeaderLine();
76 QuotedTokenizedString aHeaderLine;
77 TRowPositionInFile rowPos(0, 0);
78 sal_Int32 rowNum(0);
79 if ( bHasHeaderLine )
81 bRead = readLine(&rowPos.second, &rowPos.first, true);
82 if(bRead)
83 aHeaderLine = m_aCurrentLine;
85 setRowPos(rowNum++, rowPos);
87 // read first row
88 QuotedTokenizedString aFirstLine;
89 if(bRead)
91 bRead = readLine(&rowPos.second, &rowPos.first);
92 if(bRead)
93 setRowPos(rowNum++, rowPos);
96 if ( !bHasHeaderLine || !aHeaderLine.Len())
98 // use first non-empty row as headerline because we need the number of columns
99 while(bRead && m_aCurrentLine.Len() == 0)
101 bRead = readLine(&rowPos.second, &rowPos.first);
102 if(bRead)
103 setRowPos(rowNum++, rowPos);
105 aHeaderLine = m_aCurrentLine;
107 // column count
108 const sal_Int32 nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter);
110 if(!m_aColumns.is())
111 m_aColumns = new OSQLColumns();
112 else
113 m_aColumns->get().clear();
115 m_aTypes.clear();
116 m_aPrecisions.clear();
117 m_aScales.clear();
118 // reserve some space
119 m_aColumns->get().reserve(nFieldCount+1);
120 m_aTypes.assign(nFieldCount+1,DataType::SQLNULL);
121 m_aPrecisions.assign(nFieldCount+1,-1);
122 m_aScales.assign(nFieldCount+1,-1);
124 const bool bCase = m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers();
125 CharClass aCharClass( pConnection->getDriver()->getComponentContext(), LanguageTag( _aLocale));
126 // read description
127 const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
128 const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
129 ::comphelper::UStringMixEqual aCase(bCase);
130 vector<OUString> aColumnNames;
131 vector<OUString> aTypeNames;
132 aTypeNames.resize(nFieldCount);
133 const sal_Int32 nMaxRowsToScan = pConnection->getMaxRowsToScan();
134 sal_Int32 nRowCount = 0;
138 sal_Int32 nStartPosHeaderLine = 0; // use for efficient way to get the tokens
139 sal_Int32 nStartPosFirstLine = 0; // use for efficient way to get the tokens
140 sal_Int32 nStartPosFirstLine2 = 0;
141 for( sal_Int32 i = 0; i < nFieldCount; i++ )
143 if ( nRowCount == 0)
145 OUString aColumnName;
146 if ( bHasHeaderLine )
148 aColumnName = aHeaderLine.GetTokenSpecial(nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter);
150 if ( aColumnName.isEmpty() )
152 aColumnName = "C" + OUString::number(i+1);
154 aColumnNames.push_back(aColumnName);
156 if(bRead)
158 impl_fillColumnInfo_nothrow(m_aCurrentLine, nStartPosFirstLine, nStartPosFirstLine2,
159 m_aTypes[i], m_aPrecisions[i], m_aScales[i], aTypeNames[i],
160 cDecimalDelimiter, cThousandDelimiter, aCharClass);
163 ++nRowCount;
164 bRead = readLine(&rowPos.second, &rowPos.first);
165 if(bRead)
166 setRowPos(rowNum++, rowPos);
168 while(nRowCount < nMaxRowsToScan && bRead);
170 for( sal_Int32 i = 0; i < nFieldCount; i++ )
172 // check if the columname already exists
173 OUString aAlias(aColumnNames[i]);
174 OSQLColumns::Vector::const_iterator aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
175 sal_Int32 nExprCnt = 0;
176 while(aFind != m_aColumns->get().end())
178 aAlias = aColumnNames[i] + OUString::number(++nExprCnt);
179 aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
182 sdbcx::OColumn* pColumn = new sdbcx::OColumn(aAlias,aTypeNames[i],OUString(),OUString(),
183 ColumnValue::NULLABLE,
184 m_aPrecisions[i],
185 m_aScales[i],
186 m_aTypes[i],
187 false,
188 false,
189 false,
190 bCase,
191 m_CatalogName, getSchema(), getName());
192 Reference< XPropertySet> xCol = pColumn;
193 m_aColumns->get().push_back(xCol);
196 m_pFileStream->Seek(m_aRowPosToFilePos[0].second);
199 void OFlatTable::impl_fillColumnInfo_nothrow(QuotedTokenizedString& aFirstLine, sal_Int32& nStartPosFirstLine, sal_Int32& nStartPosFirstLine2,
200 sal_Int32& io_nType, sal_Int32& io_nPrecisions, sal_Int32& io_nScales, OUString& o_sTypeName,
201 const sal_Unicode cDecimalDelimiter, const sal_Unicode cThousandDelimiter, const CharClass& aCharClass)
203 if ( io_nType != DataType::VARCHAR )
205 bool bNumeric = io_nType == DataType::SQLNULL || io_nType == DataType::DOUBLE || io_nType == DataType::DECIMAL || io_nType == DataType::INTEGER;
206 sal_uLong nIndex = 0;
208 if ( bNumeric )
210 // first without fielddelimiter
211 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
212 if (aField.isEmpty() ||
213 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
215 bNumeric = false;
216 if ( m_cStringDelimiter != '\0' )
217 aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
218 else
219 nStartPosFirstLine2 = nStartPosFirstLine;
221 else
223 OUString aField2;
224 if ( m_cStringDelimiter != '\0' )
225 aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
226 else
227 aField2 = aField;
229 if (aField2.isEmpty())
231 bNumeric = false;
233 else
235 bNumeric = true;
236 sal_Int32 nDot = 0;
237 sal_Int32 nDecimalDelCount = 0;
238 sal_Int32 nSpaceCount = 0;
239 for( sal_Int32 j = 0; j < aField2.getLength(); j++ )
241 const sal_Unicode c = aField2[j];
242 if ( j == nSpaceCount && m_cFieldDelimiter != 32 && c == 32 )
244 ++nSpaceCount;
245 continue;
247 // just digits, decimal- and thousands-delimiter?
248 if ( ( !cDecimalDelimiter || c != cDecimalDelimiter ) &&
249 ( !cThousandDelimiter || c != cThousandDelimiter ) &&
250 !aCharClass.isDigit(aField2,j) &&
251 ( j != 0 || (c != '+' && c != '-' ) ) )
253 bNumeric = false;
254 break;
256 if (cDecimalDelimiter && c == cDecimalDelimiter)
258 io_nPrecisions = 15; // we have an decimal value
259 io_nScales = 2;
260 ++nDecimalDelCount;
261 } // if (cDecimalDelimiter && c == cDecimalDelimiter)
262 if ( c == '.' )
263 ++nDot;
266 if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number
267 bNumeric = false;
268 if (bNumeric && cThousandDelimiter)
270 // Is the delimiter correct?
271 const OUString aValue = aField2.getToken(0,cDecimalDelimiter);
272 for( sal_Int32 j = aValue.getLength() - 4; j >= 0; j -= 4)
274 const sal_Unicode c = aValue[j];
275 // just digits, decimal- and thousands-delimiter?
276 if (c == cThousandDelimiter && j)
277 continue;
278 else
280 bNumeric = false;
281 break;
286 // now also check for a date field
287 if (!bNumeric)
291 nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
293 catch(Exception&)
300 else if ( io_nType == DataType::DATE || io_nType == DataType::TIMESTAMP || io_nType == DataType::TIME)
302 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
303 if (aField.isEmpty() ||
304 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
307 else
309 OUString aField2;
310 if ( m_cStringDelimiter != '\0' )
311 aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
312 else
313 aField2 = aField;
314 if (!aField2.isEmpty() )
318 nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
320 catch(Exception&)
327 if (bNumeric)
329 if (cDecimalDelimiter)
331 if(io_nPrecisions)
333 io_nType = DataType::DECIMAL;
334 o_sTypeName = "DECIMAL";
336 else
338 io_nType = DataType::DOUBLE;
339 o_sTypeName = "DOUBLE";
342 else
344 io_nType = DataType::INTEGER;
345 io_nPrecisions = 0;
346 io_nScales = 0;
349 else
351 switch (comphelper::getNumberFormatType(m_xNumberFormatter,nIndex))
353 case css::util::NumberFormat::DATE:
354 io_nType = DataType::DATE;
355 o_sTypeName = "DATE";
356 break;
357 case css::util::NumberFormat::DATETIME:
358 io_nType = DataType::TIMESTAMP;
359 o_sTypeName = "TIMESTAMP";
360 break;
361 case css::util::NumberFormat::TIME:
362 io_nType = DataType::TIME;
363 o_sTypeName = "TIME";
364 break;
365 default:
366 io_nType = DataType::VARCHAR;
367 io_nPrecisions = 0; // nyi: Data can be longer!
368 io_nScales = 0;
369 o_sTypeName = "VARCHAR";
373 else
375 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
376 if (aField.isEmpty() ||
377 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
379 if ( m_cStringDelimiter != '\0' )
380 aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
381 else
382 nStartPosFirstLine2 = nStartPosFirstLine;
384 else
386 if ( m_cStringDelimiter != '\0' )
387 aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
392 OFlatTable::OFlatTable(sdbcx::OCollection* _pTables,OFlatConnection* _pConnection,
393 const OUString& Name,
394 const OUString& Type,
395 const OUString& Description ,
396 const OUString& SchemaName,
397 const OUString& CatalogName
398 ) : OFlatTable_BASE(_pTables,_pConnection,Name,
399 Type,
400 Description,
401 SchemaName,
402 CatalogName)
403 ,m_nRowPos(0)
404 ,m_nMaxRowCount(0)
405 ,m_cStringDelimiter(_pConnection->getStringDelimiter())
406 ,m_cFieldDelimiter(_pConnection->getFieldDelimiter())
407 ,m_bNeedToReadLine(false)
412 void OFlatTable::construct()
414 SvtSysLocale aLocale;
415 css::lang::Locale aAppLocale(aLocale.GetLanguageTag().getLocale());
417 Reference< XNumberFormatsSupplier > xSupplier = NumberFormatsSupplier::createWithLocale( m_pConnection->getDriver()->getComponentContext(), aAppLocale );
418 m_xNumberFormatter.set( NumberFormatter::create( m_pConnection->getDriver()->getComponentContext()), UNO_QUERY_THROW);
419 m_xNumberFormatter->attachNumberFormatsSupplier(xSupplier);
420 Reference<XPropertySet> xProp(xSupplier->getNumberFormatSettings(),UNO_QUERY);
421 xProp->getPropertyValue("NullDate") >>= m_aNullDate;
423 INetURLObject aURL;
424 aURL.SetURL(getEntry());
426 if(aURL.getExtension() != m_pConnection->getExtension())
427 aURL.setExtension(m_pConnection->getExtension());
429 OUString aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
431 m_pFileStream = createStream_simpleError( aFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
433 if(!m_pFileStream)
434 m_pFileStream = createStream_simpleError( aFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
436 if(m_pFileStream)
438 sal_uInt64 const nSize = m_pFileStream->remainingSize();
440 // Buffersize is dependent on the file-size
441 m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 :
442 nSize > 100000 ? 16384 :
443 nSize > 10000 ? 4096 : 1024);
445 fillColumns(aAppLocale);
447 refreshColumns();
451 OUString OFlatTable::getEntry()
453 OUString sURL;
456 Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet();
457 Reference< XRow> xRow(xDir,UNO_QUERY);
458 OUString sName;
459 OUString sExt;
461 INetURLObject aURL;
462 xDir->beforeFirst();
463 while(xDir->next())
465 sName = xRow->getString(1);
466 aURL.SetSmartProtocol(INetProtocol::File);
467 OUString sUrl = m_pConnection->getURL() + "/" + sName;
468 aURL.SetSmartURL( sUrl );
470 // cut the extension
471 sExt = aURL.getExtension();
473 // name and extension have to coincide
474 if ( m_pConnection->matchesExtension( sExt ) )
476 if ( !sExt.isEmpty() )
477 sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength()+1, OUString());
478 if ( sName == m_Name )
480 Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
481 sURL = xContentAccess->queryContentIdentifierString();
482 break;
486 xDir->beforeFirst(); // move back to before first record
488 catch(const Exception&)
490 OSL_ASSERT(false);
492 return sURL;
495 void OFlatTable::refreshColumns()
497 ::osl::MutexGuard aGuard( m_aMutex );
499 TStringVector aVector;
500 aVector.reserve(m_aColumns->get().size());
502 for(OSQLColumns::Vector::const_iterator aIter = m_aColumns->get().begin();aIter != m_aColumns->get().end();++aIter)
503 aVector.push_back(Reference< XNamed>(*aIter,UNO_QUERY)->getName());
505 if(m_pColumns)
506 m_pColumns->reFill(aVector);
507 else
508 m_pColumns = new OFlatColumns(this,m_aMutex,aVector);
512 void SAL_CALL OFlatTable::disposing()
514 OFileTable::disposing();
515 ::osl::MutexGuard aGuard(m_aMutex);
516 m_aColumns = nullptr;
519 Sequence< Type > SAL_CALL OFlatTable::getTypes( ) throw(RuntimeException, std::exception)
521 Sequence< Type > aTypes = OTable_TYPEDEF::getTypes();
522 vector<Type> aOwnTypes;
523 aOwnTypes.reserve(aTypes.getLength());
524 const Type* pBegin = aTypes.getConstArray();
525 const Type* pEnd = pBegin + aTypes.getLength();
526 for(;pBegin != pEnd;++pBegin)
528 if(!(*pBegin == cppu::UnoType<XKeysSupplier>::get()||
529 *pBegin == cppu::UnoType<XRename>::get()||
530 *pBegin == cppu::UnoType<XIndexesSupplier>::get()||
531 *pBegin == cppu::UnoType<XAlterTable>::get()||
532 *pBegin == cppu::UnoType<XDataDescriptorFactory>::get()))
534 aOwnTypes.push_back(*pBegin);
537 return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size());
541 Any SAL_CALL OFlatTable::queryInterface( const Type & rType ) throw(RuntimeException, std::exception)
543 if( rType == cppu::UnoType<XKeysSupplier>::get()||
544 rType == cppu::UnoType<XIndexesSupplier>::get()||
545 rType == cppu::UnoType<XRename>::get()||
546 rType == cppu::UnoType<XAlterTable>::get()||
547 rType == cppu::UnoType<XDataDescriptorFactory>::get())
548 return Any();
550 Any aRet = OTable_TYPEDEF::queryInterface(rType);
551 return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this));
555 Sequence< sal_Int8 > OFlatTable::getUnoTunnelImplementationId()
557 static ::cppu::OImplementationId * pId = nullptr;
558 if (! pId)
560 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
561 if (! pId)
563 static ::cppu::OImplementationId aId;
564 pId = &aId;
567 return pId->getImplementationId();
570 // css::lang::XUnoTunnel
572 sal_Int64 OFlatTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException, std::exception)
574 return (rId.getLength() == 16 && 0 == memcmp(getUnoTunnelImplementationId().getConstArray(), rId.getConstArray(), 16 ) )
575 ? reinterpret_cast< sal_Int64 >( this )
576 : OFlatTable_BASE::getSomething(rId);
579 bool OFlatTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData)
581 *(_rRow->get())[0] = m_nFilePos;
583 if (!bRetrieveData)
584 return true;
586 bool result = false;
587 if ( m_bNeedToReadLine )
589 m_pFileStream->Seek(m_nFilePos);
590 TRowPositionInFile rowPos(0, 0);
591 if(readLine(&rowPos.second, &rowPos.first))
593 setRowPos(m_nRowPos, rowPos);
594 m_bNeedToReadLine = false;
595 result = true;
597 // else let run through so that we set _rRow to all NULL
600 const OFlatConnection * const pConnection = getFlatConnection();
601 const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
602 const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
603 // Fields:
604 sal_Int32 nStartPos = 0;
605 OSQLColumns::Vector::const_iterator aIter = _rCols.get().begin();
606 OSQLColumns::Vector::const_iterator aEnd = _rCols.get().end();
607 const OValueRefVector::Vector::size_type nCount = _rRow->get().size();
608 for (OValueRefVector::Vector::size_type i = 1;
609 aIter != aEnd && i < nCount;
610 ++aIter, i++)
612 OUString aStr = m_aCurrentLine.GetTokenSpecial(nStartPos,m_cFieldDelimiter,m_cStringDelimiter);
614 if (aStr.isEmpty())
616 (_rRow->get())[i]->setNull();
618 else
620 sal_Int32 nType = m_aTypes[i-1];
621 switch(nType)
623 case DataType::TIMESTAMP:
624 case DataType::DATE:
625 case DataType::TIME:
629 double nRes = m_xNumberFormatter->convertStringToNumber(css::util::NumberFormat::ALL,aStr);
631 switch(nType)
633 case DataType::DATE:
634 *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes,m_aNullDate));
635 break;
636 case DataType::TIMESTAMP:
637 *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes,m_aNullDate));
638 break;
639 default:
640 *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes));
643 catch(Exception&)
645 (_rRow->get())[i]->setNull();
647 } break;
648 case DataType::DOUBLE:
649 case DataType::INTEGER:
650 case DataType::DECIMAL:
651 case DataType::NUMERIC:
654 OUString aStrConverted;
655 if ( DataType::INTEGER != nType )
657 OSL_ENSURE((cDecimalDelimiter && nType != DataType::INTEGER) ||
658 (!cDecimalDelimiter && nType == DataType::INTEGER),
659 "FalscherTyp");
661 OUStringBuffer aBuf(aStr.getLength());
662 // convert to Standard-Notation (DecimalPOINT without thousands-comma):
663 for (sal_Int32 j = 0; j < aStr.getLength(); ++j)
665 const sal_Unicode cChar = aStr[j];
666 if (cDecimalDelimiter && cChar == cDecimalDelimiter)
667 aBuf.append('.');
668 else if ( cChar == '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
669 continue;
670 else if (cThousandDelimiter && cChar == cThousandDelimiter)
672 // leave out
674 else
675 aBuf.append(cChar);
676 } // for (j = 0; j < aStr.getLength(); ++j)
677 aStrConverted = aBuf.makeStringAndClear();
678 } // if ( DataType::INTEGER != nType )
679 else
681 if ( cThousandDelimiter )
682 aStrConverted = aStr.replaceAll(OUStringLiteral1(cThousandDelimiter), "");
683 else
684 aStrConverted = aStr;
686 const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',',');
688 // #99178# OJ
689 if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType )
690 *(_rRow->get())[i] = OUString::number(nVal);
691 else
692 *(_rRow->get())[i] = nVal;
693 } break;
695 default:
697 // Copy Value as String in Row-Variable
698 *(_rRow->get())[i] = ORowSetValue(aStr);
700 break;
701 } // switch(nType)
702 (_rRow->get())[i]->setTypeKind(nType);
705 return result;
709 void OFlatTable::refreshHeader()
711 SAL_INFO( "connectivity.drivers", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
715 namespace
717 template< typename Tp, typename Te> struct RangeBefore
719 bool operator() (const Tp &p, const Te &e)
721 assert(p.first <= p.second);
722 return p.second <= e;
727 bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
729 OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!");
732 switch(eCursorPosition)
734 case IResultSetHelper::FIRST:
735 m_nRowPos = 0;
736 SAL_FALLTHROUGH;
737 case IResultSetHelper::NEXT:
739 assert(m_nRowPos >= 0);
740 if(m_nMaxRowCount != 0 && m_nRowPos > m_nMaxRowCount)
741 return false;
742 ++m_nRowPos;
743 if(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos))
745 m_bNeedToReadLine = true;
746 m_nFilePos = m_aRowPosToFilePos[m_nRowPos].first;
747 nCurPos = m_aRowPosToFilePos[m_nRowPos].second;
749 else
751 assert(m_aRowPosToFilePos.size() == static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
752 const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
753 // Our ResultSet is allowed to disagree with us only
754 // on the position of the first line
755 // (because of the special case of the header...)
756 assert(m_nRowPos == 1 || nCurPos == lastRowPos.second);
758 m_nFilePos = lastRowPos.second;
759 m_pFileStream->Seek(m_nFilePos);
761 TRowPositionInFile newRowPos;
762 if(!readLine(&newRowPos.second, &newRowPos.first))
764 m_nMaxRowCount = m_nRowPos - 1;
765 return false;
768 nCurPos = newRowPos.second;
769 setRowPos(m_nRowPos, newRowPos);
773 break;
774 case IResultSetHelper::PRIOR:
775 assert(m_nRowPos >= 0);
777 if(m_nRowPos == 0)
778 return false;
780 --m_nRowPos;
782 assert (m_nRowPos >= 0);
783 assert(m_aRowPosToFilePos.size() >= static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
784 const TRowPositionInFile &aPositions(m_aRowPosToFilePos[m_nRowPos]);
785 m_nFilePos = aPositions.first;
786 nCurPos = aPositions.second;
787 m_bNeedToReadLine = true;
790 break;
791 case IResultSetHelper::LAST:
792 if (m_nMaxRowCount == 0)
794 while(seekRow(IResultSetHelper::NEXT, 1, nCurPos)) ; // run through after last row
796 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
797 return seekRow(IResultSetHelper::ABSOLUTE1, m_nMaxRowCount, nCurPos);
798 break;
799 case IResultSetHelper::RELATIVE1:
801 const sal_Int32 nNewRowPos = m_nRowPos + nOffset;
802 if (nNewRowPos < 0)
803 return false;
804 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
805 return seekRow(IResultSetHelper::ABSOLUTE1, nNewRowPos, nCurPos);
807 case IResultSetHelper::ABSOLUTE1:
809 if(nOffset < 0)
811 if (m_nMaxRowCount == 0)
813 if (!seekRow(IResultSetHelper::LAST, 0, nCurPos))
814 return false;
816 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
817 nOffset = m_nMaxRowCount + nOffset;
819 if(nOffset < 0)
821 seekRow(IResultSetHelper::ABSOLUTE1, 0, nCurPos);
822 return false;
824 if(m_nMaxRowCount && nOffset > m_nMaxRowCount)
826 m_nRowPos = m_nMaxRowCount + 1;
827 const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
828 m_nFilePos = lastRowPos.second;
829 nCurPos = lastRowPos.second;
830 return false;
833 assert(m_nRowPos >=0);
834 assert(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
835 assert(nOffset >= 0);
836 if(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(nOffset))
838 m_nFilePos = m_aRowPosToFilePos[nOffset].first;
839 nCurPos = m_aRowPosToFilePos[nOffset].second;
840 m_nRowPos = nOffset;
841 m_bNeedToReadLine = true;
843 else
845 assert(m_nRowPos < nOffset);
846 while(m_nRowPos < nOffset)
848 if(!seekRow(IResultSetHelper::NEXT, 1, nCurPos))
849 return false;
851 assert(m_nRowPos == nOffset);
855 break;
856 case IResultSetHelper::BOOKMARK:
858 vector< TRowPositionInFile >::const_iterator aFind = lower_bound(m_aRowPosToFilePos.begin(),
859 m_aRowPosToFilePos.end(),
860 nOffset,
861 RangeBefore< TRowPositionInFile, sal_Int32 >());
863 if(aFind == m_aRowPosToFilePos.end() || aFind->first != nOffset)
864 //invalid bookmark
865 return false;
867 m_bNeedToReadLine = true;
868 m_nFilePos = aFind->first;
869 nCurPos = aFind->second;
870 m_nRowPos = aFind - m_aRowPosToFilePos.begin();
871 break;
875 return true;
879 bool OFlatTable::readLine(sal_Int32 * const pEndPos, sal_Int32 * const pStartPos, const bool nonEmpty)
881 const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
882 m_aCurrentLine = QuotedTokenizedString();
885 if (pStartPos)
886 *pStartPos = (sal_Int32)m_pFileStream->Tell();
887 m_pFileStream->ReadByteStringLine(m_aCurrentLine, nEncoding);
888 if (m_pFileStream->IsEof())
889 return false;
891 QuotedTokenizedString sLine = m_aCurrentLine; // check if the string continues on next line
892 sal_Int32 nLastOffset = 0;
893 bool isQuoted = false;
894 bool isFieldStarting = true;
895 while (true)
897 bool wasQuote = false;
898 const sal_Unicode *p = sLine.GetString().getStr() + nLastOffset;
899 while (*p)
901 if (isQuoted)
903 if (*p == m_cStringDelimiter)
904 wasQuote = !wasQuote;
905 else
907 if (wasQuote)
909 wasQuote = false;
910 isQuoted = false;
911 if (*p == m_cFieldDelimiter)
912 isFieldStarting = true;
916 else
918 if (isFieldStarting)
920 isFieldStarting = false;
921 if (*p == m_cStringDelimiter)
922 isQuoted = true;
923 else if (*p == m_cFieldDelimiter)
924 isFieldStarting = true;
926 else if (*p == m_cFieldDelimiter)
927 isFieldStarting = true;
929 ++p;
932 if (wasQuote)
933 isQuoted = false;
935 if (isQuoted)
937 nLastOffset = sLine.Len();
938 m_pFileStream->ReadByteStringLine(sLine,nEncoding);
939 if ( !m_pFileStream->IsEof() )
941 OUString aStr = m_aCurrentLine.GetString() + "\n" + sLine.GetString();
942 m_aCurrentLine.SetString(aStr);
943 sLine = m_aCurrentLine;
945 else
946 break;
948 else
949 break;
952 while(nonEmpty && m_aCurrentLine.Len() == 0);
954 if(pEndPos)
955 *pEndPos = (sal_Int32)m_pFileStream->Tell();
956 return true;
960 void OFlatTable::setRowPos(const vector<TRowPositionInFile>::size_type rowNum, const TRowPositionInFile &rowPos)
962 assert(m_aRowPosToFilePos.size() >= rowNum);
963 if(m_aRowPosToFilePos.size() == rowNum)
964 m_aRowPosToFilePos.push_back(rowPos);
965 else
967 SAL_WARN_IF(m_aRowPosToFilePos[rowNum] != rowPos,
968 "connectivity.flat",
969 "Setting position for row " << rowNum << " to (" << rowPos.first << ", " << rowPos.second << "), "
970 "but already had different position (" << m_aRowPosToFilePos[rowNum].first << ", " << m_aRowPosToFilePos[rowNum].second << ")");
971 m_aRowPosToFilePos[rowNum] = rowPos;
975 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */