/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"

#include <ctype.h>
#include "flat/ETable.hxx"
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/ucb/XContentAccess.hpp>
#include <svl/converter.hxx>
#include "flat/EConnection.hxx"
#include "flat/EColumns.hxx"
#include <osl/thread.h>
#include <tools/config.hxx>
#include <comphelper/sequence.hxx>
#include <svl/zforlist.hxx>
#include <rtl/math.hxx>
#include <stdio.h>      //sprintf
#include <comphelper/extract.hxx>
#include <comphelper/numbers.hxx>
#include "flat/EDriver.hxx"
#include <com/sun/star/util/NumberFormat.hpp>
#include <unotools/configmgr.hxx>
#include <i18npool/mslangid.hxx>
#include "connectivity/dbconversion.hxx"
#include <comphelper/types.hxx>
#include "file/quotedstring.hxx"
#include <unotools/syslocale.hxx>
#include <rtl/logfile.hxx>

using namespace ::comphelper;
using namespace connectivity;
using namespace connectivity::flat;
using namespace connectivity::file;
using namespace ::cppu;
using namespace utl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::lang;

// -------------------------------------------------------------------------
void OFlatTable::fillColumns(const ::com::sun::star::lang::Locale& _aLocale)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::fillColumns" );
    BOOL bRead = TRUE;

    QuotedTokenizedString aHeaderLine;
    OFlatConnection* pConnection = (OFlatConnection*)m_pConnection;
    const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
    const sal_Bool bHasHeaderLine = pConnection->isHeaderLine();
    if ( bHasHeaderLine )
    {
        while(bRead && !aHeaderLine.Len())
        {
            bRead = m_pFileStream->ReadByteStringLine(aHeaderLine,nEncoding);
        }
        m_nStartRowFilePos = m_pFileStream->Tell();
    }

    // read first row
    QuotedTokenizedString aFirstLine;
    bRead = m_pFileStream->ReadByteStringLine(aFirstLine,nEncoding);

    if ( !bHasHeaderLine || !aHeaderLine.Len())
    {
        while(bRead && !aFirstLine.Len())
        {
            bRead = m_pFileStream->ReadByteStringLine(aFirstLine,nEncoding);
        }
        // use first row as headerline because we need the number of columns
        aHeaderLine = aFirstLine;
    }
    // column count
    const xub_StrLen nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter);

    if(!m_aColumns.isValid())
        m_aColumns = new OSQLColumns();
    else
        m_aColumns->get().clear();

    m_aTypes.clear();
    m_aPrecisions.clear();
    m_aScales.clear();
    // reserve some space
    m_aColumns->get().reserve(nFieldCount+1);
    m_aTypes.reserve(nFieldCount+1);
    m_aPrecisions.reserve(nFieldCount+1);
    m_aScales.reserve(nFieldCount+1);

    const sal_Bool bCase = m_pConnection->getMetaData()->storesMixedCaseQuotedIdentifiers();
    CharClass aCharClass(pConnection->getDriver()->getFactory(),_aLocale);
    // read description
    const sal_Unicode cDecimalDelimiter  = pConnection->getDecimalDelimiter();
    const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
    String aColumnName;
    ::rtl::OUString aTypeName;
    ::comphelper::UStringMixEqual aCase(bCase);
    xub_StrLen nStartPosHeaderLine = 0; // use for eficient way to get the tokens
    xub_StrLen nStartPosFirstLine = 0; // use for eficient way to get the tokens
    xub_StrLen nStartPosFirstLine2 = 0;
    for (xub_StrLen i = 0; i < nFieldCount; i++)
    {
        if ( bHasHeaderLine )
        {
            aHeaderLine.GetTokenSpecial(aColumnName,nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter);
            if ( !aColumnName.Len() )
            {
                aColumnName = 'C';
                aColumnName += String::CreateFromInt32(i+1);
            }
        }
        else
        {
            // no column name so ...
            aColumnName = 'C';
            aColumnName += String::CreateFromInt32(i+1);
        }
        sal_Int32 eType;
        UINT16 nPrecision = 0;
        UINT16 nScale = 0;

        BOOL bNumeric = FALSE;
        ULONG  nIndex = 0;

        // first without fielddelimiter
        String aField;
        aFirstLine.GetTokenSpecial(aField,nStartPosFirstLine,m_cFieldDelimiter,'\0');
        if (aField.Len() == 0 ||
            (m_cStringDelimiter && m_cStringDelimiter == aField.GetChar(0)))
        {
            bNumeric = FALSE;
            if ( m_cStringDelimiter != '\0' )
                aFirstLine.GetTokenSpecial(aField,nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
            else
                nStartPosFirstLine2 = nStartPosFirstLine;
        }
        else
        {
            String aField2;
            if ( m_cStringDelimiter != '\0' )
                aFirstLine.GetTokenSpecial(aField2,nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
            else
                aField2 = aField;

            if (aField2.Len() == 0)
            {
                bNumeric = FALSE;
            }
            else
            {
                bNumeric = TRUE;
                xub_StrLen nDot = 0;
                xub_StrLen nDecimalDelCount = 0;
                for (xub_StrLen j = 0; j < aField2.Len(); j++)
                {
                    const sal_Unicode c = aField2.GetChar(j);
                    // nur Ziffern und Dezimalpunkt und Tausender-Trennzeichen?
                    if ( ( !cDecimalDelimiter  || c != cDecimalDelimiter )  &&
                         ( !cThousandDelimiter || c != cThousandDelimiter ) &&
                        !aCharClass.isDigit(aField2,j)                      &&
                        ( j != 0 || (c != '+' && c != '-' ) ) )
                    {
                        bNumeric = FALSE;
                        break;
                    }
                    if (cDecimalDelimiter && c == cDecimalDelimiter)
                    {
                        nPrecision = 15; // we have an decimal value
                        nScale = 2;
                        ++nDecimalDelCount;
                    } // if (cDecimalDelimiter && c == cDecimalDelimiter)
                    if ( c == '.' )
                        ++nDot;
                }

                if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number
                    bNumeric = FALSE;
                if (bNumeric && cThousandDelimiter)
                {
                    // Ist der Trenner richtig angegeben?
                    const String aValue = aField2.GetToken(0,cDecimalDelimiter);
                    for (sal_Int32 j = aValue.Len() - 4; j >= 0; j -= 4)
                    {
                        const sal_Unicode c = aValue.GetChar(static_cast<sal_uInt16>(j));
                        // nur Ziffern und Dezimalpunkt und Tausender-Trennzeichen?
                        if (c == cThousandDelimiter && j)
                            continue;
                        else
                        {
                            bNumeric = FALSE;
                            break;
                        }
                    }
                }

                // jetzt koennte es noch ein Datumsfeld sein
                if (!bNumeric)
                {
                    try
                    {
                        nIndex = m_xNumberFormatter->detectNumberFormat(::com::sun::star::util::NumberFormat::ALL,aField2);
                    }
                    catch(Exception&)
                    {
                    }
                }
            }
        }

        sal_Int32 nFlags = 0;
        if (bNumeric)
        {
            if (cDecimalDelimiter)
            {
                if(nPrecision)
                {
                    eType = DataType::DECIMAL;
                    static const ::rtl::OUString s_sDECIMAL(RTL_CONSTASCII_USTRINGPARAM("DECIMAL"));
                    aTypeName = s_sDECIMAL;
                }
                else
                {
                    eType = DataType::DOUBLE;
                    static const ::rtl::OUString s_sDOUBLE(RTL_CONSTASCII_USTRINGPARAM("DOUBLE"));
                    aTypeName = s_sDOUBLE;
                }
            }
            else
                eType = DataType::INTEGER;
            nFlags = ColumnSearch::BASIC;
        }
        else
        {

            switch (comphelper::getNumberFormatType(m_xNumberFormatter,nIndex))
            {
                case NUMBERFORMAT_DATE:
                    eType = DataType::DATE;
                    {
                        static const ::rtl::OUString s_sDATE(RTL_CONSTASCII_USTRINGPARAM("DATE"));
                        aTypeName = s_sDATE;
                    }
                    break;
                case NUMBERFORMAT_DATETIME:
                    eType = DataType::TIMESTAMP;
                    {
                        static const ::rtl::OUString s_sTIMESTAMP(RTL_CONSTASCII_USTRINGPARAM("TIMESTAMP"));
                        aTypeName = s_sTIMESTAMP;
                    }
                    break;
                case NUMBERFORMAT_TIME:
                    eType = DataType::TIME;
                    {
                        static const ::rtl::OUString s_sTIME(RTL_CONSTASCII_USTRINGPARAM("TIME"));
                        aTypeName = s_sTIME;
                    }
                    break;
                default:
                    eType = DataType::VARCHAR;
                    nPrecision = 0; // nyi: Daten koennen aber laenger sein!
                    nScale = 0;
                    {
                        static const ::rtl::OUString s_sVARCHAR(RTL_CONSTASCII_USTRINGPARAM("VARCHAR"));
                        aTypeName = s_sVARCHAR;
                    }
            };
            nFlags |= ColumnSearch::CHAR;
        }

        // check if the columname already exists
        String aAlias(aColumnName);
        OSQLColumns::Vector::const_iterator aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
        sal_Int32 nExprCnt = 0;
        while(aFind != m_aColumns->get().end())
        {
            (aAlias = aColumnName) += String::CreateFromInt32(++nExprCnt);
            aFind = connectivity::find(m_aColumns->get().begin(),m_aColumns->get().end(),aAlias,aCase);
        }

        sdbcx::OColumn* pColumn = new sdbcx::OColumn(aAlias,aTypeName,::rtl::OUString(),::rtl::OUString(),
                                                ColumnValue::NULLABLE,
                                                nPrecision,
                                                nScale,
                                                eType,
                                                sal_False,
                                                sal_False,
                                                sal_False,
                                                bCase);
        Reference< XPropertySet> xCol = pColumn;
        m_aColumns->get().push_back(xCol);
        m_aTypes.push_back(eType);
        m_aPrecisions.push_back(nPrecision);
        m_aScales.push_back(nScale);
    }
    m_pFileStream->Seek(m_nStartRowFilePos);
}
// -------------------------------------------------------------------------
OFlatTable::OFlatTable(sdbcx::OCollection* _pTables,OFlatConnection* _pConnection,
                    const ::rtl::OUString& _Name,
                    const ::rtl::OUString& _Type,
                    const ::rtl::OUString& _Description ,
                    const ::rtl::OUString& _SchemaName,
                    const ::rtl::OUString& _CatalogName
                ) : OFlatTable_BASE(_pTables,_pConnection,_Name,
                                  _Type,
                                  _Description,
                                  _SchemaName,
                                  _CatalogName)
    ,m_nStartRowFilePos(0)
    ,m_nRowPos(0)
    ,m_nMaxRowCount(0)
    ,m_cStringDelimiter(_pConnection->getStringDelimiter())
    ,m_cFieldDelimiter(_pConnection->getFieldDelimiter())
    ,m_bNeedToReadLine(false)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::OFlatTable" );

}
// -----------------------------------------------------------------------------
void OFlatTable::construct()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::construct" );
    SvtSysLocale aLocale;
    ::com::sun::star::lang::Locale aAppLocale(aLocale.GetLocaleDataPtr()->getLocale());
    Sequence< ::com::sun::star::uno::Any > aArg(1);
    aArg[0] <<= aAppLocale;

    Reference< ::com::sun::star::util::XNumberFormatsSupplier >  xSupplier(m_pConnection->getDriver()->getFactory()->createInstanceWithArguments(::rtl::OUString::createFromAscii("com.sun.star.util.NumberFormatsSupplier"),aArg),UNO_QUERY);
    m_xNumberFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(m_pConnection->getDriver()->getFactory()->createInstance(::rtl::OUString::createFromAscii("com.sun.star.util.NumberFormatter")),UNO_QUERY);
    m_xNumberFormatter->attachNumberFormatsSupplier(xSupplier);
    Reference<XPropertySet> xProp(xSupplier->getNumberFormatSettings(),UNO_QUERY);
    xProp->getPropertyValue(::rtl::OUString::createFromAscii("NullDate")) >>= m_aNullDate;

    INetURLObject aURL;
    aURL.SetURL(getEntry());

    if(aURL.getExtension() != rtl::OUString(m_pConnection->getExtension()))
        aURL.setExtension(m_pConnection->getExtension());

    String aFileName = aURL.GetMainURL(INetURLObject::NO_DECODE);

    m_pFileStream = createStream_simpleError( aFileName,STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYWRITE);

    if(!m_pFileStream)
        m_pFileStream = createStream_simpleError( aFileName,STREAM_READ | STREAM_NOCREATE | STREAM_SHARE_DENYNONE);

    if(m_pFileStream)
    {
        m_pFileStream->Seek(STREAM_SEEK_TO_END);
        sal_Int32 nSize = m_pFileStream->Tell();
        m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN);

        // Buffersize abhaengig von der Filegroesse
        m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 :
                                    nSize > 100000  ? 16384 :
                                    nSize > 10000   ? 4096  : 1024);

        fillColumns(aAppLocale);

        refreshColumns();
    }
}
// -------------------------------------------------------------------------
String OFlatTable::getEntry()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::getEntry" );
    ::rtl::OUString sURL;
    try
    {
        Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet();
        Reference< XRow> xRow(xDir,UNO_QUERY);
        ::rtl::OUString sName;
        ::rtl::OUString sExt;

        INetURLObject aURL;
        xDir->beforeFirst();
        static const ::rtl::OUString s_sSeparator(RTL_CONSTASCII_USTRINGPARAM("/"));
        while(xDir->next())
        {
            sName = xRow->getString(1);
            aURL.SetSmartProtocol(INET_PROT_FILE);
            String sUrl = m_pConnection->getURL() +  s_sSeparator + sName;
            aURL.SetSmartURL( sUrl );

            // cut the extension
            sExt = aURL.getExtension();

            // name and extension have to coincide
            if ( m_pConnection->matchesExtension( sExt ) )
            {
                sName = sName.replaceAt(sName.getLength()-(sExt.getLength()+1),sExt.getLength()+1,::rtl::OUString());
                if ( sName == m_Name )
                {
                    Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
                    sURL = xContentAccess->queryContentIdentifierString();
                    break;
                }
            }
        }
        xDir->beforeFirst(); // move back to before first record
    }
    catch(Exception&)
    {
        OSL_ASSERT(0);
    }
    return sURL.getStr();
}
// -------------------------------------------------------------------------
void OFlatTable::refreshColumns()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::refreshColumns" );
    ::osl::MutexGuard aGuard( m_aMutex );

    TStringVector aVector;
    aVector.reserve(m_aColumns->get().size());

    for(OSQLColumns::Vector::const_iterator aIter = m_aColumns->get().begin();aIter != m_aColumns->get().end();++aIter)
        aVector.push_back(Reference< XNamed>(*aIter,UNO_QUERY)->getName());

    if(m_pColumns)
        m_pColumns->reFill(aVector);
    else
        m_pColumns  = new OFlatColumns(this,m_aMutex,aVector);
}

// -------------------------------------------------------------------------
void SAL_CALL OFlatTable::disposing(void)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::disposing" );
    OFileTable::disposing();
    ::osl::MutexGuard aGuard(m_aMutex);
    m_aColumns = NULL;
}
// -------------------------------------------------------------------------
Sequence< Type > SAL_CALL OFlatTable::getTypes(  ) throw(RuntimeException)
{
    Sequence< Type > aTypes = OTable_TYPEDEF::getTypes();
    ::std::vector<Type> aOwnTypes;
    aOwnTypes.reserve(aTypes.getLength());
    const Type* pBegin = aTypes.getConstArray();
    const Type* pEnd = pBegin + aTypes.getLength();
    for(;pBegin != pEnd;++pBegin)
    {
        if(!(*pBegin == ::getCppuType((const Reference<XKeysSupplier>*)0)   ||
            *pBegin == ::getCppuType((const Reference<XRename>*)0)          ||
            *pBegin == ::getCppuType((const Reference<XIndexesSupplier>*)0) ||
            *pBegin == ::getCppuType((const Reference<XAlterTable>*)0)      ||
            *pBegin == ::getCppuType((const Reference<XDataDescriptorFactory>*)0)))
        {
            aOwnTypes.push_back(*pBegin);
        }
    }
    Type *pTypes = aOwnTypes.empty() ? 0 : &aOwnTypes[0];
    return Sequence< Type >(pTypes, aOwnTypes.size());
}

// -------------------------------------------------------------------------
Any SAL_CALL OFlatTable::queryInterface( const Type & rType ) throw(RuntimeException)
{
    if( rType == ::getCppuType((const Reference<XKeysSupplier>*)0)      ||
        rType == ::getCppuType((const Reference<XIndexesSupplier>*)0)   ||
        rType == ::getCppuType((const Reference<XRename>*)0)            ||
        rType == ::getCppuType((const Reference<XAlterTable>*)0)        ||
        rType == ::getCppuType((const Reference<XDataDescriptorFactory>*)0))
        return Any();

    Any aRet = OTable_TYPEDEF::queryInterface(rType);
    return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< ::com::sun::star::lang::XUnoTunnel*> (this));
}

//--------------------------------------------------------------------------
Sequence< sal_Int8 > OFlatTable::getUnoTunnelImplementationId()
{
    static ::cppu::OImplementationId * pId = 0;
    if (! pId)
    {
        ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
        if (! pId)
        {
            static ::cppu::OImplementationId aId;
            pId = &aId;
        }
    }
    return pId->getImplementationId();
}

// com::sun::star::lang::XUnoTunnel
//------------------------------------------------------------------
sal_Int64 OFlatTable::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::getSomething" );
    return (rId.getLength() == 16 && 0 == rtl_compareMemory(getUnoTunnelImplementationId().getConstArray(),  rId.getConstArray(), 16 ) )
                ? reinterpret_cast< sal_Int64 >( this )
                : OFlatTable_BASE::getSomething(rId);
}
//------------------------------------------------------------------
sal_Bool OFlatTable::fetchRow(OValueRefRow& _rRow,const OSQLColumns & _rCols,sal_Bool bIsTable,sal_Bool bRetrieveData)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::fetchRow" );
    *(_rRow->get())[0] = m_nFilePos;

    if (!bRetrieveData)
        return TRUE;
    if ( m_bNeedToReadLine )
    {
        sal_Int32 nCurrentPos = 0;
        m_pFileStream->Seek(m_nFilePos);
        readLine(nCurrentPos);
        m_bNeedToReadLine = false;
    }

    OFlatConnection* pConnection = (OFlatConnection*)m_pConnection;
    const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
    const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
    // Felder:
    xub_StrLen nStartPos = 0;
    String aStr;
    OSQLColumns::Vector::const_iterator aIter = _rCols.get().begin();
    OSQLColumns::Vector::const_iterator aEnd = _rCols.get().end();
    const OValueRefVector::Vector::size_type nCount = _rRow->get().size();
    for (OValueRefVector::Vector::size_type i = 1; aIter != aEnd && i < nCount;
         ++aIter, i++)
    {
        m_aCurrentLine.GetTokenSpecial(aStr,nStartPos,m_cFieldDelimiter,m_cStringDelimiter);

        if (aStr.Len() == 0)
            (_rRow->get())[i]->setNull();
        else
        {
            // Laengen je nach Datentyp:
            sal_Int32   nLen,
                        nType = 0;
            if(bIsTable)
            {
                nLen    = m_aPrecisions[i-1];
                nType   = m_aTypes[i-1];
            }
            else
            {
                Reference< XPropertySet> xColumn = *aIter;
                xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))  >>= nLen;
                xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))       >>= nType;
            }
            switch(nType)
            {
                case DataType::TIMESTAMP:
                case DataType::DATE:
                case DataType::TIME:
                {
                    try
                    {
                        double nRes = m_xNumberFormatter->convertStringToNumber(::com::sun::star::util::NumberFormat::ALL,aStr);

                        switch(nType)
                        {
                            case DataType::DATE:
                                *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes,m_aNullDate));
                                break;
                            case DataType::TIMESTAMP:
                                *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes,m_aNullDate));
                                break;
                            default:
                                *(_rRow->get())[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes));
                        }
                    }
                    catch(Exception&)
                    {
                        (_rRow->get())[i]->setNull();
                    }
                }   break;
                case DataType::DOUBLE:
                case DataType::INTEGER:
                case DataType::DECIMAL:             // #99178# OJ
                case DataType::NUMERIC:
                {

                    String aStrConverted;
                    if ( DataType::INTEGER != nType )
                    {
                        sal_Unicode* pData = aStrConverted.AllocBuffer(aStr.Len());
                        const sal_Unicode* pStart = pData;

                        OSL_ENSURE(cDecimalDelimiter && nType != DataType::INTEGER ||
                                   !cDecimalDelimiter && nType == DataType::INTEGER,
                                   "FalscherTyp");

                        // In Standard-Notation (DezimalPUNKT ohne Tausender-Komma) umwandeln:
                        for (xub_StrLen j = 0; j < aStr.Len(); ++j)
                        {
                            const sal_Unicode cChar = aStr.GetChar(j);
                            if (cDecimalDelimiter && cChar == cDecimalDelimiter)
                                *pData++ = '.';
                                //aStrConverted.Append( '.' );
                            else if ( cChar == '.' ) // special case, if decimal seperator isn't '.' we have to put the string after it
                                continue; // #99189# OJ
                            else if (cThousandDelimiter && cChar == cThousandDelimiter)
                            {
                                // weglassen
                            }
                            else
                                *pData++ = cChar;
                                //aStrConverted.Append(cChar);
                        } // for (xub_StrLen j = 0; j < aStr.Len(); ++j)
                        aStrConverted.ReleaseBufferAccess(xub_StrLen(pData - pStart));
                    } // if ( DataType::INTEGER != nType )
                    else
                    {
                        aStrConverted = aStr;
                        if ( cThousandDelimiter )
                            aStrConverted.EraseAllChars(cThousandDelimiter);
                    }
                    const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',',',NULL,NULL);

                    // #99178# OJ
                    if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType )
                        *(_rRow->get())[i] = ::rtl::OUString::valueOf(nVal);
                    else
                        *(_rRow->get())[i] = nVal;
                } break;

                default:
                {
                    // Wert als String in Variable der Row uebernehmen
                    *(_rRow->get())[i] = ORowSetValue(aStr);
                }
                break;
            } // switch(nType)
            (_rRow->get())[i]->setTypeKind(nType);
        }
    }
    return sal_True;
}
void OFlatTable::refreshHeader()
{
    m_nRowPos = 0;
}
// -----------------------------------------------------------------------------
sal_Bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::seekRow" );
    OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!");
    // ----------------------------------------------------------
    // Positionierung vorbereiten:
    m_nFilePos = nCurPos;

    switch(eCursorPosition)
    {
        case IResultSetHelper::FIRST:
            m_nRowPos = 0;
            // run through
        case IResultSetHelper::NEXT:
            {
                ++m_nRowPos;
                ::std::map<sal_Int32,TRowPositionsInFile::iterator>::const_iterator aFind = m_aRowPosToFilePos.find(m_nRowPos);
                m_bNeedToReadLine = aFind != m_aRowPosToFilePos.end();
                if ( m_bNeedToReadLine )
                {
                    m_nFilePos  = aFind->second->first;
                    nCurPos     = aFind->second->second;
                } // if ( m_bNeedToReadLine )
                else
                {
                    if ( m_nRowPos == 1 )
                        m_nFilePos = m_nStartRowFilePos;
                    m_pFileStream->Seek(m_nFilePos);
                    if ( m_pFileStream->IsEof() || !readLine(nCurPos) /*|| !checkHeaderLine()*/)
                    {
                        m_nMaxRowCount = m_nRowPos -1;
                        return sal_False;
                    } // if ( m_pFileStream->IsEof() || !readLine(nCurPos) /*|| !checkHeaderLine()*/)

                    TRowPositionsInFile::iterator aPos = m_aFilePosToEndLinePos.insert(TRowPositionsInFile::value_type(m_nFilePos,nCurPos)).first;
                    m_aRowPosToFilePos.insert(::std::map<sal_Int32,TRowPositionsInFile::iterator>::value_type(m_nRowPos,aPos));
                }
            }

            break;
        case IResultSetHelper::PRIOR:
            --m_nRowPos;
            if(m_nRowPos > 0)
            {
                TRowPositionsInFile::iterator aPositions = m_aRowPosToFilePos[m_nRowPos];
                m_nFilePos = aPositions->first;
                nCurPos = aPositions->second;
                m_bNeedToReadLine = true;
            }
            else
                m_nRowPos = 0;

            break;
        case IResultSetHelper::LAST:
            if ( m_nMaxRowCount )
            {
                ::std::map<sal_Int32,TRowPositionsInFile::iterator>::reverse_iterator aLastPos = m_aRowPosToFilePos.rbegin();
                m_nRowPos  = aLastPos->first;
                m_nFilePos = aLastPos->second->first;
                nCurPos    = aLastPos->second->second;

                //m_pFileStream->Seek(m_nFilePos);
                m_bNeedToReadLine = true;
                //if ( m_pFileStream->IsEof() /*|| !checkHeaderLine()*/ || !readLine(nCurPos) )
                //  return sal_False;
            }
            else
            {
                while(seekRow(IResultSetHelper::NEXT,1,nCurPos)) ; // run through after last row
                // now I know all
                seekRow(IResultSetHelper::PRIOR,1,nCurPos);
            }
            break;
        case IResultSetHelper::RELATIVE:
            if(nOffset > 0)
            {
                for(sal_Int32 i = 0;i<nOffset;++i)
                    seekRow(IResultSetHelper::NEXT,1,nCurPos);
            }
            else if(nOffset < 0)
            {
                for(sal_Int32 i = nOffset;i;++i)
                    seekRow(IResultSetHelper::PRIOR,1,nCurPos);
            }
            break;
        case IResultSetHelper::ABSOLUTE:
            {
                if(nOffset < 0)
                    nOffset = m_nRowPos + nOffset;
                ::std::map<sal_Int32,TRowPositionsInFile::iterator>::const_iterator aIter = m_aRowPosToFilePos.find(nOffset);
                if(aIter != m_aRowPosToFilePos.end())
                {
                    m_nFilePos  = aIter->second->first;
                    nCurPos     = aIter->second->second;
                    //m_pFileStream->Seek(m_nFilePos);
                    m_bNeedToReadLine = true;
                    //if ( m_pFileStream->IsEof() /*|| !checkHeaderLine()*/ || !readLine(nCurPos) )
                    //  return sal_False;
                }
                else if(m_nMaxRowCount && nOffset > m_nMaxRowCount) // offset is outside the table
                {
                    m_nRowPos = m_nMaxRowCount;
                    return sal_False;
                }
                else
                {
                    aIter = m_aRowPosToFilePos.upper_bound(nOffset);
                    if(aIter == m_aRowPosToFilePos.end())
                    {
                        ::std::map<sal_Int32,TRowPositionsInFile::iterator>::reverse_iterator aLastPos = m_aRowPosToFilePos.rbegin();
                        m_nRowPos   = aLastPos->first;
                        nCurPos = m_nFilePos = aLastPos->second->first;
                        while(m_nRowPos != nOffset)
                            seekRow(IResultSetHelper::NEXT,1,nCurPos);
                    }
                    else
                    {
                        --aIter;
                        m_nRowPos   = aIter->first;
                        m_nFilePos  = aIter->second->first;
                        nCurPos     = aIter->second->second;
                        //m_pFileStream->Seek(m_nFilePos);
                        m_bNeedToReadLine = true;
                        //if ( m_pFileStream->IsEof() /*|| !checkHeaderLine()*/ || !readLine(nCurPos) )
                        //  return sal_False;
                    }
                }
            }

            break;
        case IResultSetHelper::BOOKMARK:
            {
                TRowPositionsInFile::const_iterator aFind = m_aFilePosToEndLinePos.find(nOffset);
                m_bNeedToReadLine = aFind != m_aFilePosToEndLinePos.end();
                if ( m_bNeedToReadLine )
                {
                    m_nFilePos  = aFind->first;
                    nCurPos = aFind->second;
                }
                else
                {
                    m_nFilePos = nOffset;
                    m_pFileStream->Seek(nOffset);
                    if (m_pFileStream->IsEof() || !readLine(nCurPos) )
                        return sal_False;
                }
                break;
            }
    }

    //nCurPos = m_nFilePos;

    return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool OFlatTable::readLine(sal_Int32& _rnCurrentPos)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "flat", "Ocke.Janssen@sun.com", "OFlatTable::readLine" );
    const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
    m_pFileStream->ReadByteStringLine(m_aCurrentLine,nEncoding);
    if (m_pFileStream->IsEof())
        return sal_False;

    QuotedTokenizedString sLine = m_aCurrentLine; // check if the string continues on next line
    while( (sLine.GetString().GetTokenCount(m_cStringDelimiter) % 2) != 1 )
    {
        m_pFileStream->ReadByteStringLine(sLine,nEncoding);
        if ( !m_pFileStream->IsEof() )
        {
            m_aCurrentLine.GetString().Append('\n');
            m_aCurrentLine.GetString() += sLine.GetString();
            sLine = m_aCurrentLine;
        }
        else
            break;
    }
    _rnCurrentPos = m_pFileStream->Tell();
    return sal_True;
}