/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include "NDatabaseMetaData.hxx"
#include "NConnection.hxx"
#include "NResultSet.hxx"
#include <propertyids.hxx>
#include <strings.hrc>

#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/sdb/ErrorCondition.hpp>
#include <com/sun/star/sdbc/FetchDirection.hpp>
#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>

#include <comphelper/sequence.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <connectivity/dbexception.hxx>
#include <connectivity/sqlerror.hxx>
#include <rtl/string.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unotools/syslocale.hxx>
#include <unotools/intlwrapper.hxx>
#include <unotools/collatorwrapper.hxx>

#include <cstring>

namespace connectivity::evoab {

using namespace ::comphelper;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::container;
using namespace com::sun::star::io;
namespace ErrorCondition = ::com::sun::star::sdb::ErrorCondition;


OUString SAL_CALL OEvoabResultSet::getImplementationName(  )
{
    return u"com.sun.star.sdbcx.evoab.ResultSet"_ustr;
}

 Sequence< OUString > SAL_CALL OEvoabResultSet::getSupportedServiceNames(  )
{
    return { u"com.sun.star.sdbc.ResultSet"_ustr };
}

sal_Bool SAL_CALL OEvoabResultSet::supportsService( const OUString& _rServiceName )
{
    return cppu::supportsService(this, _rServiceName);
}

struct ComparisonData
{
    const SortDescriptor&   rSortOrder;
    IntlWrapper             aIntlWrapper;

    ComparisonData(const SortDescriptor& _rSortOrder)
        : rSortOrder(_rSortOrder)
        , aIntlWrapper(SvtSysLocale().GetUILanguageTag())
    {
    }
};

static OUString
valueToOUString( GValue& _rValue )
{
    const char *pStr = g_value_get_string( &_rValue );
    std::string_view aStr( pStr ? pStr : "" );
    OUString sResult( OStringToOUString( aStr, RTL_TEXTENCODING_UTF8 ) );
    g_value_unset( &_rValue );
    return sResult;
}

static bool
valueToBool( GValue& _rValue )
{
    bool bResult = g_value_get_boolean( &_rValue );
    g_value_unset( &_rValue );
    return bResult;
}

static int
whichAddress(int value)
{
    int fieldEnum;
    switch (value)
    {
        case HOME_ADDR_LINE1:
        case HOME_ADDR_LINE2:
        case HOME_CITY:
        case HOME_STATE:
        case HOME_COUNTRY:
        case HOME_ZIP:
            fieldEnum = e_contact_field_id("address_home");
            break;

        case WORK_ADDR_LINE1:
        case WORK_ADDR_LINE2:
        case WORK_CITY:
        case WORK_STATE:
        case WORK_COUNTRY:
        case WORK_ZIP:
            fieldEnum = e_contact_field_id("address_work");
            break;

        case OTHER_ADDR_LINE1:
        case OTHER_ADDR_LINE2:
        case OTHER_CITY:
        case OTHER_STATE:
        case OTHER_COUNTRY:
        case OTHER_ZIP:
            fieldEnum = e_contact_field_id("address_other");
            break;

            default: fieldEnum = e_contact_field_id("address_home");
      }
    return fieldEnum;
}

/*
* This function decides the default column values based on the first field of EContactAddress.
* The search order is Work->Home->other(defaults).
*/
static EContactAddress *
getDefaultContactAddress( EContact *pContact,int *value )
{
    EContactAddress *ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(WORK_ADDR_LINE1)));
    if ( ec && (ec->street[0]!='\0') )
    {
        *value= *value +WORK_ADDR_LINE1 -1;
        return ec;
    }
    else
        {
            ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(HOME_ADDR_LINE1)));
            if ( ec && (ec->street[0]!='\0') )
            {
                *value=*value+HOME_ADDR_LINE1-1;
                return ec;
            }
        }

    *value=*value+OTHER_ADDR_LINE1-1;
    return static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(OTHER_ADDR_LINE1)));
}

