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 .
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>
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
;
61 using std::lower_bound
;
64 void OFlatTable::fillColumns(const css::lang::Locale
& _aLocale
)
66 m_bNeedToReadLine
= true; // we overwrite m_aCurrentLine, seek the stream, ...
67 m_pFileStream
->Seek(0);
68 m_aCurrentLine
= QuotedTokenizedString();
71 const OFlatConnection
* const pConnection
= getFlatConnection();
72 const bool bHasHeaderLine
= pConnection
->isHeaderLine();
74 QuotedTokenizedString aHeaderLine
;
75 TRowPositionInFile
rowPos(0, 0);
79 bRead
= readLine(&rowPos
.second
, &rowPos
.first
, true);
81 aHeaderLine
= m_aCurrentLine
;
83 setRowPos(rowNum
++, rowPos
);
88 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
90 setRowPos(rowNum
++, rowPos
);
93 if ( !bHasHeaderLine
|| !aHeaderLine
.Len())
95 // use first non-empty row as headerline because we need the number of columns
96 while(bRead
&& m_aCurrentLine
.Len() == 0)
98 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
100 setRowPos(rowNum
++, rowPos
);
102 aHeaderLine
= m_aCurrentLine
;
105 const sal_Int32 nFieldCount
= aHeaderLine
.GetTokenCount(m_cFieldDelimiter
,m_cStringDelimiter
);
108 m_aColumns
= new OSQLColumns();
113 m_aPrecisions
.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);
121 const bool bCase
= m_pConnection
->getMetaData()->supportsMixedCaseQuotedIdentifiers();
122 CharClass
aCharClass( pConnection
->getDriver()->getComponentContext(), LanguageTag( _aLocale
));
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;
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
++ )
142 OUString aColumnName
;
143 if ( bHasHeaderLine
)
145 aColumnName
= aHeaderLine
.GetTokenSpecial(nStartPosHeaderLine
,m_cFieldDelimiter
,m_cStringDelimiter
);
147 if ( aColumnName
.isEmpty() )
149 aColumnName
= "C" + OUString::number(i
+1);
151 aColumnNames
.push_back(aColumnName
);
155 impl_fillColumnInfo_nothrow(m_aCurrentLine
, nStartPosFirstLine
, nStartPosFirstLine2
,
156 m_aTypes
[i
], m_aPrecisions
[i
], m_aScales
[i
], aTypeNames
[i
],
157 cDecimalDelimiter
, cThousandDelimiter
, aCharClass
);
161 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
163 setRowPos(rowNum
++, rowPos
);
165 while(nRowCount
< nMaxRowsToScan
&& bRead
);
167 for( sal_Int32 i
= 0; i
< nFieldCount
; i
++ )
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())
175 aAlias
= aColumnNames
[i
] + OUString::number(++nExprCnt
);
176 aFind
= connectivity::find(m_aColumns
->begin(),m_aColumns
->end(),aAlias
,aCase
);
179 rtl::Reference
<sdbcx::OColumn
> pColumn
= new sdbcx::OColumn(aAlias
,aTypeNames
[i
],OUString(),OUString(),
180 ColumnValue::NULLABLE
,
188 m_CatalogName
, getSchema(), getName());
189 m_aColumns
->push_back(pColumn
);
192 m_pFileStream
->Seek(m_aRowPosToFilePos
[0].second
);
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
)
199 if ( io_nType
!= DataType::VARCHAR
)
201 bool bNumeric
= io_nType
== DataType::SQLNULL
|| io_nType
== DataType::DOUBLE
|| io_nType
== DataType::DECIMAL
|| io_nType
== DataType::INTEGER
;
202 sal_uLong nIndex
= 0;
206 // first without fielddelimiter
207 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
208 if (aField
.isEmpty() ||
209 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
212 if ( m_cStringDelimiter
!= '\0' )
213 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
215 nStartPosFirstLine2
= nStartPosFirstLine
;
220 if ( m_cStringDelimiter
!= '\0' )
221 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
225 if (aField2
.isEmpty())
233 sal_Int32 nDecimalDelCount
= 0;
234 sal_Int32 nSpaceCount
= 0;
235 for( sal_Int32 j
= 0; j
< aField2
.getLength(); j
++ )
237 const sal_Unicode c
= aField2
[j
];
238 if ( j
== nSpaceCount
&& m_cFieldDelimiter
!= 32 && c
== 32 )
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
!= '-' ) ) )
252 if (cDecimalDelimiter
&& c
== cDecimalDelimiter
)
254 io_nPrecisions
= 15; // we have a decimal value
257 } // if (cDecimalDelimiter && c == cDecimalDelimiter)
262 if (nDecimalDelCount
> 1 || nDot
> 1 ) // if there is more than one dot it isn't a number
264 if (bNumeric
&& cThousandDelimiter
)
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)
270 const sal_Unicode c
= aValue
[j
];
271 // just digits, decimal- and thousands-delimiter?
272 if (c
== cThousandDelimiter
&& j
)
282 // now also check for a date field
287 nIndex
= m_xNumberFormatter
->detectNumberFormat(css::util::NumberFormat::ALL
,aField2
);
296 else if ( io_nType
== DataType::DATE
|| io_nType
== DataType::TIMESTAMP
|| io_nType
== DataType::TIME
)
298 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
299 if (aField
.isEmpty() ||
300 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
306 if ( m_cStringDelimiter
!= '\0' )
307 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
310 if (!aField2
.isEmpty() )
314 nIndex
= m_xNumberFormatter
->detectNumberFormat(css::util::NumberFormat::ALL
,aField2
);
325 if (cDecimalDelimiter
)
329 io_nType
= DataType::DECIMAL
;
330 o_sTypeName
= "DECIMAL";
334 io_nType
= DataType::DOUBLE
;
335 o_sTypeName
= "DOUBLE";
340 io_nType
= DataType::INTEGER
;
347 switch (comphelper::getNumberFormatType(m_xNumberFormatter
,nIndex
))
349 case css::util::NumberFormat::DATE
:
350 io_nType
= DataType::DATE
;
351 o_sTypeName
= "DATE";
353 case css::util::NumberFormat::DATETIME
:
354 io_nType
= DataType::TIMESTAMP
;
355 o_sTypeName
= "TIMESTAMP";
357 case css::util::NumberFormat::TIME
:
358 io_nType
= DataType::TIME
;
359 o_sTypeName
= "TIME";
362 io_nType
= DataType::VARCHAR
;
363 io_nPrecisions
= 0; // nyi: Data can be longer!
365 o_sTypeName
= "VARCHAR";
371 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
372 if (aField
.isEmpty() ||
373 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
375 if ( m_cStringDelimiter
!= '\0' )
376 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
378 nStartPosFirstLine2
= nStartPosFirstLine
;
382 if ( m_cStringDelimiter
!= '\0' )
383 aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
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
,
401 ,m_cStringDelimiter(_pConnection
->getStringDelimiter())
402 ,m_cFieldDelimiter(_pConnection
->getFieldDelimiter())
403 ,m_bNeedToReadLine(false)
408 void OFlatTable::construct()
410 SvtSysLocale aLocale
;
411 css::lang::Locale
aAppLocale(aLocale
.GetLanguageTag().getLocale());
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
;
420 aURL
.SetURL(getEntry());
422 if(aURL
.getExtension() != m_pConnection
->getExtension())
423 aURL
.setExtension(m_pConnection
->getExtension());
425 OUString aFileName
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
427 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READWRITE
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYWRITE
);
430 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READ
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYNONE
);
435 sal_uInt64
const nSize
= m_pFileStream
->remainingSize();
437 // Buffersize is dependent on the file-size
438 m_pFileStream
->SetBufferSize(nSize
> 1000000 ? 32768 :
439 nSize
> 100000 ? 16384 :
440 nSize
> 10000 ? 4096 : 1024);
442 fillColumns(aAppLocale
);
447 OUString
OFlatTable::getEntry() const
452 Reference
< XResultSet
> xDir
= m_pConnection
->getDir()->getStaticResultSet();
453 Reference
< XRow
> xRow(xDir
,UNO_QUERY
);
461 sName
= xRow
->getString(1);
462 aURL
.SetSmartProtocol(INetProtocol::File
);
463 OUString sUrl
= m_pConnection
->getURL() + "/" + sName
;
464 aURL
.SetSmartURL( sUrl
);
467 sExt
= aURL
.getExtension();
469 // name and extension have to coincide
470 if ( m_pConnection
->matchesExtension( sExt
) )
472 if ( !sExt
.isEmpty() )
473 sName
= sName
.replaceAt(sName
.getLength() - (sExt
.getLength() + 1), sExt
.getLength()+1, OUString());
474 if ( sName
== m_Name
)
476 Reference
< XContentAccess
> xContentAccess( xDir
, UNO_QUERY
);
477 sURL
= xContentAccess
->queryContentIdentifierString();
482 xDir
->beforeFirst(); // move back to before first record
484 catch(const Exception
&)
491 void OFlatTable::refreshColumns()
493 ::osl::MutexGuard
aGuard( m_aMutex
);
495 ::std::vector
< OUString
> aVector
;
496 aVector
.reserve(m_aColumns
->size());
498 for (auto const& column
: *m_aColumns
)
499 aVector
.push_back(Reference
< XNamed
>(column
,UNO_QUERY_THROW
)->getName());
502 m_xColumns
->reFill(aVector
);
504 m_xColumns
= new OFlatColumns(this,m_aMutex
,aVector
);
508 void SAL_CALL
OFlatTable::disposing()
510 OFileTable::disposing();
511 ::osl::MutexGuard
aGuard(m_aMutex
);
512 m_aColumns
= nullptr;
515 Sequence
< Type
> SAL_CALL
OFlatTable::getTypes( )
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
)
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()))
530 aOwnTypes
.push_back(*pBegin
);
533 return Sequence
< Type
>(aOwnTypes
.data(), aOwnTypes
.size());
537 Any SAL_CALL
OFlatTable::queryInterface( const Type
& rType
)
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())
546 Any aRet
= OTable_TYPEDEF::queryInterface(rType
);
547 return aRet
.hasValue() ? aRet
: ::cppu::queryInterface(rType
,static_cast< css::lang::XUnoTunnel
*> (this));
551 Sequence
< sal_Int8
> OFlatTable::getUnoTunnelId()
553 static ::cppu::OImplementationId implId
;
555 return implId
.getImplementationId();
558 // css::lang::XUnoTunnel
560 sal_Int64
OFlatTable::getSomething( const Sequence
< sal_Int8
> & rId
)
562 return (isUnoTunnelId
<OFlatTable
>(rId
))
563 ? reinterpret_cast< sal_Int64
>( this )
564 : OFlatTable_BASE::getSomething(rId
);
567 bool OFlatTable::fetchRow(OValueRefRow
& _rRow
, const OSQLColumns
& _rCols
, bool bRetrieveData
)
569 *(*_rRow
)[0] = m_nFilePos
;
575 if ( m_bNeedToReadLine
)
577 m_pFileStream
->Seek(m_nFilePos
);
578 TRowPositionInFile
rowPos(0, 0);
579 if(readLine(&rowPos
.second
, &rowPos
.first
))
581 setRowPos(m_nRowPos
, rowPos
);
582 m_bNeedToReadLine
= false;
585 // else let run through so that we set _rRow to all NULL
588 const OFlatConnection
* const pConnection
= getFlatConnection();
589 const sal_Unicode cDecimalDelimiter
= pConnection
->getDecimalDelimiter();
590 const sal_Unicode cThousandDelimiter
= pConnection
->getThousandDelimiter();
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
;
600 OUString aStr
= m_aCurrentLine
.GetTokenSpecial(nStartPos
,m_cFieldDelimiter
,m_cStringDelimiter
);
604 (*_rRow
)[i
]->setNull();
608 sal_Int32 nType
= m_aTypes
[i
-1];
611 case DataType::TIMESTAMP
:
617 double nRes
= m_xNumberFormatter
->convertStringToNumber(css::util::NumberFormat::ALL
,aStr
);
622 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes
,m_aNullDate
));
624 case DataType::TIMESTAMP
:
625 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes
,m_aNullDate
));
628 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes
));
633 (*_rRow
)[i
]->setNull();
636 case DataType::DOUBLE
:
637 case DataType::INTEGER
:
638 case DataType::DECIMAL
:
639 case DataType::NUMERIC
:
642 OUString aStrConverted
;
643 if ( DataType::INTEGER
!= nType
)
645 OSL_ENSURE((cDecimalDelimiter
&& nType
!= DataType::INTEGER
) ||
646 (!cDecimalDelimiter
&& nType
== DataType::INTEGER
),
649 OUStringBuffer
aBuf(aStr
.getLength());
650 // convert to Standard-Notation (DecimalPOINT without thousands-comma):
651 for (sal_Int32 j
= 0; j
< aStr
.getLength(); ++j
)
653 const sal_Unicode cChar
= aStr
[j
];
654 if (cDecimalDelimiter
&& cChar
== cDecimalDelimiter
)
656 else if ( cChar
== '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
658 else if (cThousandDelimiter
&& cChar
== cThousandDelimiter
)
664 } // for (j = 0; j < aStr.(); ++j)
665 aStrConverted
= aBuf
.makeStringAndClear();
666 } // if ( DataType::INTEGER != nType )
669 if ( cThousandDelimiter
)
670 aStrConverted
= aStr
.replaceAll(OUStringChar(cThousandDelimiter
), "");
672 aStrConverted
= aStr
;
674 const double nVal
= ::rtl::math::stringToDouble(aStrConverted
,'.',',');
677 if ( DataType::DECIMAL
== nType
|| DataType::NUMERIC
== nType
)
678 *(*_rRow
)[i
] = OUString(OUString::number(nVal
));
685 // Copy Value as String in Row-Variable
686 *(*_rRow
)[i
] = ORowSetValue(aStr
);
690 (*_rRow
)[i
]->setTypeKind(nType
);
697 void OFlatTable::refreshHeader()
699 SAL_INFO( "connectivity.flat", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
705 template< typename Tp
, typename Te
> struct RangeBefore
707 bool operator() (const Tp
&p
, const Te
&e
)
709 assert(p
.first
<= p
.second
);
710 return p
.second
<= e
;
715 bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition
, sal_Int32 nOffset
, sal_Int32
& nCurPos
)
717 OSL_ENSURE(m_pFileStream
,"OFlatTable::seekRow: FileStream is NULL!");
720 switch(eCursorPosition
)
722 case IResultSetHelper::FIRST
:
725 case IResultSetHelper::NEXT
:
727 assert(m_nRowPos
>= 0);
728 if(m_nMaxRowCount
!= 0 && m_nRowPos
> m_nMaxRowCount
)
731 if(m_aRowPosToFilePos
.size() > o3tl::make_unsigned(m_nRowPos
))
733 m_bNeedToReadLine
= true;
734 m_nFilePos
= m_aRowPosToFilePos
[m_nRowPos
].first
;
735 nCurPos
= m_aRowPosToFilePos
[m_nRowPos
].second
;
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
);
746 m_nFilePos
= lastRowPos
.second
;
747 m_pFileStream
->Seek(m_nFilePos
);
749 TRowPositionInFile newRowPos
;
750 if(!readLine(&newRowPos
.second
, &newRowPos
.first
))
752 m_nMaxRowCount
= m_nRowPos
- 1;
756 nCurPos
= newRowPos
.second
;
757 setRowPos(m_nRowPos
, newRowPos
);
762 case IResultSetHelper::PRIOR
:
763 assert(m_nRowPos
>= 0);
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;
779 case IResultSetHelper::LAST
:
780 if (m_nMaxRowCount
== 0)
782 while(seekRow(IResultSetHelper::NEXT
, 1, nCurPos
)) ; // run through after last row
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
:
788 const sal_Int32 nNewRowPos
= m_nRowPos
+ nOffset
;
791 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
792 return seekRow(IResultSetHelper::ABSOLUTE1
, nNewRowPos
, nCurPos
);
794 case IResultSetHelper::ABSOLUTE1
:
798 if (m_nMaxRowCount
== 0)
800 if (!seekRow(IResultSetHelper::LAST
, 0, nCurPos
))
803 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
804 nOffset
= m_nMaxRowCount
+ nOffset
;
808 seekRow(IResultSetHelper::ABSOLUTE1
, 0, nCurPos
);
811 if(m_nMaxRowCount
&& nOffset
> m_nMaxRowCount
)
813 m_nRowPos
= m_nMaxRowCount
+ 1;
814 const TRowPositionInFile
&lastRowPos(m_aRowPosToFilePos
.back());
815 m_nFilePos
= lastRowPos
.second
;
816 nCurPos
= lastRowPos
.second
;
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
))
825 m_nFilePos
= m_aRowPosToFilePos
[nOffset
].first
;
826 nCurPos
= m_aRowPosToFilePos
[nOffset
].second
;
828 m_bNeedToReadLine
= true;
832 assert(m_nRowPos
< nOffset
);
833 while(m_nRowPos
< nOffset
)
835 if(!seekRow(IResultSetHelper::NEXT
, 1, nCurPos
))
838 assert(m_nRowPos
== nOffset
);
843 case IResultSetHelper::BOOKMARK
:
845 vector
< TRowPositionInFile
>::const_iterator aFind
= lower_bound(m_aRowPosToFilePos
.begin(),
846 m_aRowPosToFilePos
.end(),
848 RangeBefore
< TRowPositionInFile
, sal_Int32
>());
850 if(aFind
== m_aRowPosToFilePos
.end() || aFind
->first
!= nOffset
)
854 m_bNeedToReadLine
= true;
855 m_nFilePos
= aFind
->first
;
856 nCurPos
= aFind
->second
;
857 m_nRowPos
= aFind
- m_aRowPosToFilePos
.begin();
866 bool OFlatTable::readLine(sal_Int32
* const pEndPos
, sal_Int32
* const pStartPos
, const bool nonEmpty
)
868 const rtl_TextEncoding nEncoding
= m_pConnection
->getTextEncoding();
869 m_aCurrentLine
= QuotedTokenizedString();
873 *pStartPos
= static_cast<sal_Int32
>(m_pFileStream
->Tell());
874 m_pFileStream
->ReadByteStringLine(m_aCurrentLine
, nEncoding
);
875 if (m_pFileStream
->eof())
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;
884 bool wasQuote
= false;
885 const sal_Unicode
*p
= sLine
.GetString().getStr() + nLastOffset
;
890 if (*p
== m_cStringDelimiter
)
891 wasQuote
= !wasQuote
;
898 if (*p
== m_cFieldDelimiter
)
899 isFieldStarting
= true;
907 isFieldStarting
= false;
908 if (*p
== m_cStringDelimiter
)
910 else if (*p
== m_cFieldDelimiter
)
911 isFieldStarting
= true;
913 else if (*p
== m_cFieldDelimiter
)
914 isFieldStarting
= true;
924 nLastOffset
= sLine
.Len();
925 m_pFileStream
->ReadByteStringLine(sLine
,nEncoding
);
926 if ( !m_pFileStream
->eof() )
928 OUString aStr
= m_aCurrentLine
.GetString() + "\n" + sLine
.GetString();
929 m_aCurrentLine
.SetString(aStr
);
930 sLine
= m_aCurrentLine
;
939 while(nonEmpty
&& m_aCurrentLine
.Len() == 0);
942 *pEndPos
= static_cast<sal_Int32
>(m_pFileStream
->Tell());
947 void OFlatTable::setRowPos(const vector
<TRowPositionInFile
>::size_type rowNum
, const TRowPositionInFile
&rowPos
)
949 assert(m_aRowPosToFilePos
.size() >= rowNum
);
950 if(m_aRowPosToFilePos
.size() == rowNum
)
951 m_aRowPosToFilePos
.push_back(rowPos
);
954 SAL_WARN_IF(m_aRowPosToFilePos
[rowNum
] != rowPos
,
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
;
962 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */