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