static EContactAddress*
getContactAddress( EContact *pContact, int * address_enum )
{
    EContactAddress *ec = nullptr;
    switch (*address_enum) {

        case DEFAULT_ADDR_LINE1:
        case DEFAULT_ADDR_LINE2:
        case DEFAULT_CITY:
        case DEFAULT_STATE:
        case DEFAULT_COUNTRY:
        case DEFAULT_ZIP:
            ec = getDefaultContactAddress(pContact,address_enum);break;
           default:
            ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(*address_enum)));
    }
    return ec;
}

static bool
handleSplitAddress( EContact *pContact,GValue *pStackValue, int value )
{
    EContactAddress *ec = getContactAddress(pContact,&value) ;

    if (ec==nullptr)
        return true;

    switch (value) {
        case WORK_ADDR_LINE1:
            g_value_set_string(pStackValue,ec->street ); break;
        case WORK_ADDR_LINE2:
            g_value_set_string(pStackValue,ec->po ); break;
        case WORK_CITY:
            g_value_set_string(pStackValue,ec->locality ); break;
        case WORK_STATE:
             g_value_set_string(pStackValue,ec->region ); break;
        case WORK_COUNTRY:
            g_value_set_string(pStackValue,ec->country ); break;
        case WORK_ZIP:
            g_value_set_string(pStackValue,ec->code ); break;

        case HOME_ADDR_LINE1:
            g_value_set_string(pStackValue,ec->street ); break;
        case HOME_ADDR_LINE2:
            g_value_set_string(pStackValue,ec->po ); break;
        case HOME_CITY:
            g_value_set_string(pStackValue,ec->locality ); break;
        case HOME_STATE:
            g_value_set_string(pStackValue,ec->region ); break;
        case HOME_COUNTRY:
            g_value_set_string(pStackValue,ec->country ); break;
        case HOME_ZIP:
            g_value_set_string(pStackValue,ec->code ); break;

        case OTHER_ADDR_LINE1:
            g_value_set_string(pStackValue,ec->street ); break;
        case OTHER_ADDR_LINE2:
            g_value_set_string(pStackValue,ec->po ); break;
        case OTHER_CITY:
            g_value_set_string(pStackValue,ec->locality ); break;
        case OTHER_STATE:
            g_value_set_string(pStackValue,ec->region ); break;
        case OTHER_COUNTRY:
            g_value_set_string(pStackValue,ec->country ); break;
        case OTHER_ZIP:
            g_value_set_string(pStackValue,ec->code ); break;

    }

    return false;
}

static bool
getValue( EContact* pContact, sal_Int32 nColumnNum, GType nType, GValue* pStackValue, bool& _out_rWasNull )
{
    const ColumnProperty * pSpecs = evoab::getField( nColumnNum );
    if ( !pSpecs )
        return false;

    GParamSpec* pSpec = pSpecs->pField;
    bool bIsSplittedColumn = pSpecs->bIsSplittedValue;

    _out_rWasNull = true;
    if ( !pSpec || !pContact)
        return false;

    if ( G_PARAM_SPEC_VALUE_TYPE (pSpec) != nType )
    {
        SAL_WARN("connectivity.evoab2", "Wrong type (0x" << std::hex << static_cast<int>(G_PARAM_SPEC_VALUE_TYPE(pSpec)) << ") (0x"
                  << std::hex << static_cast<int>(nType) << ") " <<  (pSpec->name ? pSpec->name : "<noname>"));
        return false;
    }

    g_value_init( pStackValue, nType );
    if ( bIsSplittedColumn )
    {
        const SplitEvoColumns* evo_addr( get_evo_addr() );
        for (int i=0;i<OTHER_ZIP;i++)
        {
            if (0 == strcmp (g_param_spec_get_name (pSpec), evo_addr[i].pColumnName))
            {
                _out_rWasNull = handleSplitAddress( pContact, pStackValue, evo_addr[i].value );
                return true;
            }
        }
    }
    else
    {
        g_object_get_property( G_OBJECT (pContact),
                               g_param_spec_get_name (pSpec),
                               pStackValue );
        if ( G_VALUE_TYPE( pStackValue ) != nType )
        {
            SAL_WARN("connectivity.evoab2", "Fetched type mismatch" );
            g_value_unset( pStackValue );
            return false;
        }
    }
    _out_rWasNull = false;
    return true;
}

extern "C" {

static int CompareContacts( gconstpointer _lhs, gconstpointer _rhs, gpointer _userData )
{
    EContact* lhs = const_cast< gpointer >( _lhs );
    EContact* rhs = const_cast< gpointer >( _rhs );

    GValue aLhsValue = { 0, { { 0 } } };
    GValue aRhsValue = { 0, { { 0 } } };
    bool bLhsNull = true;
    bool bRhsNull = true;

    OUString sLhs, sRhs;
    bool bLhs(false), bRhs(false);

    const ComparisonData& rCompData = *static_cast< const ComparisonData* >( _userData );
    for ( const auto& sortCol : rCompData.rSortOrder )
    {
        sal_Int32 nField = sortCol.nField;
        int nOrder = 1;
        // if descending sort, reverse order
        if (!sortCol.bAscending)
            nOrder = -1;
        GType eFieldType = evoab::getGFieldType( nField );

        bool success =  getValue( lhs, nField, eFieldType, &aLhsValue, bLhsNull )
                    &&  getValue( rhs, nField, eFieldType, &aRhsValue, bRhsNull );
        OSL_ENSURE( success, "CompareContacts: could not retrieve both values!" );
        if ( !success )
            return 0;

        if ( bLhsNull && !bRhsNull )
            return -1 * nOrder;
        if ( !bLhsNull && bRhsNull )
            return 1 * nOrder;
        if ( bLhsNull && bRhsNull )
            continue;

        if ( eFieldType == G_TYPE_STRING )
        {
            sLhs = valueToOUString( aLhsValue );
            sRhs = valueToOUString( aRhsValue );
            sal_Int32 nCompResult = rCompData.aIntlWrapper.getCaseCollator()->compareString( sLhs, sRhs );
            if ( nCompResult != 0 )
                return nCompResult * nOrder;
            continue;
        }

        bLhs = valueToBool( aLhsValue );
        bRhs = valueToBool( aRhsValue );
        if ( bLhs && !bRhs )
            return -1 * nOrder;
        if ( !bLhs && bRhs )
            return 1 * nOrder;
        continue;
    }

    return 0;
}

}

OString OEvoabVersionHelper::getUserName( EBook *pBook )
{
    OString aName;
    if( isLDAP( pBook ) )
        aName = e_source_get_property( e_book_get_source( pBook ), "binddn" );
    else
        aName = e_source_get_property( e_book_get_source( pBook ), "user" );
    return aName;
}

namespace {

bool isBookBackend( EBookClient *pBook, const char *backendname)
{
    if (!pBook)
        return false;
    ESource *pSource = e_client_get_source (reinterpret_cast<EClient *>(pBook));
    return isSourceBackend(pSource, backendname);
}

class OEvoabVersion36Helper : public OEvoabVersionHelper
{
private:
    GSList   *m_pContacts;
public:
    OEvoabVersion36Helper()
        : m_pContacts(nullptr)
    {
    }

    virtual ~OEvoabVersion36Helper() override
    {
        freeContacts();
    }

    virtual EBook* openBook(const char *abname) override
    {
        //It would be better if here we had id to begin with, see
        //NDatabaseMetaData.cxx
        const char *id = nullptr;
        GList *pSources = e_source_registry_list_sources(get_e_source_registry(), E_SOURCE_EXTENSION_ADDRESS_BOOK);
        for (GList* liter = pSources; liter; liter = liter->next)
        {
            ESource *pSource = E_SOURCE (liter->data);

            if (strcmp(abname, e_source_get_display_name( pSource )) == 0)
            {
                id = e_source_get_uid( pSource );
                break;
            }
        }
        g_list_foreach (pSources, object_unref, nullptr);
        g_list_free (pSources);
        if (!id)
            return nullptr;

        ESource *pSource = e_source_registry_ref_source(get_e_source_registry(), id);
        EBookClient *pBook = pSource ? createClient (pSource) : nullptr;
        if (pBook && !e_client_open_sync (pBook, true, nullptr, nullptr))
        {
            g_object_unref (G_OBJECT (pBook));
            pBook = nullptr;
        }
        if (pSource)
            g_object_unref (pSource);
        return pBook;
    }

    virtual bool isLDAP( EBook *pBook ) override
    {
        return isBookBackend(pBook, "ldap");
    }

    virtual bool isLocal( EBook *pBook ) override
    {
        return isBookBackend(pBook, "local");
    }

    virtual void freeContacts() override final
    {
        e_client_util_free_object_slist(m_pContacts);
        m_pContacts = nullptr;
    }

    virtual void executeQuery (EBook* pBook, EBookQuery* pQuery) override
    {
        freeContacts();
        char *sexp = e_book_query_to_string( pQuery );
        e_book_client_get_contacts_sync( pBook, sexp, &m_pContacts, nullptr, nullptr );
        g_free (sexp);
    }

    virtual EContact *getContact(sal_Int32 nIndex) override
    {
        gpointer pData = g_slist_nth_data (m_pContacts, nIndex);
        return pData ? E_CONTACT (pData) : nullptr;
    }

    virtual sal_Int32 getNumContacts() override
    {
        return g_slist_length( m_pContacts );
    }

    virtual bool hasContacts() override
    {
        return m_pContacts != nullptr;
    }

    virtual void sortContacts( const ComparisonData& _rCompData ) override
    {
        OSL_ENSURE( !_rCompData.rSortOrder.empty(), "sortContacts: no need to call this without any sort order!" );
        ENSURE_OR_THROW( _rCompData.aIntlWrapper.getCaseCollator(), "no collator for comparing strings" );

        m_pContacts = g_slist_sort_with_data( m_pContacts, &CompareContacts,
            const_cast< gpointer >( static_cast< gconstpointer >( &_rCompData ) ) );
    }

protected:
    virtual EBookClient * createClient( ESource *pSource )
    {
        return e_book_client_new (pSource, nullptr);
    }
};

class OEvoabVersion38Helper : public OEvoabVersion36Helper
{
protected:
    virtual EBookClient * createClient( ESource *pSource ) override
    {
        return e_book_client_connect_direct_sync (get_e_source_registry (), pSource, 10, nullptr, nullptr);
    }
};

}

OEvoabResultSet::OEvoabResultSet( OCommonStatement* pStmt, OEvoabConnection *pConnection )
    :OResultSet_BASE(m_aMutex)
    ,::comphelper::OPropertyContainer( OResultSet_BASE::rBHelper )
    ,m_pStatement(pStmt)
    ,m_pConnection(pConnection)
    ,m_bWasNull(true)
    ,m_nFetchSize(0)
    ,m_nResultSetType(ResultSetType::SCROLL_INSENSITIVE)
    ,m_nFetchDirection(FetchDirection::FORWARD)
    ,m_nResultSetConcurrency(ResultSetConcurrency::READ_ONLY)
    ,m_nIndex(-1)
    ,m_nLength(0)
{
    m_pVersionHelper  = std::make_unique<OEvoabVersion38Helper>();

    registerProperty(
        OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
        PROPERTY_ID_FETCHSIZE,
        PropertyAttribute::READONLY,
        &m_nFetchSize,
        cppu::UnoType<decltype(m_nFetchSize)>::get()
    );
    registerProperty(
        OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
        PROPERTY_ID_RESULTSETTYPE,
        PropertyAttribute::READONLY,
        &m_nResultSetType,
        cppu::UnoType<decltype(m_nResultSetType)>::get()
    );
    registerProperty(
        OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
        PROPERTY_ID_FETCHDIRECTION,
        PropertyAttribute::READONLY,
        &m_nFetchDirection,
        cppu::UnoType<decltype(m_nFetchDirection)>::get()
    );
    registerProperty(
        OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
        PROPERTY_ID_RESULTSETCONCURRENCY,
        PropertyAttribute::READONLY,
        &m_nResultSetConcurrency,
        cppu::UnoType<decltype(m_nResultSetConcurrency)>::get()
    );
}

OEvoabResultSet::~OEvoabResultSet()
{}

void OEvoabResultSet::construct( const QueryData& _rData )
{
    ENSURE_OR_THROW( _rData.getQuery(), "internal error: no EBookQuery" );

    EBook *pBook = m_pVersionHelper->openBook(OUStringToOString(_rData.sTable, RTL_TEXTENCODING_UTF8).getStr());
    if ( !pBook )
        m_pConnection->throwGenericSQLException( STR_CANNOT_OPEN_BOOK, *this );

    m_pVersionHelper->freeContacts();
    bool bExecuteQuery = true;
    switch ( _rData.eFilterType )
    {
        case eFilterNone:
            if ( !m_pVersionHelper->isLocal( pBook ) )
            {
                SQLError aErrorFactory;
                SQLException aAsException = aErrorFactory.getSQLException( ErrorCondition::DATA_CANNOT_SELECT_UNFILTERED, *this );
                m_aWarnings.appendWarning( SQLWarning(
                    aAsException.Message,
                    aAsException.Context,
                    aAsException.SQLState,
                    aAsException.ErrorCode,
                    aAsException.NextException
                ) );
                bExecuteQuery = false;
            }
            break;
        case eFilterAlwaysFalse:
            bExecuteQuery = false;
            break;
        case eFilterOther:
            bExecuteQuery = true;
            break;
    }
    if ( bExecuteQuery )
    {
        m_pVersionHelper->executeQuery(pBook, _rData.getQuery());

        if ( m_pVersionHelper->hasContacts() && !_rData.aSortOrder.empty() )
        {
            ComparisonData aCompData(_rData.aSortOrder);
            m_pVersionHelper->sortContacts(aCompData);
        }
    }
    m_nLength = m_pVersionHelper->getNumContacts();
    SAL_INFO("connectivity.evoab2", "Query return " << m_nLength << " records");
    m_nIndex = -1;

    // create our meta data (need the EBookQuery for this)
    m_xMetaData = new OEvoabResultSetMetaData( _rData.sTable );

    m_xMetaData->setEvoabFields( _rData.xSelectColumns );
}


void OEvoabResultSet::disposing()
{
    ::comphelper::OPropertyContainer::disposing();

    ::osl::MutexGuard aGuard(m_aMutex);
    m_pVersionHelper.reset();
    m_pStatement = nullptr;
    m_xMetaData.clear();
}

Any SAL_CALL OEvoabResultSet::queryInterface( const Type & rType )
{
    Any aRet = ::comphelper::OPropertyContainer::queryInterface(rType);
    if(!aRet.hasValue())
        aRet = OResultSet_BASE::queryInterface(rType);
    return aRet;
}

Sequence< Type > SAL_CALL OEvoabResultSet::getTypes(  )
{
    return ::comphelper::concatSequences(
        OResultSet_BASE::getTypes(),
        getBaseTypes()
    );
}


// XRow Interface

/**
 * getString:
 * @nColumnNum: The column index from the table.
 *
 * If the equivalent NResultSetMetaData.cxx marks the columntype of
 * nColumnNum as DataType::VARCHAR this accessor is used.
 */
OUString SAL_CALL OEvoabResultSet::getString( sal_Int32 nColumnNum )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    OUString aResult;
    if ( m_xMetaData.is())
    {
        sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(nColumnNum);
        GValue aValue = { 0, { { 0 } } };
        if ( getValue( getCur(), nFieldNumber, G_TYPE_STRING, &aValue, m_bWasNull ) )
            aResult = valueToOUString( aValue );
    }
    return aResult;
}

