1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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
;
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
;
63 using std::lower_bound
;
66 void OFlatTable::fillColumns(const ::com::sun::star::lang::Locale
& _aLocale
)
68 m_bNeedToReadLine
= true; // we overwrite m_aCurrentLine, seek the stream, ...
69 m_pFileStream
->Seek(0);
70 m_aCurrentLine
= QuotedTokenizedString();
73 const OFlatConnection
* const pConnection
= getFlatConnection();
74 const bool bHasHeaderLine
= pConnection
->isHeaderLine();
76 QuotedTokenizedString aHeaderLine
;
77 TRowPositionInFile
rowPos(0, 0);
81 bRead
= readLine(&rowPos
.second
, &rowPos
.first
, true);
83 aHeaderLine
= m_aCurrentLine
;
85 setRowPos(rowNum
++, rowPos
);
88 QuotedTokenizedString aFirstLine
;
91 bRead
= readLine(&rowPos
.second
, &rowPos
.first
, false);
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
, false);
103 setRowPos(rowNum
++, rowPos
);
105 aHeaderLine
= m_aCurrentLine
;
108 const sal_Int32 nFieldCount
= aHeaderLine
.GetTokenCount(m_cFieldDelimiter
,m_cStringDelimiter
);
111 m_aColumns
= new OSQLColumns();
113 m_aColumns
->get().clear();
116 m_aPrecisions
.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
));
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
> m_aTypeNames
;
132 m_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
++ )
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
);
158 impl_fillColumnInfo_nothrow(m_aCurrentLine
, nStartPosFirstLine
, nStartPosFirstLine2
,
159 m_aTypes
[i
], m_aPrecisions
[i
], m_aScales
[i
], m_aTypeNames
[i
],
160 cDecimalDelimiter
, cThousandDelimiter
, aCharClass
);
164 bRead
= readLine(&rowPos
.second
, &rowPos
.first
, false);
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
,m_aTypeNames
[i
],OUString(),OUString(),
183 ColumnValue::NULLABLE
,
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;
210 // first without fielddelimiter
211 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
,'\0');
212 if (aField
.isEmpty() ||
213 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
216 if ( m_cStringDelimiter
!= '\0' )
217 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
219 nStartPosFirstLine2
= nStartPosFirstLine
;
224 if ( m_cStringDelimiter
!= '\0' )
225 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
229 if (aField2
.isEmpty())
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 )
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
!= '-' ) ) )
256 if (cDecimalDelimiter
&& c
== cDecimalDelimiter
)
258 io_nPrecisions
= 15; // we have an decimal value
261 } // if (cDecimalDelimiter && c == cDecimalDelimiter)
266 if (nDecimalDelCount
> 1 || nDot
> 1 ) // if there is more than one dot it isn't a number
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
)
286 // now also check for a date field
291 nIndex
= m_xNumberFormatter
->detectNumberFormat(::com::sun::star::util::NumberFormat::ALL
,aField2
);
300 else if ( io_nType
== DataType::DATE
|| io_nType
== DataType::TIMESTAMP
|| io_nType
== DataType::TIME
)
302 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
,'\0');
303 if (aField
.isEmpty() ||
304 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
310 if ( m_cStringDelimiter
!= '\0' )
311 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
314 if (!aField2
.isEmpty() )
318 nIndex
= m_xNumberFormatter
->detectNumberFormat(::com::sun::star::util::NumberFormat::ALL
,aField2
);
327 sal_Int32 nFlags
= 0;
330 if (cDecimalDelimiter
)
334 io_nType
= DataType::DECIMAL
;
335 o_sTypeName
= "DECIMAL";
339 io_nType
= DataType::DOUBLE
;
340 o_sTypeName
= "DOUBLE";
345 io_nType
= DataType::INTEGER
;
349 nFlags
= ColumnSearch::BASIC
;
353 switch (comphelper::getNumberFormatType(m_xNumberFormatter
,nIndex
))
355 case css::util::NumberFormat::DATE
:
356 io_nType
= DataType::DATE
;
357 o_sTypeName
= "DATE";
359 case css::util::NumberFormat::DATETIME
:
360 io_nType
= DataType::TIMESTAMP
;
361 o_sTypeName
= "TIMESTAMP";
363 case css::util::NumberFormat::TIME
:
364 io_nType
= DataType::TIME
;
365 o_sTypeName
= "TIME";
368 io_nType
= DataType::VARCHAR
;
369 io_nPrecisions
= 0; // nyi: Data can be longer!
371 o_sTypeName
= "VARCHAR";
373 nFlags
|= ColumnSearch::CHAR
;
378 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
,'\0');
379 if (aField
.isEmpty() ||
380 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
382 if ( m_cStringDelimiter
!= '\0' )
383 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
385 nStartPosFirstLine2
= nStartPosFirstLine
;
389 if ( m_cStringDelimiter
!= '\0' )
390 aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
395 OFlatTable::OFlatTable(sdbcx::OCollection
* _pTables
,OFlatConnection
* _pConnection
,
396 const OUString
& _Name
,
397 const OUString
& _Type
,
398 const OUString
& _Description
,
399 const OUString
& _SchemaName
,
400 const OUString
& _CatalogName
401 ) : OFlatTable_BASE(_pTables
,_pConnection
,_Name
,
408 ,m_cStringDelimiter(_pConnection
->getStringDelimiter())
409 ,m_cFieldDelimiter(_pConnection
->getFieldDelimiter())
410 ,m_bNeedToReadLine(false)
415 void OFlatTable::construct()
417 SvtSysLocale aLocale
;
418 ::com::sun::star::lang::Locale
aAppLocale(aLocale
.GetLanguageTag().getLocale());
420 Reference
< XNumberFormatsSupplier
> xSupplier
= NumberFormatsSupplier::createWithLocale( m_pConnection
->getDriver()->getComponentContext(), aAppLocale
);
421 m_xNumberFormatter
.set( NumberFormatter::create( m_pConnection
->getDriver()->getComponentContext()), UNO_QUERY_THROW
);
422 m_xNumberFormatter
->attachNumberFormatsSupplier(xSupplier
);
423 Reference
<XPropertySet
> xProp(xSupplier
->getNumberFormatSettings(),UNO_QUERY
);
424 xProp
->getPropertyValue("NullDate") >>= m_aNullDate
;
427 aURL
.SetURL(getEntry());
429 if(aURL
.getExtension() != OUString(m_pConnection
->getExtension()))
430 aURL
.setExtension(m_pConnection
->getExtension());
432 OUString aFileName
= aURL
.GetMainURL(INetURLObject::NO_DECODE
);
434 m_pFileStream
= createStream_simpleError( aFileName
, STREAM_READWRITE
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYWRITE
);
437 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READ
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYNONE
);
441 sal_uInt64
const nSize
= m_pFileStream
->remainingSize();
443 // Buffersize is dependent on the file-size
444 m_pFileStream
->SetBufferSize(nSize
> 1000000 ? 32768 :
445 nSize
> 100000 ? 16384 :
446 nSize
> 10000 ? 4096 : 1024);
448 fillColumns(aAppLocale
);
454 OUString
OFlatTable::getEntry()
459 Reference
< XResultSet
> xDir
= m_pConnection
->getDir()->getStaticResultSet();
460 Reference
< XRow
> xRow(xDir
,UNO_QUERY
);
466 static const char s_sSeparator
[] = "/";
469 sName
= xRow
->getString(1);
470 aURL
.SetSmartProtocol(INetProtocol::File
);
471 OUString sUrl
= m_pConnection
->getURL() + s_sSeparator
+ sName
;
472 aURL
.SetSmartURL( sUrl
);
475 sExt
= aURL
.getExtension();
477 // name and extension have to coincide
478 if ( m_pConnection
->matchesExtension( sExt
) )
480 if ( !sExt
.isEmpty() )
481 sName
= sName
.replaceAt(sName
.getLength() - (sExt
.getLength() + 1), sExt
.getLength()+1, OUString());
482 if ( sName
== m_Name
)
484 Reference
< XContentAccess
> xContentAccess( xDir
, UNO_QUERY
);
485 sURL
= xContentAccess
->queryContentIdentifierString();
490 xDir
->beforeFirst(); // move back to before first record
492 catch(const Exception
&)
499 void OFlatTable::refreshColumns()
501 ::osl::MutexGuard
aGuard( m_aMutex
);
503 TStringVector aVector
;
504 aVector
.reserve(m_aColumns
->get().size());
506 for(OSQLColumns::Vector::const_iterator aIter
= m_aColumns
->get().begin();aIter
!= m_aColumns
->get().end();++aIter
)
507 aVector
.push_back(Reference
< XNamed
>(*aIter
,UNO_QUERY
)->getName());
510 m_pColumns
->reFill(aVector
);
512 m_pColumns
= new OFlatColumns(this,m_aMutex
,aVector
);
516 void SAL_CALL
OFlatTable::disposing()
518 OFileTable::disposing();
519 ::osl::MutexGuard
aGuard(m_aMutex
);
523 Sequence
< Type
> SAL_CALL
OFlatTable::getTypes( ) throw(RuntimeException
, std::exception
)
525 Sequence
< Type
> aTypes
= OTable_TYPEDEF::getTypes();
526 vector
<Type
> aOwnTypes
;
527 aOwnTypes
.reserve(aTypes
.getLength());
528 const Type
* pBegin
= aTypes
.getConstArray();
529 const Type
* pEnd
= pBegin
+ aTypes
.getLength();
530 for(;pBegin
!= pEnd
;++pBegin
)
532 if(!(*pBegin
== cppu::UnoType
<XKeysSupplier
>::get()||
533 *pBegin
== cppu::UnoType
<XRename
>::get()||
534 *pBegin
== cppu::UnoType
<XIndexesSupplier
>::get()||
535 *pBegin
== cppu::UnoType
<XAlterTable
>::get()||
536 *pBegin
== cppu::UnoType
<XDataDescriptorFactory
>::get()))
538 aOwnTypes
.push_back(*pBegin
);
541 return Sequence
< Type
>(aOwnTypes
.data(), aOwnTypes
.size());
545 Any SAL_CALL
OFlatTable::queryInterface( const Type
& rType
) throw(RuntimeException
, std::exception
)
547 if( rType
== cppu::UnoType
<XKeysSupplier
>::get()||
548 rType
== cppu::UnoType
<XIndexesSupplier
>::get()||
549 rType
== cppu::UnoType
<XRename
>::get()||
550 rType
== cppu::UnoType
<XAlterTable
>::get()||
551 rType
== cppu::UnoType
<XDataDescriptorFactory
>::get())
554 Any aRet
= OTable_TYPEDEF::queryInterface(rType
);
555 return aRet
.hasValue() ? aRet
: ::cppu::queryInterface(rType
,static_cast< ::com::sun::star::lang::XUnoTunnel
*> (this));
559 Sequence
< sal_Int8
> OFlatTable::getUnoTunnelImplementationId()
561 static ::cppu::OImplementationId
* pId
= 0;
564 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
567 static ::cppu::OImplementationId aId
;
571 return pId
->getImplementationId();
574 // com::sun::star::lang::XUnoTunnel
576 sal_Int64
OFlatTable::getSomething( const Sequence
< sal_Int8
> & rId
) throw (RuntimeException
, std::exception
)
578 return (rId
.getLength() == 16 && 0 == memcmp(getUnoTunnelImplementationId().getConstArray(), rId
.getConstArray(), 16 ) )
579 ? reinterpret_cast< sal_Int64
>( this )
580 : OFlatTable_BASE::getSomething(rId
);
583 bool OFlatTable::fetchRow(OValueRefRow
& _rRow
, const OSQLColumns
& _rCols
, bool bIsTable
, bool bRetrieveData
)
585 *(_rRow
->get())[0] = m_nFilePos
;
591 if ( m_bNeedToReadLine
)
593 m_pFileStream
->Seek(m_nFilePos
);
594 TRowPositionInFile
rowPos(0, 0);
595 if(readLine(&rowPos
.second
, &rowPos
.first
))
597 setRowPos(m_nRowPos
, rowPos
);
598 m_bNeedToReadLine
= false;
601 // else let run through so that we set _rRow to all NULL
604 const OFlatConnection
* const pConnection
= getFlatConnection();
605 const sal_Unicode cDecimalDelimiter
= pConnection
->getDecimalDelimiter();
606 const sal_Unicode cThousandDelimiter
= pConnection
->getThousandDelimiter();
608 sal_Int32 nStartPos
= 0;
609 OSQLColumns::Vector::const_iterator aIter
= _rCols
.get().begin();
610 OSQLColumns::Vector::const_iterator aEnd
= _rCols
.get().end();
611 const OValueRefVector::Vector::size_type nCount
= _rRow
->get().size();
612 for (OValueRefVector::Vector::size_type i
= 1;
613 aIter
!= aEnd
&& i
< nCount
;
616 OUString aStr
= m_aCurrentLine
.GetTokenSpecial(nStartPos
,m_cFieldDelimiter
,m_cStringDelimiter
);
620 (_rRow
->get())[i
]->setNull();
624 // lengths depending on data-type:
629 nLen
= m_aPrecisions
[i
-1];
630 nType
= m_aTypes
[i
-1];
634 Reference
< XPropertySet
> xColumn
= *aIter
;
635 xColumn
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION
)) >>= nLen
;
636 xColumn
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE
)) >>= nType
;
640 case DataType::TIMESTAMP
:
646 double nRes
= m_xNumberFormatter
->convertStringToNumber(::com::sun::star::util::NumberFormat::ALL
,aStr
);
651 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes
,m_aNullDate
));
653 case DataType::TIMESTAMP
:
654 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes
,m_aNullDate
));
657 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes
));
662 (_rRow
->get())[i
]->setNull();
665 case DataType::DOUBLE
:
666 case DataType::INTEGER
:
667 case DataType::DECIMAL
:
668 case DataType::NUMERIC
:
671 OUString aStrConverted
;
672 if ( DataType::INTEGER
!= nType
)
674 OSL_ENSURE((cDecimalDelimiter
&& nType
!= DataType::INTEGER
) ||
675 (!cDecimalDelimiter
&& nType
== DataType::INTEGER
),
678 OUStringBuffer
aBuf(aStr
.getLength());
679 // convert to Standard-Notation (DecimalPOINT without thousands-comma):
680 for (sal_Int32 j
= 0; j
< aStr
.getLength(); ++j
)
682 const sal_Unicode cChar
= aStr
[j
];
683 if (cDecimalDelimiter
&& cChar
== cDecimalDelimiter
)
685 else if ( cChar
== '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
687 else if (cThousandDelimiter
&& cChar
== cThousandDelimiter
)
693 } // for (j = 0; j < aStr.getLength(); ++j)
694 aStrConverted
= aBuf
.makeStringAndClear();
695 } // if ( DataType::INTEGER != nType )
698 if ( cThousandDelimiter
)
699 aStrConverted
= comphelper::string::remove(aStr
, cThousandDelimiter
);
701 aStrConverted
= aStr
;
703 const double nVal
= ::rtl::math::stringToDouble(aStrConverted
,'.',',',NULL
,NULL
);
706 if ( DataType::DECIMAL
== nType
|| DataType::NUMERIC
== nType
)
707 *(_rRow
->get())[i
] = OUString::number(nVal
);
709 *(_rRow
->get())[i
] = nVal
;
714 // Copy Value as String in Row-Variable
715 *(_rRow
->get())[i
] = ORowSetValue(aStr
);
719 (_rRow
->get())[i
]->setTypeKind(nType
);
726 void OFlatTable::refreshHeader()
728 SAL_INFO( "connectivity.drivers", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
734 template< typename Tp
, typename Te
> struct RangeBefore
736 bool operator() (const Tp
&p
, const Te
&e
)
738 assert(p
.first
<= p
.second
);
739 return p
.second
<= e
;
744 bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition
, sal_Int32 nOffset
, sal_Int32
& nCurPos
)
746 OSL_ENSURE(m_pFileStream
,"OFlatTable::seekRow: FileStream is NULL!");
749 switch(eCursorPosition
)
751 case IResultSetHelper::FIRST
:
754 case IResultSetHelper::NEXT
:
756 assert(m_nRowPos
>= 0);
757 if(m_nMaxRowCount
!= 0 && m_nRowPos
> m_nMaxRowCount
)
760 if(m_aRowPosToFilePos
.size() > static_cast< vector
< TRowPositionInFile
>::size_type
>(m_nRowPos
))
762 m_bNeedToReadLine
= true;
763 m_nFilePos
= m_aRowPosToFilePos
[m_nRowPos
].first
;
764 nCurPos
= m_aRowPosToFilePos
[m_nRowPos
].second
;
768 assert(m_aRowPosToFilePos
.size() == static_cast< vector
< TRowPositionInFile
>::size_type
>(m_nRowPos
));
769 const TRowPositionInFile
&lastRowPos(m_aRowPosToFilePos
.back());
770 // Our ResultSet is allowed to disagree with us only
771 // on the position of the first line
772 // (because of the special case of the header...)
773 assert(m_nRowPos
== 1 || nCurPos
== lastRowPos
.second
);
775 m_nFilePos
= lastRowPos
.second
;
776 m_pFileStream
->Seek(m_nFilePos
);
778 TRowPositionInFile newRowPos
;
779 if(!readLine(&newRowPos
.second
, &newRowPos
.first
, false))
781 m_nMaxRowCount
= m_nRowPos
- 1;
785 nCurPos
= newRowPos
.second
;
786 setRowPos(m_nRowPos
, newRowPos
);
791 case IResultSetHelper::PRIOR
:
792 assert(m_nRowPos
>= 0);
799 assert (m_nRowPos
>= 0);
800 assert(m_aRowPosToFilePos
.size() >= static_cast< vector
< TRowPositionInFile
>::size_type
>(m_nRowPos
));
801 const TRowPositionInFile
&aPositions(m_aRowPosToFilePos
[m_nRowPos
]);
802 m_nFilePos
= aPositions
.first
;
803 nCurPos
= aPositions
.second
;
804 m_bNeedToReadLine
= true;
808 case IResultSetHelper::LAST
:
809 if (m_nMaxRowCount
== 0)
811 while(seekRow(IResultSetHelper::NEXT
, 1, nCurPos
)) ; // run through after last row
813 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
814 return seekRow(IResultSetHelper::ABSOLUTE1
, m_nMaxRowCount
, nCurPos
);
816 case IResultSetHelper::RELATIVE1
:
818 const sal_Int32 nNewRowPos
= m_nRowPos
+ nOffset
;
821 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
822 return seekRow(IResultSetHelper::ABSOLUTE1
, nNewRowPos
, nCurPos
);
824 case IResultSetHelper::ABSOLUTE1
:
828 if (m_nMaxRowCount
== 0)
830 if (!seekRow(IResultSetHelper::LAST
, 0, nCurPos
))
833 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
834 nOffset
= m_nMaxRowCount
+ nOffset
;
838 seekRow(IResultSetHelper::ABSOLUTE1
, 0, nCurPos
);
841 if(m_nMaxRowCount
&& nOffset
> m_nMaxRowCount
)
843 m_nRowPos
= m_nMaxRowCount
+ 1;
844 const TRowPositionInFile
&lastRowPos(m_aRowPosToFilePos
.back());
845 m_nFilePos
= lastRowPos
.second
;
846 nCurPos
= lastRowPos
.second
;
850 assert(m_nRowPos
>=0);
851 assert(m_aRowPosToFilePos
.size() > static_cast< vector
< TRowPositionInFile
>::size_type
>(m_nRowPos
));
852 assert(nOffset
>= 0);
853 if(m_aRowPosToFilePos
.size() > static_cast< vector
< TRowPositionInFile
>::size_type
>(nOffset
))
855 m_nFilePos
= m_aRowPosToFilePos
[nOffset
].first
;
856 nCurPos
= m_aRowPosToFilePos
[nOffset
].second
;
858 m_bNeedToReadLine
= true;
862 assert(m_nRowPos
< nOffset
);
863 while(m_nRowPos
< nOffset
)
865 if(!seekRow(IResultSetHelper::NEXT
, 1, nCurPos
))
868 assert(m_nRowPos
== nOffset
);
873 case IResultSetHelper::BOOKMARK
:
875 vector
< TRowPositionInFile
>::const_iterator aFind
= lower_bound(m_aRowPosToFilePos
.begin(),
876 m_aRowPosToFilePos
.end(),
878 RangeBefore
< TRowPositionInFile
, sal_Int32
>());
880 if(aFind
== m_aRowPosToFilePos
.end() || aFind
->first
!= nOffset
)
884 m_bNeedToReadLine
= true;
885 m_nFilePos
= aFind
->first
;
886 nCurPos
= aFind
->second
;
887 m_nRowPos
= aFind
- m_aRowPosToFilePos
.begin();
896 bool OFlatTable::readLine(sal_Int32
* const pEndPos
, sal_Int32
* const pStartPos
, const bool nonEmpty
)
898 const rtl_TextEncoding nEncoding
= m_pConnection
->getTextEncoding();
899 m_aCurrentLine
= QuotedTokenizedString();
903 *pStartPos
= (sal_Int32
)m_pFileStream
->Tell();
904 m_pFileStream
->ReadByteStringLine(m_aCurrentLine
, nEncoding
);
905 if (m_pFileStream
->IsEof())
908 QuotedTokenizedString sLine
= m_aCurrentLine
; // check if the string continues on next line
909 while( (comphelper::string::getTokenCount(sLine
.GetString(), m_cStringDelimiter
) % 2) != 1 )
911 m_pFileStream
->ReadByteStringLine(sLine
,nEncoding
);
912 if ( !m_pFileStream
->IsEof() )
914 OUString aStr
= m_aCurrentLine
.GetString() + "\n" + sLine
.GetString();
915 m_aCurrentLine
.SetString(aStr
);
916 sLine
= m_aCurrentLine
;
922 while(nonEmpty
&& m_aCurrentLine
.Len() == 0);
925 *pEndPos
= (sal_Int32
)m_pFileStream
->Tell();
930 void OFlatTable::setRowPos(const vector
<TRowPositionInFile
>::size_type rowNum
, const TRowPositionInFile
&rowPos
)
932 assert(m_aRowPosToFilePos
.size() >= rowNum
);
933 if(m_aRowPosToFilePos
.size() == rowNum
)
934 m_aRowPosToFilePos
.push_back(rowPos
);
937 SAL_WARN_IF(m_aRowPosToFilePos
[rowNum
] != rowPos
,
939 "Setting position for row " << rowNum
<< " to (" << rowPos
.first
<< ", " << rowPos
.second
<< "), "
940 "but already had different position (" << m_aRowPosToFilePos
[rowNum
].first
<< ", " << m_aRowPosToFilePos
[rowNum
].second
<< ")");
941 m_aRowPosToFilePos
[rowNum
] = rowPos
;
945 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */