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 css::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
);
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
);
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
> 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
++ )
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
], aTypeNames
[i
],
160 cDecimalDelimiter
, cThousandDelimiter
, aCharClass
);
164 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
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
,
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
);
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(css::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
);
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(css::util::NumberFormat::ALL
,aField2
);
329 if (cDecimalDelimiter
)
333 io_nType
= DataType::DECIMAL
;
334 o_sTypeName
= "DECIMAL";
338 io_nType
= DataType::DOUBLE
;
339 o_sTypeName
= "DOUBLE";
344 io_nType
= DataType::INTEGER
;
351 switch (comphelper::getNumberFormatType(m_xNumberFormatter
,nIndex
))
353 case css::util::NumberFormat::DATE
:
354 io_nType
= DataType::DATE
;
355 o_sTypeName
= "DATE";
357 case css::util::NumberFormat::DATETIME
:
358 io_nType
= DataType::TIMESTAMP
;
359 o_sTypeName
= "TIMESTAMP";
361 case css::util::NumberFormat::TIME
:
362 io_nType
= DataType::TIME
;
363 o_sTypeName
= "TIME";
366 io_nType
= DataType::VARCHAR
;
367 io_nPrecisions
= 0; // nyi: Data can be longer!
369 o_sTypeName
= "VARCHAR";
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
);
382 nStartPosFirstLine2
= nStartPosFirstLine
;
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
,
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
;
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
);
434 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READ
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYNONE
);
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
);
451 OUString
OFlatTable::getEntry()
456 Reference
< XResultSet
> xDir
= m_pConnection
->getDir()->getStaticResultSet();
457 Reference
< XRow
> xRow(xDir
,UNO_QUERY
);
465 sName
= xRow
->getString(1);
466 aURL
.SetSmartProtocol(INetProtocol::File
);
467 OUString sUrl
= m_pConnection
->getURL() + "/" + sName
;
468 aURL
.SetSmartURL( sUrl
);
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();
486 xDir
->beforeFirst(); // move back to before first record
488 catch(const Exception
&)
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());
506 m_pColumns
->reFill(aVector
);
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())
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;
560 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
563 static ::cppu::OImplementationId 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
;
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;
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();
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
;
612 OUString aStr
= m_aCurrentLine
.GetTokenSpecial(nStartPos
,m_cFieldDelimiter
,m_cStringDelimiter
);
616 (_rRow
->get())[i
]->setNull();
620 sal_Int32 nType
= m_aTypes
[i
-1];
623 case DataType::TIMESTAMP
:
629 double nRes
= m_xNumberFormatter
->convertStringToNumber(css::util::NumberFormat::ALL
,aStr
);
634 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes
,m_aNullDate
));
636 case DataType::TIMESTAMP
:
637 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes
,m_aNullDate
));
640 *(_rRow
->get())[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes
));
645 (_rRow
->get())[i
]->setNull();
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
),
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
)
668 else if ( cChar
== '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
670 else if (cThousandDelimiter
&& cChar
== cThousandDelimiter
)
676 } // for (j = 0; j < aStr.getLength(); ++j)
677 aStrConverted
= aBuf
.makeStringAndClear();
678 } // if ( DataType::INTEGER != nType )
681 if ( cThousandDelimiter
)
682 aStrConverted
= aStr
.replaceAll(OUStringLiteral1(cThousandDelimiter
), "");
684 aStrConverted
= aStr
;
686 const double nVal
= ::rtl::math::stringToDouble(aStrConverted
,'.',',');
689 if ( DataType::DECIMAL
== nType
|| DataType::NUMERIC
== nType
)
690 *(_rRow
->get())[i
] = OUString::number(nVal
);
692 *(_rRow
->get())[i
] = nVal
;
697 // Copy Value as String in Row-Variable
698 *(_rRow
->get())[i
] = ORowSetValue(aStr
);
702 (_rRow
->get())[i
]->setTypeKind(nType
);
709 void OFlatTable::refreshHeader()
711 SAL_INFO( "connectivity.drivers", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
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
:
737 case IResultSetHelper::NEXT
:
739 assert(m_nRowPos
>= 0);
740 if(m_nMaxRowCount
!= 0 && m_nRowPos
> m_nMaxRowCount
)
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
;
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;
768 nCurPos
= newRowPos
.second
;
769 setRowPos(m_nRowPos
, newRowPos
);
774 case IResultSetHelper::PRIOR
:
775 assert(m_nRowPos
>= 0);
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;
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
);
799 case IResultSetHelper::RELATIVE1
:
801 const sal_Int32 nNewRowPos
= m_nRowPos
+ nOffset
;
804 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
805 return seekRow(IResultSetHelper::ABSOLUTE1
, nNewRowPos
, nCurPos
);
807 case IResultSetHelper::ABSOLUTE1
:
811 if (m_nMaxRowCount
== 0)
813 if (!seekRow(IResultSetHelper::LAST
, 0, nCurPos
))
816 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
817 nOffset
= m_nMaxRowCount
+ nOffset
;
821 seekRow(IResultSetHelper::ABSOLUTE1
, 0, nCurPos
);
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
;
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
;
841 m_bNeedToReadLine
= true;
845 assert(m_nRowPos
< nOffset
);
846 while(m_nRowPos
< nOffset
)
848 if(!seekRow(IResultSetHelper::NEXT
, 1, nCurPos
))
851 assert(m_nRowPos
== nOffset
);
856 case IResultSetHelper::BOOKMARK
:
858 vector
< TRowPositionInFile
>::const_iterator aFind
= lower_bound(m_aRowPosToFilePos
.begin(),
859 m_aRowPosToFilePos
.end(),
861 RangeBefore
< TRowPositionInFile
, sal_Int32
>());
863 if(aFind
== m_aRowPosToFilePos
.end() || aFind
->first
!= nOffset
)
867 m_bNeedToReadLine
= true;
868 m_nFilePos
= aFind
->first
;
869 nCurPos
= aFind
->second
;
870 m_nRowPos
= aFind
- m_aRowPosToFilePos
.begin();
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();
886 *pStartPos
= (sal_Int32
)m_pFileStream
->Tell();
887 m_pFileStream
->ReadByteStringLine(m_aCurrentLine
, nEncoding
);
888 if (m_pFileStream
->IsEof())
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;
897 bool wasQuote
= false;
898 const sal_Unicode
*p
= sLine
.GetString().getStr() + nLastOffset
;
903 if (*p
== m_cStringDelimiter
)
904 wasQuote
= !wasQuote
;
911 if (*p
== m_cFieldDelimiter
)
912 isFieldStarting
= true;
920 isFieldStarting
= false;
921 if (*p
== m_cStringDelimiter
)
923 else if (*p
== m_cFieldDelimiter
)
924 isFieldStarting
= true;
926 else if (*p
== m_cFieldDelimiter
)
927 isFieldStarting
= true;
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
;
952 while(nonEmpty
&& m_aCurrentLine
.Len() == 0);
955 *pEndPos
= (sal_Int32
)m_pFileStream
->Tell();
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
);
967 SAL_WARN_IF(m_aRowPosToFilePos
[rowNum
] != rowPos
,
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: */