sal_Bool SAL_CALL OEvoabResultSet::getBoolean( sal_Int32 nColumnNum )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    bool bResult = false;

    if ( m_xMetaData.is())
    {
        sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(nColumnNum);
        GValue aValue = { 0, { { 0 } } };
        if ( getValue( getCur(), nFieldNumber, G_TYPE_BOOLEAN, &aValue, m_bWasNull ) )
            bResult = valueToBool( aValue );
    }
    return bResult;
}

sal_Int64 SAL_CALL OEvoabResultSet::getLong( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getLong"_ustr, *this );
    return sal_Int64();
}

Reference< XArray > SAL_CALL OEvoabResultSet::getArray( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getArray"_ustr, *this );
    return nullptr;
}

Reference< XClob > SAL_CALL OEvoabResultSet::getClob( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getClob"_ustr, *this );
    return nullptr;
}

Reference< XBlob > SAL_CALL OEvoabResultSet::getBlob( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getBlob"_ustr, *this );
    return nullptr;
}

Reference< XRef > SAL_CALL OEvoabResultSet::getRef( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getRef"_ustr, *this );
    return nullptr;
}

Any SAL_CALL OEvoabResultSet::getObject( sal_Int32 /*nColumnNum*/, const Reference< css::container::XNameAccess >& /*typeMap*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getObject"_ustr, *this );
    return Any();
}

sal_Int16 SAL_CALL OEvoabResultSet::getShort( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getShort"_ustr, *this );
    return 0;
}

css::util::Time SAL_CALL OEvoabResultSet::getTime( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getTime"_ustr, *this );
    return css::util::Time();
}

util::DateTime SAL_CALL OEvoabResultSet::getTimestamp( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getTimestamp"_ustr, *this );
    return css::util::DateTime();
}

Reference< XInputStream > SAL_CALL OEvoabResultSet::getBinaryStream( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getBinaryStream"_ustr, *this );
    return nullptr;
}

Reference< XInputStream > SAL_CALL OEvoabResultSet::getCharacterStream( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getCharacterStream"_ustr, *this );
    return nullptr;
}

sal_Int8 SAL_CALL OEvoabResultSet::getByte( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getByte"_ustr, *this );
    return 0;
}

Sequence< sal_Int8 > SAL_CALL OEvoabResultSet::getBytes( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getBytes"_ustr, *this );
    return Sequence< sal_Int8 >();
}

css::util::Date SAL_CALL OEvoabResultSet::getDate( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getDate"_ustr, *this );
    return css::util::Date();
}

double SAL_CALL OEvoabResultSet::getDouble( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getDouble"_ustr, *this );
    return 0;
}

float SAL_CALL OEvoabResultSet::getFloat( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getFloat"_ustr, *this );
    return 0;
}

sal_Int32 SAL_CALL OEvoabResultSet::getInt( sal_Int32 /*nColumnNum*/ )
{
    ::dbtools::throwFunctionNotSupportedSQLException( u"XRow::getInt"_ustr, *this );
    return 0;
}
// XRow Interface Ends


// XResultSetMetaDataSupplier Interface
Reference< XResultSetMetaData > SAL_CALL OEvoabResultSet::getMetaData(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    // the meta data should have been created at construction time
    ENSURE_OR_THROW( m_xMetaData.is(), "internal error: no meta data" );
    return m_xMetaData;
}
// XResultSetMetaDataSupplier Interface Ends


// XResultSet Interface
sal_Bool SAL_CALL OEvoabResultSet::next(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    if (m_nIndex+1 < m_nLength) {
        ++m_nIndex ;
        return true;
    }
    else
        return false;
}

sal_Bool SAL_CALL OEvoabResultSet::wasNull(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_bWasNull;
}

sal_Bool SAL_CALL OEvoabResultSet::isBeforeFirst(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_nIndex < 0;
}

