Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <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 <stdio.h>
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>
47 :
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;
64 :
65 :
66 0 : void OFlatTable::fillColumns(const ::com::sun::star::lang::Locale& _aLocale)
67 : {
68 0 : m_bNeedToReadLine = true; // we overwrite m_aCurrentLine, seek the stream, ...
69 0 : m_pFileStream->Seek(0);
70 0 : m_aCurrentLine = QuotedTokenizedString();
71 0 : bool bRead = true;
72 :
73 0 : const OFlatConnection* const pConnection = getFlatConnection();
74 0 : const bool bHasHeaderLine = pConnection->isHeaderLine();
75 :
76 0 : QuotedTokenizedString aHeaderLine;
77 0 : TRowPositionInFile rowPos(0, 0);
78 0 : sal_Int32 rowNum(0);
79 0 : if ( bHasHeaderLine )
80 : {
81 0 : bRead = readLine(&rowPos.second, &rowPos.first, true);
82 0 : if(bRead)
83 0 : aHeaderLine = m_aCurrentLine;
84 : }
85 0 : setRowPos(rowNum++, rowPos);
86 :
87 : // read first row
88 0 : QuotedTokenizedString aFirstLine;
89 0 : if(bRead)
90 : {
91 0 : bRead = readLine(&rowPos.second, &rowPos.first, false);
92 0 : if(bRead)
93 0 : setRowPos(rowNum++, rowPos);
94 : }
95 :
96 0 : if ( !bHasHeaderLine || !aHeaderLine.Len())
97 : {
98 : // use first non-empty row as headerline because we need the number of columns
99 0 : while(bRead && m_aCurrentLine.Len() == 0)
100 : {
101 0 : bRead = readLine(&rowPos.second, &rowPos.first, false);
102 0 : if(bRead)
103 0 : setRowPos(rowNum++, rowPos);
104 : }
105 0 : aHeaderLine = m_aCurrentLine;
106 : }
107 : // column count
108 0 : const sal_Int32 nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter);
109 :
110 0 : if(!m_aColumns.is())
111 0 : m_aColumns = new OSQLColumns();
112 : else
113 0 : m_aColumns->get().clear();
114 :
115 0 : m_aTypes.clear();
116 0 : m_aPrecisions.clear();
117 0 : m_aScales.clear();
118 : // reserve some space
119 0 : m_aColumns->get().reserve(nFieldCount+1);
120 0 : m_aTypes.assign(nFieldCount+1,DataType::SQLNULL);
121 0 : m_aPrecisions.assign(nFieldCount+1,-1);
122 0 : m_aScales.assign(nFieldCount+1,-1);
123 :
124 0 : const bool bCase = m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers();
125 0 : CharClass aCharClass( pConnection->getDriver()->getComponentContext(), LanguageTag( _aLocale));
126 : // read description
127 0 : const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
128 0 : const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
129 0 : ::comphelper::UStringMixEqual aCase(bCase);
130 0 : vector<OUString> aColumnNames;
131 0 : vector<OUString> m_aTypeNames;
132 0 : m_aTypeNames.resize(nFieldCount);
133 0 : const sal_Int32 nMaxRowsToScan = pConnection->getMaxRowsToScan();
134 0 : sal_Int32 nRowCount = 0;
135 :
136 0 : do
137 : {
138 0 : sal_Int32 nStartPosHeaderLine = 0; // use for efficient way to get the tokens
139 0 : sal_Int32 nStartPosFirstLine = 0; // use for efficient way to get the tokens
140 0 : sal_Int32 nStartPosFirstLine2 = 0;
141 0 : for( sal_Int32 i = 0; i < nFieldCount; i++ )
142 : {
143 0 : if ( nRowCount == 0)
144 : {
145 0 : OUString aColumnName;
146 0 : if ( bHasHeaderLine )
147 : {
148 0 : aColumnName = aHeaderLine.GetTokenSpecial(nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter);
149 : }
150 0 : if ( aColumnName.isEmpty() )
151 : {
152 0 : aColumnName = "C" + OUString::number(i+1);
153 : }
154 0 : aColumnNames.push_back(aColumnName);
155 : }
156 0 : if(bRead)
157 : {
158 : impl_fillColumnInfo_nothrow(m_aCurrentLine, nStartPosFirstLine, nStartPosFirstLine2,
159 0 : m_aTypes[i], m_aPrecisions[i], m_aScales[i], m_aTypeNames[i],
160 0 : cDecimalDelimiter, cThousandDelimiter, aCharClass);
161 : }
162 : }
163 0 : ++nRowCount;
164 0 : bRead = readLine(&rowPos.second, &rowPos.first, false);
165 0 : if(bRead)
166 0 : setRowPos(rowNum++, rowPos);
167 : }
168 0 : while(nRowCount < nMaxRowsToScan && bRead);
169 :
170 0 : for( sal_Int32 i = 0; i < nFieldCount; i++ )
171 : {
172 : // check if the columname already exists
173 0 : OUString aAlias(aColumnNames[i]);
174 0 : OSQLColumns::Vector::const_iterator aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
175 0 : sal_Int32 nExprCnt = 0;
176 0 : while(aFind != m_aColumns->get().end())
177 : {
178 0 : aAlias = aColumnNames[i] + OUString::number(++nExprCnt);
179 0 : aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
180 : }
181 :
182 0 : sdbcx::OColumn* pColumn = new sdbcx::OColumn(aAlias,m_aTypeNames[i],OUString(),OUString(),
183 : ColumnValue::NULLABLE,
184 0 : m_aPrecisions[i],
185 0 : m_aScales[i],
186 0 : m_aTypes[i],
187 : false,
188 : false,
189 : false,
190 : bCase,
191 0 : m_CatalogName, getSchema(), getName());
192 0 : Reference< XPropertySet> xCol = pColumn;
193 0 : m_aColumns->get().push_back(xCol);
194 0 : }
195 :
196 0 : m_pFileStream->Seek(m_aRowPosToFilePos[0].second);
197 0 : }
198 :
199 0 : 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)
202 : {
203 0 : if ( io_nType != DataType::VARCHAR )
204 : {
205 0 : bool bNumeric = io_nType == DataType::SQLNULL || io_nType == DataType::DOUBLE || io_nType == DataType::DECIMAL || io_nType == DataType::INTEGER;
206 0 : sal_uLong nIndex = 0;
207 :
208 0 : if ( bNumeric )
209 : {
210 : // first without fielddelimiter
211 0 : OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter,'\0');
212 0 : if (aField.isEmpty() ||
213 0 : (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
214 : {
215 0 : bNumeric = false;
216 0 : if ( m_cStringDelimiter != '\0' )
217 0 : aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
218 : else
219 0 : nStartPosFirstLine2 = nStartPosFirstLine;
220 : }
221 : else
222 : {
223 0 : OUString aField2;
224 0 : if ( m_cStringDelimiter != '\0' )
225 0 : aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
226 : else
227 0 : aField2 = aField;
228 :
229 0 : if (aField2.isEmpty())
230 : {
231 0 : bNumeric = false;
232 : }
233 : else
234 : {
235 0 : bNumeric = true;
236 0 : sal_Int32 nDot = 0;
237 0 : sal_Int32 nDecimalDelCount = 0;
238 0 : sal_Int32 nSpaceCount = 0;
239 0 : for( sal_Int32 j = 0; j < aField2.getLength(); j++ )
240 : {
241 0 : const sal_Unicode c = aField2[j];
242 0 : if ( j == nSpaceCount && m_cFieldDelimiter != 32 && c == 32 )
243 : {
244 0 : ++nSpaceCount;
245 0 : continue;
246 : }
247 : // just digits, decimal- and thousands-delimiter?
248 0 : if ( ( !cDecimalDelimiter || c != cDecimalDelimiter ) &&
249 0 : ( !cThousandDelimiter || c != cThousandDelimiter ) &&
250 0 : !aCharClass.isDigit(aField2,j) &&
251 0 : ( j != 0 || (c != '+' && c != '-' ) ) )
252 : {
253 0 : bNumeric = false;
254 0 : break;
255 : }
256 0 : if (cDecimalDelimiter && c == cDecimalDelimiter)
257 : {
258 0 : io_nPrecisions = 15; // we have an decimal value
259 0 : io_nScales = 2;
260 0 : ++nDecimalDelCount;
261 : } // if (cDecimalDelimiter && c == cDecimalDelimiter)
262 0 : if ( c == '.' )
263 0 : ++nDot;
264 : }
265 :
266 0 : if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number
267 0 : bNumeric = false;
268 0 : if (bNumeric && cThousandDelimiter)
269 : {
270 : // Is the delimiter correct?
271 0 : const OUString aValue = aField2.getToken(0,cDecimalDelimiter);
272 0 : for( sal_Int32 j = aValue.getLength() - 4; j >= 0; j -= 4)
273 : {
274 0 : const sal_Unicode c = aValue[j];
275 : // just digits, decimal- and thousands-delimiter?
276 0 : if (c == cThousandDelimiter && j)
277 0 : continue;
278 : else
279 : {
280 0 : bNumeric = false;
281 0 : break;
282 : }
283 0 : }
284 : }
285 :
286 : // now also check for a date field
287 0 : if (!bNumeric)
288 : {
289 : try
290 : {
291 0 : nIndex = m_xNumberFormatter->detectNumberFormat(::com::sun::star::util::NumberFormat::ALL,aField2);
292 : }
293 0 : catch(Exception&)
294 : {
295 : }
296 : }
297 0 : }
298 0 : }
299 : }
300 0 : else if ( io_nType == DataType::DATE || io_nType == DataType::TIMESTAMP || io_nType == DataType::TIME)
301 : {
302 0 : OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter,'\0');
303 0 : if (aField.isEmpty() ||
304 0 : (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
305 : {
306 : }
307 : else
308 : {
309 0 : OUString aField2;
310 0 : if ( m_cStringDelimiter != '\0' )
311 0 : aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
312 : else
313 0 : aField2 = aField;
314 0 : if (!aField2.isEmpty() )
315 : {
316 : try
317 : {
318 0 : nIndex = m_xNumberFormatter->detectNumberFormat(::com::sun::star::util::NumberFormat::ALL,aField2);
319 : }
320 0 : catch(Exception&)
321 : {
322 : }
323 0 : }
324 0 : }
325 : }
326 :
327 0 : sal_Int32 nFlags = 0;
328 0 : if (bNumeric)
329 : {
330 0 : if (cDecimalDelimiter)
331 : {
332 0 : if(io_nPrecisions)
333 : {
334 0 : io_nType = DataType::DECIMAL;
335 0 : static const OUString s_sDECIMAL("DECIMAL");
336 0 : o_sTypeName = s_sDECIMAL;
337 : }
338 : else
339 : {
340 0 : io_nType = DataType::DOUBLE;
341 0 : static const OUString s_sDOUBLE("DOUBLE");
342 0 : o_sTypeName = s_sDOUBLE;
343 : }
344 : }
345 : else
346 : {
347 0 : io_nType = DataType::INTEGER;
348 0 : io_nPrecisions = 0;
349 0 : io_nScales = 0;
350 : }
351 0 : nFlags = ColumnSearch::BASIC;
352 : }
353 : else
354 : {
355 0 : switch (comphelper::getNumberFormatType(m_xNumberFormatter,nIndex))
356 : {
357 : case NUMBERFORMAT_DATE:
358 0 : io_nType = DataType::DATE;
359 : {
360 0 : static const OUString s_sDATE("DATE");
361 0 : o_sTypeName = s_sDATE;
362 : }
363 0 : break;
364 : case NUMBERFORMAT_DATETIME:
365 0 : io_nType = DataType::TIMESTAMP;
366 : {
367 0 : static const OUString s_sTIMESTAMP("TIMESTAMP");
368 0 : o_sTypeName = s_sTIMESTAMP;
369 : }
370 0 : break;
371 : case NUMBERFORMAT_TIME:
372 0 : io_nType = DataType::TIME;
373 : {
374 0 : static const OUString s_sTIME("TIME");
375 0 : o_sTypeName = s_sTIME;
376 : }
377 0 : break;
378 : default:
379 0 : io_nType = DataType::VARCHAR;
380 0 : io_nPrecisions = 0; // nyi: Data can be longer!
381 0 : io_nScales = 0;
382 : {
383 0 : static const OUString s_sVARCHAR("VARCHAR");
384 0 : o_sTypeName = s_sVARCHAR;
385 : }
386 : };
387 0 : nFlags |= ColumnSearch::CHAR;
388 : }
389 : }
390 : else
391 : {
392 0 : OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter,'\0');
393 0 : if (aField.isEmpty() ||
394 0 : (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
395 : {
396 0 : if ( m_cStringDelimiter != '\0' )
397 0 : aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
398 : else
399 0 : nStartPosFirstLine2 = nStartPosFirstLine;
400 : }
401 : else
402 : {
403 0 : if ( m_cStringDelimiter != '\0' )
404 0 : aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
405 0 : }
406 : }
407 0 : }
408 :
409 0 : OFlatTable::OFlatTable(sdbcx::OCollection* _pTables,OFlatConnection* _pConnection,
410 : const OUString& _Name,
411 : const OUString& _Type,
412 : const OUString& _Description ,
413 : const OUString& _SchemaName,
414 : const OUString& _CatalogName
415 : ) : OFlatTable_BASE(_pTables,_pConnection,_Name,
416 : _Type,
417 : _Description,
418 : _SchemaName,
419 : _CatalogName)
420 : ,m_nRowPos(0)
421 : ,m_nMaxRowCount(0)
422 0 : ,m_cStringDelimiter(_pConnection->getStringDelimiter())
423 0 : ,m_cFieldDelimiter(_pConnection->getFieldDelimiter())
424 0 : ,m_bNeedToReadLine(false)
425 : {
426 :
427 0 : }
428 :
429 0 : void OFlatTable::construct()
430 : {
431 0 : SvtSysLocale aLocale;
432 0 : ::com::sun::star::lang::Locale aAppLocale(aLocale.GetLanguageTag().getLocale());
433 :
434 0 : Reference< XNumberFormatsSupplier > xSupplier = NumberFormatsSupplier::createWithLocale( m_pConnection->getDriver()->getComponentContext(), aAppLocale );
435 0 : m_xNumberFormatter.set( NumberFormatter::create( m_pConnection->getDriver()->getComponentContext()), UNO_QUERY_THROW);
436 0 : m_xNumberFormatter->attachNumberFormatsSupplier(xSupplier);
437 0 : Reference<XPropertySet> xProp(xSupplier->getNumberFormatSettings(),UNO_QUERY);
438 0 : xProp->getPropertyValue("NullDate") >>= m_aNullDate;
439 :
440 0 : INetURLObject aURL;
441 0 : aURL.SetURL(getEntry());
442 :
443 0 : if(aURL.getExtension() != OUString(m_pConnection->getExtension()))
444 0 : aURL.setExtension(m_pConnection->getExtension());
445 :
446 0 : OUString aFileName = aURL.GetMainURL(INetURLObject::NO_DECODE);
447 :
448 0 : m_pFileStream = createStream_simpleError( aFileName,STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYWRITE);
449 :
450 0 : if(!m_pFileStream)
451 0 : m_pFileStream = createStream_simpleError( aFileName,STREAM_READ | STREAM_NOCREATE | STREAM_SHARE_DENYNONE);
452 :
453 0 : if(m_pFileStream)
454 : {
455 0 : sal_uInt64 const nSize = m_pFileStream->remainingSize();
456 :
457 : // Buffersize is dependent on the file-size
458 : m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 :
459 : nSize > 100000 ? 16384 :
460 0 : nSize > 10000 ? 4096 : 1024);
461 :
462 0 : fillColumns(aAppLocale);
463 :
464 0 : refreshColumns();
465 0 : }
466 0 : }
467 :
468 0 : OUString OFlatTable::getEntry()
469 : {
470 0 : OUString sURL;
471 : try
472 : {
473 0 : Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet();
474 0 : Reference< XRow> xRow(xDir,UNO_QUERY);
475 0 : OUString sName;
476 0 : OUString sExt;
477 :
478 0 : INetURLObject aURL;
479 0 : xDir->beforeFirst();
480 0 : static const OUString s_sSeparator("/");
481 0 : while(xDir->next())
482 : {
483 0 : sName = xRow->getString(1);
484 0 : aURL.SetSmartProtocol(INET_PROT_FILE);
485 0 : OUString sUrl = m_pConnection->getURL() + s_sSeparator + sName;
486 0 : aURL.SetSmartURL( sUrl );
487 :
488 : // cut the extension
489 0 : sExt = aURL.getExtension();
490 :
491 : // name and extension have to coincide
492 0 : if ( m_pConnection->matchesExtension( sExt ) )
493 : {
494 0 : if ( !sExt.isEmpty() )
495 0 : sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength()+1, OUString());
496 0 : if ( sName == m_Name )
497 : {
498 0 : Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
499 0 : sURL = xContentAccess->queryContentIdentifierString();
500 0 : break;
501 : }
502 : }
503 0 : }
504 0 : xDir->beforeFirst(); // move back to before first record
505 : }
506 0 : catch(const Exception&)
507 : {
508 : OSL_ASSERT(false);
509 : }
510 0 : return sURL;
511 : }
512 :
513 0 : void OFlatTable::refreshColumns()
514 : {
515 0 : ::osl::MutexGuard aGuard( m_aMutex );
516 :
517 0 : TStringVector aVector;
518 0 : aVector.reserve(m_aColumns->get().size());
519 :
520 0 : for(OSQLColumns::Vector::const_iterator aIter = m_aColumns->get().begin();aIter != m_aColumns->get().end();++aIter)
521 0 : aVector.push_back(Reference< XNamed>(*aIter,UNO_QUERY)->getName());
522 :
523 0 : if(m_pColumns)
524 0 : m_pColumns->reFill(aVector);
525 : else
526 0 : m_pColumns = new OFlatColumns(this,m_aMutex,aVector);
527 0 : }
528 :
529 :
530 0 : void SAL_CALL OFlatTable::disposing(void)
531 : {
532 0 : OFileTable::disposing();
533 0 : ::osl::MutexGuard aGuard(m_aMutex);
534 0 : m_aColumns = NULL;
535 0 : }
536 :
537 0 : Sequence< Type > SAL_CALL OFlatTable::getTypes( ) throw(RuntimeException, std::exception)
538 : {
539 0 : Sequence< Type > aTypes = OTable_TYPEDEF::getTypes();
540 0 : vector<Type> aOwnTypes;
541 0 : aOwnTypes.reserve(aTypes.getLength());
542 0 : const Type* pBegin = aTypes.getConstArray();
543 0 : const Type* pEnd = pBegin + aTypes.getLength();
544 0 : for(;pBegin != pEnd;++pBegin)
545 : {
546 0 : if(!(*pBegin == cppu::UnoType<XKeysSupplier>::get()||
547 0 : *pBegin == cppu::UnoType<XRename>::get()||
548 0 : *pBegin == cppu::UnoType<XIndexesSupplier>::get()||
549 0 : *pBegin == cppu::UnoType<XAlterTable>::get()||
550 0 : *pBegin == cppu::UnoType<XDataDescriptorFactory>::get()))
551 : {
552 0 : aOwnTypes.push_back(*pBegin);
553 : }
554 : }
555 0 : Type *pTypes = aOwnTypes.empty() ? 0 : &aOwnTypes[0];
556 0 : return Sequence< Type >(pTypes, aOwnTypes.size());
557 : }
558 :
559 :
560 0 : Any SAL_CALL OFlatTable::queryInterface( const Type & rType ) throw(RuntimeException, std::exception)
561 : {
562 0 : if( rType == cppu::UnoType<XKeysSupplier>::get()||
563 0 : rType == cppu::UnoType<XIndexesSupplier>::get()||
564 0 : rType == cppu::UnoType<XRename>::get()||
565 0 : rType == cppu::UnoType<XAlterTable>::get()||
566 0 : rType == cppu::UnoType<XDataDescriptorFactory>::get())
567 0 : return Any();
568 :
569 0 : Any aRet = OTable_TYPEDEF::queryInterface(rType);
570 0 : return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< ::com::sun::star::lang::XUnoTunnel*> (this));
571 : }
572 :
573 :
574 0 : Sequence< sal_Int8 > OFlatTable::getUnoTunnelImplementationId()
575 : {
576 : static ::cppu::OImplementationId * pId = 0;
577 0 : if (! pId)
578 : {
579 0 : ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
580 0 : if (! pId)
581 : {
582 0 : static ::cppu::OImplementationId aId;
583 0 : pId = &aId;
584 0 : }
585 : }
586 0 : return pId->getImplementationId();
587 : }
588 :
589 : // com::sun::star::lang::XUnoTunnel
590 :
591 0 : sal_Int64 OFlatTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException, std::exception)
592 : {
593 0 : return (rId.getLength() == 16 && 0 == memcmp(getUnoTunnelImplementationId().getConstArray(), rId.getConstArray(), 16 ) )
594 : ? reinterpret_cast< sal_Int64 >( this )
595 0 : : OFlatTable_BASE::getSomething(rId);
596 : }
597 :
598 0 : bool OFlatTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bIsTable, bool bRetrieveData)
599 : {
600 0 : *(_rRow->get())[0] = m_nFilePos;
601 :
602 0 : if (!bRetrieveData)
603 0 : return true;
604 :
605 0 : bool result = false;
606 0 : if ( m_bNeedToReadLine )
607 : {
608 0 : m_pFileStream->Seek(m_nFilePos);
609 0 : TRowPositionInFile rowPos(0, 0);
610 0 : if(readLine(&rowPos.second, &rowPos.first))
611 : {
612 0 : setRowPos(m_nRowPos, rowPos);
613 0 : m_bNeedToReadLine = false;
614 0 : result = true;
615 : }
616 : // else let run through so that we set _rRow to all NULL
617 : }
618 :
619 0 : const OFlatConnection * const pConnection = getFlatConnection();
620 0 : const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
621 0 : const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
622 : // Fields:
623 0 : sal_Int32 nStartPos = 0;
624 0 : OSQLColumns::Vector::const_iterator aIter = _rCols.get().begin();
625 0 : OSQLColumns::Vector::const_iterator aEnd = _rCols.get().end();
626 0 : const OValueRefVector::Vector::size_type nCount = _rRow->get().size();
627 0 : for (OValueRefVector::Vector::size_type i = 1;
628 0 : aIter != aEnd && i < nCount;
629 : ++aIter, i++)
630 : {
631 0 : OUString aStr = m_aCurrentLine.GetTokenSpecial(nStartPos,m_cFieldDelimiter,m_cStringDelimiter);
632 :
633 0 : if (aStr.isEmpty())
634 : {
635 0 : (_rRow->get())[i]->setNull();
636 : }
637 : else
638 : {
639 : // lengths depending on data-type:
640 : sal_Int32 nLen;
641 0 : sal_Int32 nType = 0;
642 0 : if(bIsTable)
643 : {
644 0 : nLen = m_aPrecisions[i-1];
645 0 : nType = m_aTypes[i-1];
646 : }
647 : else
648 : {
649 0 : Reference< XPropertySet> xColumn = *aIter;
650 0 : xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)) >>= nLen;
651 0 : xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType;
652 : }
653 0 : switch(nType)
654 : {
655 : case DataType::TIMESTAMP:
656 : case DataType::DATE:
657 : case DataType::TIME:
658 : {
659 : try
660 : {
661 0 : double nRes = m_xNumberFormatter->convertStringToNumber(::com::sun::star::util::NumberFormat::ALL,aStr);
662 :
663 0 : switch(nType)
664 : {
665 : case DataType::DATE:
666 0 : *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes,m_aNullDate));
667 0 : break;
668 : case DataType::TIMESTAMP:
669 0 : *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes,m_aNullDate));
670 0 : break;
671 : default:
672 0 : *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes));
673 : }
674 : }
675 0 : catch(Exception&)
676 : {
677 0 : (_rRow->get())[i]->setNull();
678 : }
679 0 : } break;
680 : case DataType::DOUBLE:
681 : case DataType::INTEGER:
682 : case DataType::DECIMAL:
683 : case DataType::NUMERIC:
684 : {
685 :
686 0 : OUString aStrConverted;
687 0 : if ( DataType::INTEGER != nType )
688 : {
689 : OSL_ENSURE((cDecimalDelimiter && nType != DataType::INTEGER) ||
690 : (!cDecimalDelimiter && nType == DataType::INTEGER),
691 : "FalscherTyp");
692 :
693 0 : OUStringBuffer aBuf(aStr.getLength());
694 : // convert to Standard-Notation (DecimalPOINT without thousands-comma):
695 0 : for (sal_Int32 j = 0; j < aStr.getLength(); ++j)
696 : {
697 0 : const sal_Unicode cChar = aStr[j];
698 0 : if (cDecimalDelimiter && cChar == cDecimalDelimiter)
699 0 : aBuf.append('.');
700 0 : else if ( cChar == '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
701 0 : continue;
702 0 : else if (cThousandDelimiter && cChar == cThousandDelimiter)
703 : {
704 : // leave out
705 : }
706 : else
707 0 : aBuf.append(cChar);
708 : } // for (j = 0; j < aStr.getLength(); ++j)
709 0 : aStrConverted = aBuf.makeStringAndClear();
710 : } // if ( DataType::INTEGER != nType )
711 : else
712 : {
713 0 : if ( cThousandDelimiter )
714 0 : aStrConverted = comphelper::string::remove(aStr, cThousandDelimiter);
715 : else
716 0 : aStrConverted = aStr;
717 : }
718 0 : const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',',',NULL,NULL);
719 :
720 : // #99178# OJ
721 0 : if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType )
722 0 : *(_rRow->get())[i] = OUString::number(nVal);
723 : else
724 0 : *(_rRow->get())[i] = nVal;
725 0 : } break;
726 :
727 : default:
728 : {
729 : // Copy Value as String in Row-Variable
730 0 : *(_rRow->get())[i] = ORowSetValue(aStr);
731 : }
732 0 : break;
733 : } // switch(nType)
734 0 : (_rRow->get())[i]->setTypeKind(nType);
735 : }
736 0 : }
737 0 : return result;
738 : }
739 :
740 :
741 0 : void OFlatTable::refreshHeader()
742 : {
743 : SAL_INFO( "connectivity.drivers", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
744 0 : }
745 :
746 :
747 : namespace
748 : {
749 : template< typename Tp, typename Te> struct RangeBefore
750 : {
751 0 : bool operator() (const Tp &p, const Te &e)
752 : {
753 : assert(p.first <= p.second);
754 0 : return p.second <= e;
755 : }
756 : };
757 : }
758 :
759 0 : bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
760 : {
761 : OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!");
762 :
763 :
764 0 : switch(eCursorPosition)
765 : {
766 : case IResultSetHelper::FIRST:
767 0 : m_nRowPos = 0;
768 : // run through
769 : case IResultSetHelper::NEXT:
770 : {
771 : assert(m_nRowPos >= 0);
772 0 : if(m_nMaxRowCount != 0 && m_nRowPos > m_nMaxRowCount)
773 0 : return false;
774 0 : ++m_nRowPos;
775 0 : if(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos))
776 : {
777 0 : m_bNeedToReadLine = true;
778 0 : m_nFilePos = m_aRowPosToFilePos[m_nRowPos].first;
779 0 : nCurPos = m_aRowPosToFilePos[m_nRowPos].second;
780 : }
781 : else
782 : {
783 : assert(m_aRowPosToFilePos.size() == static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
784 0 : const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
785 : // Our ResultSet is allowed to disagree with us only
786 : // on the position of the first line
787 : // (because of the special case of the header...)
788 : assert(m_nRowPos == 1 || nCurPos == lastRowPos.second);
789 :
790 0 : m_nFilePos = lastRowPos.second;
791 0 : m_pFileStream->Seek(m_nFilePos);
792 :
793 0 : TRowPositionInFile newRowPos;
794 0 : if(!readLine(&newRowPos.second, &newRowPos.first, false))
795 : {
796 0 : m_nMaxRowCount = m_nRowPos - 1;
797 0 : return false;
798 : }
799 :
800 0 : nCurPos = newRowPos.second;
801 0 : setRowPos(m_nRowPos, newRowPos);
802 : }
803 : }
804 :
805 0 : break;
806 : case IResultSetHelper::PRIOR:
807 : assert(m_nRowPos >= 0);
808 :
809 0 : if(m_nRowPos == 0)
810 0 : return false;
811 :
812 0 : --m_nRowPos;
813 : {
814 : assert (m_nRowPos >= 0);
815 : assert(m_aRowPosToFilePos.size() >= static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
816 0 : const TRowPositionInFile &aPositions(m_aRowPosToFilePos[m_nRowPos]);
817 0 : m_nFilePos = aPositions.first;
818 0 : nCurPos = aPositions.second;
819 0 : m_bNeedToReadLine = true;
820 : }
821 :
822 0 : break;
823 : case IResultSetHelper::LAST:
824 0 : if (m_nMaxRowCount == 0)
825 : {
826 0 : while(seekRow(IResultSetHelper::NEXT, 1, nCurPos)) ; // run through after last row
827 : }
828 : // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
829 0 : return seekRow(IResultSetHelper::ABSOLUTE1, m_nMaxRowCount, nCurPos);
830 : break;
831 : case IResultSetHelper::RELATIVE1:
832 : {
833 0 : const sal_Int32 nNewRowPos = m_nRowPos + nOffset;
834 0 : if (nNewRowPos < 0)
835 0 : return false;
836 : // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
837 0 : return seekRow(IResultSetHelper::ABSOLUTE1, nNewRowPos, nCurPos);
838 : }
839 : case IResultSetHelper::ABSOLUTE1:
840 : {
841 0 : if(nOffset < 0)
842 : {
843 0 : if (m_nMaxRowCount == 0)
844 : {
845 0 : if (!seekRow(IResultSetHelper::LAST, 0, nCurPos))
846 0 : return false;
847 : }
848 : // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
849 0 : nOffset = m_nMaxRowCount + nOffset;
850 : }
851 0 : if(nOffset < 0)
852 : {
853 0 : seekRow(IResultSetHelper::ABSOLUTE1, 0, nCurPos);
854 0 : return false;
855 : }
856 0 : if(m_nMaxRowCount && nOffset > m_nMaxRowCount)
857 : {
858 0 : m_nRowPos = m_nMaxRowCount + 1;
859 0 : const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
860 0 : m_nFilePos = lastRowPos.second;
861 0 : nCurPos = lastRowPos.second;
862 0 : return false;
863 : }
864 :
865 : assert(m_nRowPos >=0);
866 : assert(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos));
867 : assert(nOffset >= 0);
868 0 : if(m_aRowPosToFilePos.size() > static_cast< vector< TRowPositionInFile >::size_type >(nOffset))
869 : {
870 0 : m_nFilePos = m_aRowPosToFilePos[nOffset].first;
871 0 : nCurPos = m_aRowPosToFilePos[nOffset].second;
872 0 : m_nRowPos = nOffset;
873 0 : m_bNeedToReadLine = true;
874 : }
875 : else
876 : {
877 : assert(m_nRowPos < nOffset);
878 0 : while(m_nRowPos < nOffset)
879 : {
880 0 : if(!seekRow(IResultSetHelper::NEXT, 1, nCurPos))
881 0 : return false;
882 : }
883 : assert(m_nRowPos == nOffset);
884 : }
885 : }
886 :
887 0 : break;
888 : case IResultSetHelper::BOOKMARK:
889 : {
890 : vector< TRowPositionInFile >::const_iterator aFind = lower_bound(m_aRowPosToFilePos.begin(),
891 : m_aRowPosToFilePos.end(),
892 : nOffset,
893 0 : RangeBefore< TRowPositionInFile, sal_Int32 >());
894 :
895 0 : if(aFind == m_aRowPosToFilePos.end() || aFind->first != nOffset)
896 : //invalid bookmark
897 0 : return false;
898 :
899 0 : m_bNeedToReadLine = true;
900 0 : m_nFilePos = aFind->first;
901 0 : nCurPos = aFind->second;
902 0 : m_nRowPos = aFind - m_aRowPosToFilePos.begin();
903 0 : break;
904 : }
905 : }
906 :
907 0 : return true;
908 : }
909 :
910 :
911 0 : bool OFlatTable::readLine(sal_Int32 * const pEndPos, sal_Int32 * const pStartPos, const bool nonEmpty)
912 : {
913 0 : const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
914 0 : m_aCurrentLine = QuotedTokenizedString();
915 0 : do
916 : {
917 0 : if (pStartPos)
918 0 : *pStartPos = (sal_Int32)m_pFileStream->Tell();
919 0 : m_pFileStream->ReadByteStringLine(m_aCurrentLine, nEncoding);
920 0 : if (m_pFileStream->IsEof())
921 0 : return false;
922 :
923 0 : QuotedTokenizedString sLine = m_aCurrentLine; // check if the string continues on next line
924 0 : while( (comphelper::string::getTokenCount(sLine.GetString(), m_cStringDelimiter) % 2) != 1 )
925 : {
926 0 : m_pFileStream->ReadByteStringLine(sLine,nEncoding);
927 0 : if ( !m_pFileStream->IsEof() )
928 : {
929 0 : OUString aStr = m_aCurrentLine.GetString() + "\n" + sLine.GetString();
930 0 : m_aCurrentLine.SetString(aStr);
931 0 : sLine = m_aCurrentLine;
932 : }
933 : else
934 0 : break;
935 0 : }
936 : }
937 0 : while(nonEmpty && m_aCurrentLine.Len() == 0);
938 :
939 0 : if(pEndPos)
940 0 : *pEndPos = (sal_Int32)m_pFileStream->Tell();
941 0 : return true;
942 : }
943 :
944 :
945 0 : void OFlatTable::setRowPos(const vector<TRowPositionInFile>::size_type rowNum, const TRowPositionInFile &rowPos)
946 : {
947 : assert(m_aRowPosToFilePos.size() >= rowNum);
948 0 : if(m_aRowPosToFilePos.size() == rowNum)
949 0 : m_aRowPosToFilePos.push_back(rowPos);
950 : else
951 : {
952 : SAL_WARN_IF(m_aRowPosToFilePos[rowNum] != rowPos,
953 : "connectivity.flat",
954 : "Setting position for row " << rowNum << " to (" << rowPos.first << ", " << rowPos.second << "), "
955 : "but already had different position (" << m_aRowPosToFilePos[rowNum].first << ", " << m_aRowPosToFilePos[rowNum].second << ")");
956 0 : m_aRowPosToFilePos[rowNum] = rowPos;
957 : }
958 0 : }
959 :
960 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|