sal_Int32 SAL_CALL OEvoabResultSet::getRow(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_nIndex;
}

sal_Bool SAL_CALL OEvoabResultSet::isAfterLast(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_nIndex >= m_nLength;
}

sal_Bool SAL_CALL OEvoabResultSet::isFirst(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_nIndex == 0;
}

sal_Bool SAL_CALL OEvoabResultSet::isLast(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return m_nIndex == m_nLength - 1;
}

void SAL_CALL OEvoabResultSet::beforeFirst(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    m_nIndex = -1;
}

void SAL_CALL OEvoabResultSet::afterLast(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    m_nIndex = m_nLength;
}


sal_Bool SAL_CALL OEvoabResultSet::first(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    m_nIndex = 0;
    return true;
}


sal_Bool SAL_CALL OEvoabResultSet::last(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    m_nIndex = m_nLength - 1;
    return true;
}

sal_Bool SAL_CALL OEvoabResultSet::absolute( sal_Int32 row )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    if (row < m_nLength) {
        m_nIndex = row;
        return true;
    }
    else
        return false;
}

sal_Bool SAL_CALL OEvoabResultSet::relative( sal_Int32 row )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    if ((m_nIndex+row) < m_nLength) {
        m_nIndex += row;
        return true;
    }
    else
        return false;
}

sal_Bool SAL_CALL OEvoabResultSet::previous(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    if(m_nIndex > 0) {
        m_nIndex--;
        return true;
    }
    else
        return false;
}

Reference< XInterface > SAL_CALL OEvoabResultSet::getStatement(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    return cppu::getXWeak(m_pStatement);
}


sal_Bool SAL_CALL OEvoabResultSet::rowDeleted(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return false;
}

sal_Bool SAL_CALL OEvoabResultSet::rowInserted(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return false;
}

sal_Bool SAL_CALL OEvoabResultSet::rowUpdated(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    return false;
}

void SAL_CALL OEvoabResultSet::refreshRow(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
}
//XResult Interface ends

// XCancellable

void SAL_CALL OEvoabResultSet::cancel(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
}

//XCloseable
void SAL_CALL OEvoabResultSet::close(  )
{
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
    }
    dispose();
}

// XWarningsSupplier

void SAL_CALL OEvoabResultSet::clearWarnings(  )
{
    m_aWarnings.clearWarnings();
}

Any SAL_CALL OEvoabResultSet::getWarnings(  )
{
    return m_aWarnings.getWarnings();
}

//XColumnLocate Interface
sal_Int32 SAL_CALL OEvoabResultSet::findColumn( const OUString& columnName )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

    // find the first column with the name columnName
    Reference< XResultSetMetaData > xMeta = getMetaData();
    sal_Int32 nLen = xMeta->getColumnCount();
    sal_Int32 i = 1;
    for(;i<=nLen;++i)
    {
        if(xMeta->isCaseSensitive(i) ? columnName == xMeta->getColumnName(i) :
                columnName.equalsIgnoreAsciiCase(xMeta->getColumnName(i)))
            return i;
    }

    ::dbtools::throwInvalidColumnException( columnName, *this );
    assert(false);
    return 0; // Never reached
}

//XColumnLocate interface ends


::cppu::IPropertyArrayHelper* OEvoabResultSet::createArrayHelper( ) const
{
    Sequence< Property > aProps;
    describeProperties( aProps );
    return new ::cppu::OPropertyArrayHelper( aProps );
}

::cppu::IPropertyArrayHelper & OEvoabResultSet::getInfoHelper()
{
    return *getArrayHelper();
}

void SAL_CALL OEvoabResultSet::acquire() noexcept
{
    OResultSet_BASE::acquire();
}

void SAL_CALL OEvoabResultSet::release() noexcept
{
    OResultSet_BASE::release();
}

css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
OEvoabResultSet::getPropertySetInfo(  )
{
    return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
}


} // connectivity::evoab

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
