/* -*- 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 <strings.hrc>
#include <strings.hxx>
#include <core_resource.hxx>
#include <sqlmessage.hxx>
#include <UITools.hxx>
#include <WColumnSelect.hxx>
#include <WCopyTable.hxx>
#include <WCPage.hxx>
#include <WExtendPages.hxx>
#include <WNameMatch.hxx>
#include <WTypeSelect.hxx>
 
#include <com/sun/star/sdb/application/CopyTableOperation.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XStatement.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbcx/KeyType.hpp>
#include <com/sun/star/sdbcx/XAppend.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XViewsSupplier.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
 
#include <comphelper/interaction.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/dbmetadata.hxx>
#include <connectivity/dbexception.hxx>
#include <o3tl/safeint.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
 
#include <algorithm>
#include <utility>
 
using namespace ::dbaui;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::task;
using namespace dbtools;
 
namespace CopyTableOperation = ::com::sun::star::sdb::application::CopyTableOperation;
 
#define MAX_PAGES   4   // max. number of pages, which are shown
 
namespace
{
    void clearColumns(ODatabaseExport::TColumns& _rColumns, ODatabaseExport::TColumnVector& _rColumnsVec)
    {
        for (auto const& column : _rColumns)
            delete column.second;
 
        _rColumnsVec.clear();
        _rColumns.clear();
    }
}
 
// ICopyTableSourceObject
ICopyTableSourceObject::~ICopyTableSourceObject()
{
}
 
// ObjectCopySource
ObjectCopySource::ObjectCopySource( const Reference< XConnection >& _rxConnection, const Reference< XPropertySet >& _rxObject )
    :m_xConnection( _rxConnection, UNO_SET_THROW )
    ,m_xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW )
    ,m_xObject( _rxObject, UNO_SET_THROW )
    ,m_xObjectPSI( _rxObject->getPropertySetInfo(), UNO_SET_THROW )
    ,m_xObjectColumns( Reference< XColumnsSupplier >( _rxObject, UNO_QUERY_THROW )->getColumns(), UNO_SET_THROW )
{
}
 
OUString ObjectCopySource::getQualifiedObjectName() const
{
    OUString sName;
 
    if ( !m_xObjectPSI->hasPropertyByName( PROPERTY_COMMAND ) )
        sName = ::dbtools::composeTableName( m_xMetaData, m_xObject, ::dbtools::EComposeRule::InDataManipulation, false );
    else
        m_xObject->getPropertyValue( PROPERTY_NAME ) >>= sName;
    return sName;
}
 
bool ObjectCopySource::isView() const
{
    bool bIsView = false;
    try
    {
        if ( m_xObjectPSI->hasPropertyByName( PROPERTY_TYPE ) )
        {
            OUString sObjectType;
            OSL_VERIFY( m_xObject->getPropertyValue( PROPERTY_TYPE ) >>= sObjectType );
            bIsView = sObjectType == "VIEW";
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    return bIsView;
}
 
void ObjectCopySource::copyUISettingsTo( const Reference< XPropertySet >& _rxObject ) const
{
    static constexpr OUString aCopyProperties[] {
        PROPERTY_FONT, PROPERTY_ROW_HEIGHT, PROPERTY_TEXTCOLOR,PROPERTY_TEXTLINECOLOR,PROPERTY_TEXTEMPHASIS,PROPERTY_TEXTRELIEF
    };
    for (const auto & aCopyProperty : aCopyProperties)
    {
        if ( m_xObjectPSI->hasPropertyByName( aCopyProperty ) )
            _rxObject->setPropertyValue( aCopyProperty, m_xObject->getPropertyValue( aCopyProperty ) );
    }
}
 
void ObjectCopySource::copyFilterAndSortingTo( const Reference< XConnection >& _xConnection,const Reference< XPropertySet >& _rxObject ) const
{
    static constexpr std::pair< OUString, OUString > aProperties[] {
                 std::pair< OUString, OUString >(PROPERTY_FILTER,u" AND "_ustr)
                ,std::pair< OUString, OUString >(PROPERTY_ORDER,u" ORDER BY "_ustr)
    };
 
    try
    {
        const OUString sSourceName = ::dbtools::composeTableNameForSelect(m_xConnection,m_xObject) + ".";
        const OUString sTargetName = ::dbtools::composeTableNameForSelect(_xConnection,_rxObject);
        const OUString sTargetNameTemp = sTargetName + ".";
 
        OUStringBuffer sStatement = "SELECT * FROM " + sTargetName + " WHERE 0=1";
 
        for (const std::pair<OUString,OUString> & aProperty : aProperties)
        {
            if ( m_xObjectPSI->hasPropertyByName( aProperty.first ) )
            {
                OUString sFilter;
                m_xObject->getPropertyValue( aProperty.first ) >>= sFilter;
                if ( !sFilter.isEmpty() )
                {
                    sStatement.append(aProperty.second);
                    sFilter = sFilter.replaceFirst(sSourceName,sTargetNameTemp);
                    _rxObject->setPropertyValue( aProperty.first, Any(sFilter) );
                    sStatement.append(sFilter);
                }
            }
        }
 
        _xConnection->createStatement()->executeQuery(sStatement.makeStringAndClear());
 
        if ( m_xObjectPSI->hasPropertyByName( PROPERTY_APPLYFILTER ) )
            _rxObject->setPropertyValue( PROPERTY_APPLYFILTER, m_xObject->getPropertyValue( PROPERTY_APPLYFILTER ) );
    }
    catch(Exception&)
    {
    }
}
 
Sequence< OUString > ObjectCopySource::getColumnNames() const
{
    return m_xObjectColumns->getElementNames();
}
 
Sequence< OUString > ObjectCopySource::getPrimaryKeyColumnNames() const
{
    const Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(m_xObject);
    Sequence< OUString > aKeyColNames;
    if ( xPrimaryKeyColumns.is() )
        aKeyColNames = xPrimaryKeyColumns->getElementNames();
    return aKeyColNames;
}
 
OFieldDescription* ObjectCopySource::createFieldDescription( const OUString& _rColumnName ) const
{
    Reference< XPropertySet > xColumn( m_xObjectColumns->getByName( _rColumnName ), UNO_QUERY_THROW );
    return new OFieldDescription( xColumn );
}
 
OUString ObjectCopySource::getSelectStatement() const
{
    OUString sSelectStatement;
    if ( m_xObjectPSI->hasPropertyByName( PROPERTY_COMMAND ) )
    {   // query
        OSL_VERIFY( m_xObject->getPropertyValue( PROPERTY_COMMAND ) >>= sSelectStatement );
    }
    else
    {   // table
        OUStringBuffer aSQL( "SELECT " );
 
        // we need to create the sql stmt with column names
        // otherwise it is possible that names don't match
        const OUString sQuote = m_xMetaData->getIdentifierQuoteString();
 
        Sequence< OUString > aColumnNames = getColumnNames();
        for (sal_Int32 i = 0; i < aColumnNames.getLength(); ++i)
        {
            if (i > 0)
                aSQL.append(", ");
            aSQL.append(::dbtools::quoteName(sQuote, aColumnNames[i]));
        }
 
        aSQL.append( " FROM " + ::dbtools::composeTableNameForSelect( m_xConnection, m_xObject ) );
 
        sSelectStatement = aSQL.makeStringAndClear();
    }
 
    return sSelectStatement;
}
 
::utl::SharedUNOComponent< XPreparedStatement > ObjectCopySource::getPreparedSelectStatement() const
{
    ::utl::SharedUNOComponent< XPreparedStatement > xStatement(
        m_xConnection->prepareStatement( getSelectStatement() ),
        ::utl::SharedUNOComponent< XPreparedStatement >::TakeOwnership
    );
    return xStatement;
}
 
// NamedTableCopySource
NamedTableCopySource::NamedTableCopySource( const Reference< XConnection >& _rxConnection, OUString _sTableName )
    :m_xConnection( _rxConnection, UNO_SET_THROW )
    ,m_xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW )
    ,m_sTableName(std::move( _sTableName ))
{
    ::dbtools::qualifiedNameComponents( m_xMetaData, m_sTableName, m_sTableCatalog, m_sTableSchema, m_sTableBareName, ::dbtools::EComposeRule::Complete );
    impl_ensureColumnInfo_throw();
}
 
OUString NamedTableCopySource::getQualifiedObjectName() const
{
    return m_sTableName;
}
 
bool NamedTableCopySource::isView() const
{
    OUString sTableType;
    try
    {
        Reference< XResultSet > xTableDesc( m_xMetaData->getTables( Any( m_sTableCatalog ), m_sTableSchema, m_sTableBareName,
            Sequence< OUString >() ) );
        Reference< XRow > xTableDescRow( xTableDesc, UNO_QUERY_THROW );
        OSL_VERIFY( xTableDesc->next() );
        sTableType = xTableDescRow->getString( 4 );
        OSL_ENSURE( !xTableDescRow->wasNull(), "NamedTableCopySource::isView: invalid table type!" );
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    return sTableType == "VIEW";
}
 
void NamedTableCopySource::copyUISettingsTo( const Reference< XPropertySet >& /*_rxObject*/ ) const
{
    // not supported: we do not have UI settings to copy
}
 
void NamedTableCopySource::copyFilterAndSortingTo( const Reference< XConnection >& ,const Reference< XPropertySet >& /*_rxObject*/ ) const
{
}
 
void NamedTableCopySource::impl_ensureColumnInfo_throw()
{
    if ( !m_aColumnInfo.empty() )
        return;
 
    Reference< XResultSetMetaDataSupplier > xStatementMetaSupp( impl_ensureStatement_throw().getTyped(), UNO_QUERY_THROW );
    Reference< XResultSetMetaData > xStatementMeta( xStatementMetaSupp->getMetaData(), UNO_SET_THROW );
 
    sal_Int32 nColCount( xStatementMeta->getColumnCount() );
    for ( sal_Int32 i = 1; i <= nColCount; ++i )
    {
        OFieldDescription aDesc;
 
        aDesc.SetName(          xStatementMeta->getColumnName(      i ) );
        aDesc.SetHelpText(      xStatementMeta->getColumnLabel(     i ) );
        aDesc.SetTypeValue(     xStatementMeta->getColumnType(      i ) );
        aDesc.SetTypeName(      xStatementMeta->getColumnTypeName(  i ) );
        aDesc.SetPrecision(     xStatementMeta->getPrecision(       i ) );
        aDesc.SetScale(         xStatementMeta->getScale(           i ) );
        aDesc.SetIsNullable(    xStatementMeta->isNullable(         i ) );
        aDesc.SetCurrency(      xStatementMeta->isCurrency(         i ) );
        aDesc.SetAutoIncrement( xStatementMeta->isAutoIncrement(    i ) );
 
        m_aColumnInfo.push_back( aDesc );
    }
}
 
::utl::SharedUNOComponent< XPreparedStatement > const & NamedTableCopySource::impl_ensureStatement_throw()
{
    if ( !m_xStatement.is() )
        m_xStatement.set( m_xConnection->prepareStatement( getSelectStatement() ), UNO_SET_THROW );
    return m_xStatement;
}
 
Sequence< OUString > NamedTableCopySource::getColumnNames() const
{
    Sequence< OUString > aNames( m_aColumnInfo.size() );
    std::transform(m_aColumnInfo.begin(), m_aColumnInfo.end(), aNames.getArray(),
                   [](const auto& elem) { return elem.GetName(); });
 
    return aNames;
}
 
Sequence< OUString > NamedTableCopySource::getPrimaryKeyColumnNames() const
{
    Sequence< OUString > aPKColNames;
 
    try
    {
        Reference< XResultSet > xPKDesc( m_xMetaData->getPrimaryKeys( Any( m_sTableCatalog ), m_sTableSchema, m_sTableBareName ) );
        Reference< XRow > xPKDescRow( xPKDesc, UNO_QUERY_THROW );
        while ( xPKDesc->next() )
        {
            sal_Int32 len( aPKColNames.getLength() );
            aPKColNames.realloc( len + 1 );
            aPKColNames.getArray()[ len ] = xPKDescRow->getString( 4 );    // COLUMN_NAME
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    return aPKColNames;
}
 
OFieldDescription* NamedTableCopySource::createFieldDescription( const OUString& _rColumnName ) const
{
    for (auto const& elem : m_aColumnInfo)
        if ( elem.GetName() == _rColumnName )
            return new OFieldDescription(elem);
 
    return nullptr;
}
 
OUString NamedTableCopySource::getSelectStatement() const
{
    return "SELECT * FROM " +
        ::dbtools::composeTableNameForSelect( m_xConnection, m_sTableCatalog, m_sTableSchema, m_sTableBareName );
}
 
::utl::SharedUNOComponent< XPreparedStatement > NamedTableCopySource::getPreparedSelectStatement() const
{
    return const_cast< NamedTableCopySource* >( this )->impl_ensureStatement_throw();
}
 
namespace {
 
// DummyCopySource
class DummyCopySource : public ICopyTableSourceObject
{
public:
    DummyCopySource() { }
 
    static const DummyCopySource& Instance();
 
    // ICopyTableSourceObject overridables
    virtual OUString            getQualifiedObjectName() const override;
    virtual bool                isView() const override;
    virtual void                copyUISettingsTo( const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) const override;
    virtual void                copyFilterAndSortingTo(const css::uno::Reference< css::sdbc::XConnection >& _xConnection, const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) const override;
    virtual css::uno::Sequence< OUString >
                                getColumnNames() const override;
    virtual css::uno::Sequence< OUString >
                                getPrimaryKeyColumnNames() const override;
    virtual OFieldDescription*  createFieldDescription( const OUString& _rColumnName ) const override;
    virtual OUString            getSelectStatement() const override;
    virtual ::utl::SharedUNOComponent< XPreparedStatement >
                                getPreparedSelectStatement() const override;
};
 
}
 
const DummyCopySource& DummyCopySource::Instance()
{
    static DummyCopySource s_aTheInstance;
    return s_aTheInstance;
}
 
OUString DummyCopySource::getQualifiedObjectName() const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::getQualifiedObjectName: not to be called!" );
    return OUString();
}
 
bool DummyCopySource::isView() const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::isView: not to be called!" );
    return false;
}
 
void DummyCopySource::copyUISettingsTo( const Reference< XPropertySet >& /*_rxObject*/ ) const
{
    // no support
}
 
void DummyCopySource::copyFilterAndSortingTo( const Reference< XConnection >& ,const Reference< XPropertySet >& /*_rxObject*/ ) const
{
}
 
Sequence< OUString > DummyCopySource::getColumnNames() const
{
    return Sequence< OUString >();
}
 
Sequence< OUString > DummyCopySource::getPrimaryKeyColumnNames() const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::getPrimaryKeyColumnNames: not to be called!" );
    return Sequence< OUString >();
}
 
OFieldDescription* DummyCopySource::createFieldDescription( const OUString& /*_rColumnName*/ ) const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::createFieldDescription: not to be called!" );
    return nullptr;
}
 
OUString DummyCopySource::getSelectStatement() const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::getSelectStatement: not to be called!" );
    return OUString();
}
 
::utl::SharedUNOComponent< XPreparedStatement > DummyCopySource::getPreparedSelectStatement() const
{
    SAL_WARN("dbaccess.ui",  "DummyCopySource::getPreparedSelectStatement: not to be called!" );
    return ::utl::SharedUNOComponent< XPreparedStatement >();
}
 
namespace
{
    bool lcl_canCreateViewFor_nothrow( const Reference< XConnection >& _rxConnection )
    {
        Reference< XViewsSupplier > xSup( _rxConnection, UNO_QUERY );
        Reference< XDataDescriptorFactory > xViewFac;
        if ( xSup.is() )
            xViewFac.set( xSup->getViews(), UNO_QUERY );
        return xViewFac.is();
    }
 
    bool lcl_sameConnection_throw( const Reference< XConnection >& _rxLHS, const Reference< XConnection >& _rxRHS )
    {
        Reference< XDatabaseMetaData > xMetaLHS( _rxLHS->getMetaData(), UNO_SET_THROW );
        Reference< XDatabaseMetaData > xMetaRHS( _rxRHS->getMetaData(), UNO_SET_THROW );
        return xMetaLHS->getURL() == xMetaRHS->getURL();
    }
}
 
// OCopyTableWizard
OCopyTableWizard::OCopyTableWizard(weld::Window* pParent, const OUString& _rDefaultName, sal_Int16 _nOperation,
        const ICopyTableSourceObject& _rSourceObject, const Reference< XConnection >& _xSourceConnection,
        const Reference< XConnection >& _xConnection, const Reference< XComponentContext >& _rxContext,
        const Reference< XInteractionHandler>& _xInteractionHandler)
    : vcl::RoadmapWizardMachine(pParent)
    , m_mNameMapping(comphelper::UStringMixLess(_xConnection->getMetaData().is() && _xConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()))
    , m_xDestConnection( _xConnection )
    , m_rSourceObject( _rSourceObject )
    , m_xFormatter( getNumberFormatter( _xConnection, _rxContext ) )
    , m_xContext(_rxContext)
    , m_xInteractionHandler(_xInteractionHandler)
    , m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
    , m_nPageCount(0)
    , m_bDeleteSourceColumns(true)
    , m_bInterConnectionCopy( _xSourceConnection != _xConnection )
    , m_sName( _rDefaultName )
    , m_nOperation( _nOperation )
    , m_ePressed( WIZARD_NONE )
    , m_bCreatePrimaryKeyColumn(false)
    , m_bUseHeaderLine(false)
{
    construct();
 
    // extract table name
    OUString sInitialTableName( _rDefaultName );
    try
    {
        m_sSourceName = m_rSourceObject.getQualifiedObjectName();
        OSL_ENSURE( !m_sSourceName.isEmpty(), "OCopyTableWizard::OCopyTableWizard: unable to retrieve the source object's name!" );
 
        if ( sInitialTableName.isEmpty() )
            sInitialTableName = m_sSourceName;
 
        if ( m_sName.isEmpty() )
        {
            if ( _xSourceConnection == m_xDestConnection )
            {
                Reference< XTablesSupplier > xSup( m_xDestConnection, UNO_QUERY_THROW );
                m_sName = ::dbtools::createUniqueName( xSup->getTables(), sInitialTableName, false );
            }
            else
                m_sName = sInitialTableName;
        }
    }
    catch ( const Exception& )
    {
        m_sName = sInitialTableName;
    }
 
    ::dbaui::fillTypeInfo( _xSourceConnection, m_sTypeNames, m_aTypeInfo, m_aTypeInfoIndex );
    ::dbaui::fillTypeInfo( m_xDestConnection, m_sTypeNames, m_aDestTypeInfo, m_aDestTypeInfoIndex );
    loadData( m_rSourceObject, m_vSourceColumns, m_vSourceVec );
 
    bool bAllowViews = true;
    // if the source is a, don't allow creating views
    if ( m_rSourceObject.isView() )
        bAllowViews = false;
    // no views if the target connection does not support creating them
    if ( !lcl_canCreateViewFor_nothrow( m_xDestConnection ) )
        bAllowViews = false;
    // no views if we're copying to a different database
    if ( !lcl_sameConnection_throw( _xSourceConnection, m_xDestConnection ) )
        bAllowViews = false;
 
    if ( m_bInterConnectionCopy )
    {
        Reference< XDatabaseMetaData > xSrcMeta = _xSourceConnection->getMetaData();
        OUString sCatalog;
        OUString sSchema;
        OUString sTable;
        ::dbtools::qualifiedNameComponents( xSrcMeta,
                                            m_sName,
                                            sCatalog,
                                            sSchema,
                                            sTable,
                                            ::dbtools::EComposeRule::InDataManipulation);
 
        m_sName = ::dbtools::composeTableName(m_xDestConnection->getMetaData(),sCatalog,sSchema,sTable,false,::dbtools::EComposeRule::InTableDefinitions);
    }
 
    std::unique_ptr<OCopyTable> xPage1(new OCopyTable(CreatePageContainer(), this));
    xPage1->disallowUseHeaderLine();
    if ( !bAllowViews )
        xPage1->disallowViews();
    xPage1->setCreateStyleAction();
    AddWizardPage(std::move(xPage1));
 
    AddWizardPage( std::make_unique<OWizNameMatching>(CreatePageContainer(), this));
    AddWizardPage( std::make_unique<OWizColumnSelect>(CreatePageContainer(), this));
    AddWizardPage( std::make_unique<OWizNormalExtend>(CreatePageContainer(), this));
    ActivatePage();
 
    m_xAssistant->set_current_page(0);
}
 
weld::Container* OCopyTableWizard::CreatePageContainer()
{
    OUString sIdent(OUString::number(m_nPageCount));
    weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
    return pPageContainer;
}
 
OCopyTableWizard::OCopyTableWizard( weld::Window* pParent, OUString _sDefaultName, sal_Int16 _nOperation,
        ODatabaseExport::TColumns&& _rSourceColumns, const ODatabaseExport::TColumnVector& _rSourceColVec,
        const Reference< XConnection >& _xConnection, const Reference< XNumberFormatter >&  _xFormatter,
        TypeSelectionPageFactory _pTypeSelectionPageFactory, SvStream& _rTypeSelectionPageArg, const Reference< XComponentContext >& _rxContext )
    : vcl::RoadmapWizardMachine(pParent)
    , m_vSourceColumns(std::move(_rSourceColumns))
    , m_mNameMapping(comphelper::UStringMixLess(_xConnection->getMetaData().is() && _xConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()))
    , m_xDestConnection( _xConnection )
    , m_rSourceObject( DummyCopySource::Instance() )
    , m_xFormatter(_xFormatter)
    , m_xContext(_rxContext)
    , m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
    , m_nPageCount(0)
    , m_bDeleteSourceColumns(false)
    , m_bInterConnectionCopy( false )
    , m_sName(std::move(_sDefaultName))
    , m_nOperation( _nOperation )
    , m_ePressed( WIZARD_NONE )
    , m_bCreatePrimaryKeyColumn(false)
    , m_bUseHeaderLine(false)
{
    construct();
    for (auto const& sourceCol : _rSourceColVec)
    {
        m_vSourceVec.emplace_back(m_vSourceColumns.find(sourceCol->first));
    }
 
    ::dbaui::fillTypeInfo( _xConnection, m_sTypeNames, m_aTypeInfo, m_aTypeInfoIndex );
    ::dbaui::fillTypeInfo( _xConnection, m_sTypeNames, m_aDestTypeInfo, m_aDestTypeInfoIndex );
 
    m_xInteractionHandler = InteractionHandler::createWithParent(m_xContext, nullptr);
 
    std::unique_ptr<OCopyTable> xPage1(new OCopyTable(CreatePageContainer(), this));
    xPage1->disallowViews();
    xPage1->setCreateStyleAction();
    AddWizardPage(std::move(xPage1));
 
    AddWizardPage(std::make_unique<OWizNameMatching>(CreatePageContainer(), this));
    AddWizardPage(std::make_unique<OWizColumnSelect>(CreatePageContainer(), this));
    AddWizardPage((*_pTypeSelectionPageFactory)(CreatePageContainer(), this, _rTypeSelectionPageArg));
 
    ActivatePage();
 
    m_xAssistant->set_current_page(0);
}
 
void OCopyTableWizard::construct()
{
    m_xAssistant->set_size_request(700, 350);
 
    m_xPrevPage->set_label(DBA_RES(STR_WIZ_PB_PREV));
    m_xNextPage->set_label(DBA_RES(STR_WIZ_PB_NEXT));
    m_xFinish->set_label(DBA_RES(STR_WIZ_PB_OK));
 
    m_xHelp->show();
    m_xCancel->show();
    m_xPrevPage->show();
    m_xNextPage->show();
    m_xFinish->show();
 
    m_xPrevPage->connect_clicked( LINK( this, OCopyTableWizard, ImplPrevHdl ) );
    m_xNextPage->connect_clicked( LINK( this, OCopyTableWizard, ImplNextHdl ) );
    m_xFinish->connect_clicked( LINK( this, OCopyTableWizard, ImplOKHdl ) );
 
    m_xNextPage->grab_focus();
 
    if (!m_vDestColumns.empty())
        // source is a html or rtf table
        m_xAssistant->change_default_widget(nullptr, m_xNextPage.get());
    else
        m_xAssistant->change_default_widget(nullptr, m_xFinish.get());
 
    m_pTypeInfo = std::make_shared<OTypeInfo>();
    m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';');
    m_bAddPKFirstTime = true;
}
 
OCopyTableWizard::~OCopyTableWizard()
{
    if ( m_bDeleteSourceColumns )
        clearColumns(m_vSourceColumns,m_vSourceVec);
 
    clearColumns(m_vDestColumns,m_aDestVec);
 
    // clear the type information
    m_aTypeInfoIndex.clear();
    m_aTypeInfo.clear();
    m_aDestTypeInfoIndex.clear();
    m_aDestTypeInfo.clear();
}
 
IMPL_LINK_NOARG(OCopyTableWizard, ImplPrevHdl, weld::Button&, void)
{
    m_ePressed = WIZARD_PREV;
    if ( GetCurLevel() )
    {
        if ( getOperation() != CopyTableOperation::AppendData )
        {
            if(GetCurLevel() == 2)
                ShowPage(GetCurLevel()-2);
            else
                ShowPrevPage();
        }
        else
            ShowPrevPage();
    }
}
 
IMPL_LINK_NOARG(OCopyTableWizard, ImplNextHdl, weld::Button&, void)
{
    m_ePressed = WIZARD_NEXT;
    if ( GetCurLevel() < MAX_PAGES )
    {
        if ( getOperation() != CopyTableOperation::AppendData )
        {
            if(GetCurLevel() == 0)
                ShowPage(GetCurLevel()+2);
            else
                ShowNextPage();
        }
        else
            ShowNextPage();
    }
}
 
bool OCopyTableWizard::CheckColumns(sal_Int32& _rnBreakPos)
{
    bool bRet = true;
    m_vColumnPositions.clear();
    m_vColumnTypes.clear();
 
    OSL_ENSURE( m_xDestConnection.is(), "OCopyTableWizard::CheckColumns: No connection!" );
    // If database is able to process PrimaryKeys, set PrimaryKey
    if ( m_xDestConnection.is() )
    {
        bool bPKeyAllowed = supportsPrimaryKey();
 
        bool bContainsColumns = !m_vDestColumns.empty();
 
        if ( bPKeyAllowed && shouldCreatePrimaryKey() )
        {
            // add extra column for the primary key
            TOTypeInfoSP pTypeInfo = queryPrimaryKeyType(m_aDestTypeInfo);
            if ( pTypeInfo )
            {
                if ( m_bAddPKFirstTime )
                {
                    // tdf#114955: since we chose to create a primary key
                    // be sure all other columns won't be in primary key
                    for (auto const& elem : m_vDestColumns)
                        elem.second->SetPrimaryKey(false);
                    OFieldDescription* pField = new OFieldDescription();
                    pField->SetName(m_aKeyName);
                    pField->FillFromTypeInfo(pTypeInfo,true,true);
                    pField->SetAutoIncrement(pTypeInfo->bAutoIncrement);
                    pField->SetPrimaryKey(true);
                    m_bAddPKFirstTime = false;
                    insertColumn(0,pField);
                }
                m_vColumnPositions.emplace_back(1,1);
                m_vColumnTypes.push_back(pTypeInfo->nType);
            }
        }
 
        if ( bContainsColumns )
        {   // we have dest columns so look for the matching column
            for (auto const& elemSource : m_vSourceVec)
            {
                ODatabaseExport::TColumns::const_iterator aDestIter = m_vDestColumns.find(m_mNameMapping[elemSource->first]);
 
                if ( aDestIter != m_vDestColumns.end() )
                {
                    ODatabaseExport::TColumnVector::const_iterator aFind = std::find(m_aDestVec.begin(),m_aDestVec.end(),aDestIter);
                    assert(aFind != m_aDestVec.end());
                    sal_Int32 nPos = (aFind - m_aDestVec.begin())+1;
                    m_vColumnPositions.emplace_back(nPos,nPos);
                    m_vColumnTypes.push_back((*aFind)->second->GetType());
                }
                else
                {
                    m_vColumnPositions.emplace_back( COLUMN_POSITION_NOT_FOUND, COLUMN_POSITION_NOT_FOUND );
                    m_vColumnTypes.push_back(0);
                }
            }
        }
        else
        {
            Reference< XDatabaseMetaData > xMetaData( m_xDestConnection->getMetaData() );
            OUString sExtraChars = xMetaData->getExtraNameCharacters();
            sal_Int32 nMaxNameLen       = getMaxColumnNameLength();
 
            _rnBreakPos=0;
            for (auto const& elemSource : m_vSourceVec)
            {
                OFieldDescription* pField = new OFieldDescription(*elemSource->second);
                pField->SetName(convertColumnName(TExportColumnFindFunctor(&m_vDestColumns),elemSource->first,sExtraChars,nMaxNameLen));
                TOTypeInfoSP pType = convertType(elemSource->second->getSpecialTypeInfo(),bRet);
                pField->SetType(pType);
                if ( !bPKeyAllowed )
                    pField->SetPrimaryKey(false);
 
                // now create a column
                insertColumn(m_vDestColumns.size(),pField);
                m_vColumnPositions.emplace_back(m_vDestColumns.size(),m_vDestColumns.size());
                m_vColumnTypes.push_back(elemSource->second->GetType());
                ++_rnBreakPos;
                if (!bRet)
                    break;
            }
        }
    }
    return bRet;
}
 
IMPL_LINK_NOARG(OCopyTableWizard, ImplOKHdl, weld::Button&, void)
{
    m_ePressed = WIZARD_FINISH;
    bool bFinish = DeactivatePage();
 
    if(!bFinish)
        return;
 
    weld::WaitObject aWait(m_xAssistant.get());
    switch(getOperation())
    {
        case CopyTableOperation::CopyDefinitionAndData:
        case CopyTableOperation::CopyDefinitionOnly:
        {
            bool bOnFirstPage = GetCurLevel() == 0;
            if ( bOnFirstPage )
            {
                // we came from the first page so we have to clear
                // all column information already collected
                clearDestColumns();
                m_mNameMapping.clear();
            }
            sal_Int32 nBreakPos = 0;
            bool bCheckOk = CheckColumns(nBreakPos);
            if ( bOnFirstPage && !bCheckOk )
            {
                showColumnTypeNotSupported(m_vSourceVec[nBreakPos-1]->first);
                OWizTypeSelect* pPage = static_cast<OWizTypeSelect*>(GetPage(3));
                if ( pPage )
                {
                    m_mNameMapping.clear();
                    pPage->setDisplayRow(nBreakPos);
                    ShowPage(3);
                    return;
                }
            }
            if ( m_xDestConnection.is() )
            {
                if ( supportsPrimaryKey() )
                {
                    bool noPrimaryKey = std::none_of(m_vDestColumns.begin(),m_vDestColumns.end(),
                        [] (const ODatabaseExport::TColumns::value_type& tCol) { return tCol.second->IsPrimaryKey(); });
                    if ( noPrimaryKey && m_xInteractionHandler.is() )
                    {
 
                        OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY));
                        SQLContext aError(sMsg, {}, {}, 0, {}, {});
                        ::rtl::Reference xRequest( new ::comphelper::OInteractionRequest( Any( aError ) ) );
                        ::rtl::Reference xYes = new ::comphelper::OInteractionApprove;
                        xRequest->addContinuation( xYes );
                        xRequest->addContinuation( new ::comphelper::OInteractionDisapprove );
                        ::rtl::Reference< ::comphelper::OInteractionAbort > xAbort = new ::comphelper::OInteractionAbort;
                        xRequest->addContinuation( xAbort );
 
                        m_xInteractionHandler->handle( xRequest );
 
                        if ( xYes->wasSelected() )
                        {
                            OCopyTable* pPage = static_cast<OCopyTable*>(GetPage(0));
                            m_bCreatePrimaryKeyColumn = true;
                            m_aKeyName = pPage->GetKeyName();
                            if ( m_aKeyName.isEmpty() )
                                m_aKeyName = "ID";
                            m_aKeyName = createUniqueName( m_aKeyName );
                            sal_Int32 nBreakPos2 = 0;
                            CheckColumns(nBreakPos2);
                        }
                        else if ( xAbort->wasSelected() )
                        {
                            ShowPage(3);
                            return;
                        }
                    }
                }
            }
            break;
        }
        case CopyTableOperation::AppendData:
        case CopyTableOperation::CreateAsView:
            break;
        default:
        {
            SAL_WARN("dbaccess.ui", "OCopyTableWizard::ImplOKHdl: invalid creation style!");
        }
    }
 
    m_xAssistant->response(RET_OK);
}
 
void OCopyTableWizard::setCreatePrimaryKey( bool _bDoCreate, const OUString& _rSuggestedName )
{
    m_bCreatePrimaryKeyColumn = _bDoCreate;
    if ( !_rSuggestedName.isEmpty() )
        m_aKeyName = _rSuggestedName;
 
    OCopyTable* pSettingsPage = dynamic_cast< OCopyTable* >( GetPage( 0 ) );
    OSL_ENSURE( pSettingsPage, "OCopyTableWizard::setCreatePrimaryKey: page should have been added in the ctor!" );
    if ( pSettingsPage )
        pSettingsPage->setCreatePrimaryKey( _bDoCreate, _rSuggestedName );
}
 
void OCopyTableWizard::ActivatePage()
{
    OWizardPage* pCurrent = static_cast<OWizardPage*>(GetPage(GetCurLevel()));
    if (pCurrent)
    {
        bool bFirstTime = pCurrent->IsFirstTime();
        if(bFirstTime)
            pCurrent->Reset();
 
        CheckButtons();
 
        m_xAssistant->set_title(pCurrent->GetTitle());
    }
}
 
void OCopyTableWizard::CheckButtons()
{
    if(GetCurLevel() == 0) // the first page has no back button
    {
        if(m_nPageCount > 1)
            m_xNextPage->set_sensitive(true);
        else
            m_xNextPage->set_sensitive(false);
 
        m_xPrevPage->set_sensitive(false);
    }
    else if(GetCurLevel() == m_nPageCount-1) // the last page has no next button
    {
        m_xNextPage->set_sensitive(false);
        m_xPrevPage->set_sensitive(true);
    }
    else
    {
        m_xPrevPage->set_sensitive(true);
        // next already has its state
    }
}
 
void OCopyTableWizard::EnableNextButton(bool bEnable)
{
    m_xNextPage->set_sensitive(bEnable);
}
 
bool OCopyTableWizard::DeactivatePage()
{
    OWizardPage* pPage = static_cast<OWizardPage*>(GetPage(GetCurLevel()));
    return pPage && pPage->LeavePage();
}
 
void OCopyTableWizard::AddWizardPage(std::unique_ptr<OWizardPage> xPage)
{
    AddPage(std::move(xPage));
    ++m_nPageCount;
}
 
void OCopyTableWizard::insertColumn(sal_Int32 _nPos,OFieldDescription* _pField)
{
    OSL_ENSURE(_pField,"FieldDescrioption is null!");
    if ( !_pField )
        return;
 
    ODatabaseExport::TColumns::const_iterator aFind = m_vDestColumns.find(_pField->GetName());
    if ( aFind != m_vDestColumns.end() )
    {
        delete aFind->second;
        m_vDestColumns.erase(aFind);
    }
 
    m_aDestVec.insert(m_aDestVec.begin() + _nPos,
        m_vDestColumns.emplace(_pField->GetName(),_pField).first);
    m_mNameMapping[_pField->GetName()] = _pField->GetName();
}
 
void OCopyTableWizard::replaceColumn(sal_Int32 _nPos,OFieldDescription* _pField,const OUString& _sOldName)
{
    OSL_ENSURE(_pField,"FieldDescrioption is null!");
    if ( _pField )
    {
        m_vDestColumns.erase(_sOldName);
        OSL_ENSURE( m_vDestColumns.find(_pField->GetName()) == m_vDestColumns.end(),"Column with that name already exist!");
 
        m_aDestVec[_nPos] = m_vDestColumns.emplace(_pField->GetName(),_pField).first;
    }
}
 
void OCopyTableWizard::loadData(  const ICopyTableSourceObject& _rSourceObject, ODatabaseExport::TColumns& _rColumns, ODatabaseExport::TColumnVector& _rColVector )
{
    for (auto const& column : _rColumns)
        delete column.second;
 
    _rColVector.clear();
    _rColumns.clear();
 
    OFieldDescription* pActFieldDescr = nullptr;
    static constexpr OUStringLiteral sCreateParam(u"x");
    // ReadOnly-Flag
    // On drop no line must be editable.
    // On add only empty lines must be editable.
    // On Add and Drop all lines can be edited.
    for (auto& column : _rSourceObject.getColumnNames())
    {
        // get the properties of the column
        pActFieldDescr = _rSourceObject.createFieldDescription(column);
        OSL_ENSURE( pActFieldDescr, "OCopyTableWizard::loadData: illegal field description!" );
        if ( !pActFieldDescr )
            continue;
 
        sal_Int32 nType           = pActFieldDescr->GetType();
        sal_Int32 nScale          = pActFieldDescr->GetScale();
        sal_Int32 nPrecision      = pActFieldDescr->GetPrecision();
        bool bAutoIncrement   = pActFieldDescr->IsAutoIncrement();
        OUString sTypeName = pActFieldDescr->GetTypeName();
 
        // search for type
        bool bForce;
        TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,sCreateParam,nPrecision,nScale,bAutoIncrement,bForce);
        if ( !pTypeInfo )
            pTypeInfo = m_pTypeInfo;
 
        pActFieldDescr->FillFromTypeInfo(pTypeInfo,true,false);
        _rColVector.emplace_back(_rColumns.emplace(pActFieldDescr->GetName(),pActFieldDescr).first);
    }
 
    // determine which columns belong to the primary key
    for (auto& keyColName : _rSourceObject.getPrimaryKeyColumnNames())
    {
        ODatabaseExport::TColumns::const_iterator keyPos = _rColumns.find(keyColName);
        if ( keyPos != _rColumns.end() )
        {
            keyPos->second->SetPrimaryKey( true );
            keyPos->second->SetIsNullable( ColumnValue::NO_NULLS );
        }
    }
}
 
void OCopyTableWizard::clearDestColumns()
{
    clearColumns(m_vDestColumns,m_aDestVec);
    m_bAddPKFirstTime = true;
    m_mNameMapping.clear();
}
 
void OCopyTableWizard::appendColumns( Reference<XColumnsSupplier> const & _rxColSup, const ODatabaseExport::TColumnVector* _pVec, bool _bKeyColumns)
{
    // now append the columns
    OSL_ENSURE(_rxColSup.is(),"No columns supplier");
    if(!_rxColSup.is())
        return;
    Reference<XNameAccess> xColumns = _rxColSup->getColumns();
    OSL_ENSURE(xColumns.is(),"No columns");
    Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY);
 
    Reference<XAppend> xAppend(xColumns,UNO_QUERY);
    OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
 
    for (auto const& elem : *_pVec)
    {
        OFieldDescription* pField = elem->second;
        if(!pField)
            continue;
 
        Reference<XPropertySet> xColumn;
        if(pField->IsPrimaryKey() || !_bKeyColumns)
            xColumn = xColumnFactory->createDataDescriptor();
        if(xColumn.is())
        {
            if(!_bKeyColumns)
                dbaui::setColumnProperties(xColumn,pField);
            else
                xColumn->setPropertyValue(PROPERTY_NAME,Any(pField->GetName()));
 
            xAppend->appendByDescriptor(xColumn);
            xColumn = nullptr;
            // now only the settings are missing
            if(xColumns->hasByName(pField->GetName()))
            {
                xColumn.set(xColumns->getByName(pField->GetName()),UNO_QUERY);
                OSL_ENSURE(xColumn.is(),"OCopyTableWizard::appendColumns: Column is NULL!");
                if ( xColumn.is() )
                    pField->copyColumnSettingsTo(xColumn);
            }
            else
            {
                SAL_WARN("dbaccess.ui", "OCopyTableWizard::appendColumns: invalid field name!");
            }
 
        }
    }
}
 
void OCopyTableWizard::appendKey( Reference<XKeysSupplier> const & _rxSup, const ODatabaseExport::TColumnVector* _pVec)
{
    if(!_rxSup.is())
        return; // the database doesn't support keys
    OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!");
    Reference<XDataDescriptorFactory> xKeyFactory(_rxSup->getKeys(),UNO_QUERY);
    OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!");
    if ( !xKeyFactory.is() )
        return;
    Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY);
    OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
 
    Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor();
    OSL_ENSURE(xKey.is(),"Key is null!");
    xKey->setPropertyValue(PROPERTY_TYPE,Any(KeyType::PRIMARY));
 
    Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY);
    if(xColSup.is())
    {
        appendColumns(xColSup,_pVec,true);
        Reference<XNameAccess> xColumns = xColSup->getColumns();
        if(xColumns.is() && xColumns->getElementNames().hasElements())
            xAppend->appendByDescriptor(xKey);
    }
 
}
 
Reference< XPropertySet > OCopyTableWizard::createView() const
{
    OUString sCommand( m_rSourceObject.getSelectStatement() );
    OSL_ENSURE( !sCommand.isEmpty(), "OCopyTableWizard::createView: no statement in the source object!" );
        // there are legitimate cases in which getSelectStatement does not provide a statement,
        // but in all those cases, this method here should never be called.
    return ::dbaui::createView( m_sName, m_xDestConnection, sCommand );
}
 
Reference< XPropertySet > OCopyTableWizard::returnTable()
{
    if ( getOperation() == CopyTableOperation::AppendData )
        return getTable();
    else
        return createTable();
}
 
Reference< XPropertySet > OCopyTableWizard::getTable() const
{
    Reference< XPropertySet > xTable;
 
    Reference<XTablesSupplier> xSup( m_xDestConnection, UNO_QUERY );
    Reference< XNameAccess > xTables;
    if(xSup.is())
        xTables = xSup->getTables();
    if(xTables.is() && xTables->hasByName(m_sName))
        xTables->getByName(m_sName) >>= xTable;
 
    return xTable;
}
 
Reference< XPropertySet > OCopyTableWizard::createTable()
{
    Reference< XPropertySet > xTable;
 
    Reference<XTablesSupplier> xSup( m_xDestConnection, UNO_QUERY );
    Reference< XNameAccess > xTables;
    if(xSup.is())
        xTables = xSup->getTables();
    Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY);
    OSL_ENSURE(xFact.is(),"No XDataDescriptorFactory available!");
    if(!xFact.is())
        return nullptr;
 
    xTable = xFact->createDataDescriptor();
    OSL_ENSURE(xTable.is(),"Could not create a new object!");
    if(!xTable.is())
        return nullptr;
 
    OUString sCatalog,sSchema,sTable;
    Reference< XDatabaseMetaData> xMetaData = m_xDestConnection->getMetaData();
    ::dbtools::qualifiedNameComponents(xMetaData,
                                       m_sName,
                                       sCatalog,
                                       sSchema,
                                       sTable,
                                       ::dbtools::EComposeRule::InDataManipulation);
 
    if ( sCatalog.isEmpty() && xMetaData->supportsCatalogsInTableDefinitions() )
    {
        sCatalog = m_xDestConnection->getCatalog();
    }
 
    if ( sSchema.isEmpty() && xMetaData->supportsSchemasInTableDefinitions() )
    {
        // query of current schema is quite inconsistent. In case of some
        // DBMS's each user has their own schema.
        sSchema = xMetaData->getUserName();
        // In case of mysql it is not that simple
        if(xMetaData->getDatabaseProductName() == "MySQL")
        {
            Reference< XStatement > xSelect = m_xDestConnection->createStatement();
            Reference< XResultSet > xRs = xSelect->executeQuery(u"select database()"_ustr);
            (void)xRs->next(); // first and only result
            Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
            sSchema = xRow->getString(1);
        }
    }
 
    xTable->setPropertyValue(PROPERTY_CATALOGNAME,Any(sCatalog));
    xTable->setPropertyValue(PROPERTY_SCHEMANAME,Any(sSchema));
    xTable->setPropertyValue(PROPERTY_NAME,Any(sTable));
 
    Reference< XColumnsSupplier > xSuppDestinationColumns( xTable, UNO_QUERY );
    // now append the columns
    const ODatabaseExport::TColumnVector& rVec = getDestVector();
    appendColumns( xSuppDestinationColumns, &rVec );
    // now append the primary key
    Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY);
    appendKey(xKeySup, &rVec);
 
    Reference<XAppend> xAppend(xTables,UNO_QUERY);
    if(xAppend.is())
        xAppend->appendByDescriptor(xTable);
 
    //  xTable = NULL;
    // we need to reget the table because after appending it, it is no longer valid
    if(xTables->hasByName(m_sName))
        xTables->getByName(m_sName) >>= xTable;
    else
    {
        OUString sComposedName(
            ::dbtools::composeTableName( m_xDestConnection->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false ) );
        if(xTables->hasByName(sComposedName))
        {
            xTables->getByName(sComposedName) >>= xTable;
            m_sName = sComposedName;
        }
        else
            xTable = nullptr;
    }
 
    if(xTable.is())
    {
        xSuppDestinationColumns.set( xTable, UNO_QUERY_THROW );
        // insert new table name into table filter
        ::dbaui::appendToFilter(m_xDestConnection, m_sName, GetComponentContext(), m_xAssistant.get());
 
        // copy ui settings
        m_rSourceObject.copyUISettingsTo( xTable );
        //copy filter and sorting
        m_rSourceObject.copyFilterAndSortingTo(m_xDestConnection,xTable);
        // set column mappings
        Reference<XNameAccess> xNameAccess = xSuppDestinationColumns->getColumns();
        Sequence< OUString> aSeq = xNameAccess->getElementNames();
 
        for (sal_Int32 i = 0; i < aSeq.getLength(); ++i)
        {
            ODatabaseExport::TColumns::const_iterator aDestIter = m_vDestColumns.find(aSeq[i]);
 
            if ( aDestIter != m_vDestColumns.end() )
            {
                ODatabaseExport::TColumnVector::const_iterator aFind = std::find(m_aDestVec.begin(),m_aDestVec.end(),aDestIter);
                sal_Int32 nPos = (aFind - m_aDestVec.begin())+1;
 
                ODatabaseExport::TPositions::iterator aPosFind = std::find_if(
                    m_vColumnPositions.begin(),
                    m_vColumnPositions.end(),
                    [nPos] (const ODatabaseExport::TPositions::value_type& tPos) {
                        return tPos.first == nPos;
                    }
                );
 
                if ( m_vColumnPositions.end() != aPosFind )
                {
                    aPosFind->second = i + 1;
                    OSL_ENSURE( m_vColumnTypes.size() > o3tl::make_unsigned( aPosFind - m_vColumnPositions.begin() ),
                        "Invalid index for vector!" );
                    m_vColumnTypes[ aPosFind - m_vColumnPositions.begin() ] = (*aFind)->second->GetType();
                }
            }
        }
    }
 
    return xTable;
}
 
bool OCopyTableWizard::supportsPrimaryKey( const Reference< XConnection >& _rxConnection )
{
    OSL_PRECOND( _rxConnection.is(), "OCopyTableWizard::supportsPrimaryKey: invalid connection!" );
    if ( !_rxConnection.is() )
        return false;
 
    ::dbtools::DatabaseMetaData aMetaData( _rxConnection );
    return aMetaData.supportsPrimaryKeys();
}
 
bool OCopyTableWizard::supportsViews( const Reference< XConnection >& _rxConnection )
{
    OSL_PRECOND( _rxConnection.is(), "OCopyTableWizard::supportsViews: invalid connection!" );
    if ( !_rxConnection.is() )
        return false;
 
    bool bSupportsViews( false );
    try
    {
        Reference< XDatabaseMetaData > xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW );
        Reference< XViewsSupplier > xViewSups( _rxConnection, UNO_QUERY );
        bSupportsViews = xViewSups.is();
        if ( !bSupportsViews )
        {
            try
            {
                Reference< XResultSet > xRs( xMetaData->getTableTypes(), UNO_SET_THROW );
                Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
                while ( xRs->next() )
                {
                    OUString sValue = xRow->getString( 1 );
                    if ( !xRow->wasNull() && sValue.equalsIgnoreAsciiCase("View") )
                    {
                        bSupportsViews = true;
                        break;
                    }
                }
            }
            catch( const SQLException& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    return bSupportsViews;
}
 
sal_Int32 OCopyTableWizard::getMaxColumnNameLength() const
{
    sal_Int32 nLen = 0;
    if ( m_xDestConnection.is() )
    {
        try
        {
            Reference< XDatabaseMetaData > xMetaData( m_xDestConnection->getMetaData(), UNO_SET_THROW );
            nLen = xMetaData->getMaxColumnNameLength();
        }
        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }
    return nLen;
}
 
void OCopyTableWizard::setOperation( const sal_Int16 _nOperation )
{
    m_nOperation = _nOperation;
}
 
 
OUString OCopyTableWizard::convertColumnName(const TColumnFindFunctor&   _rCmpFunctor,
                                                    const OUString&  _sColumnName,
                                                    std::u16string_view  _sExtraChars,
                                                    sal_Int32               _nMaxNameLen)
{
    OUString sAlias = _sColumnName;
    if ( isSQL92CheckEnabled( m_xDestConnection ) )
        sAlias = ::dbtools::convertName2SQLName(_sColumnName,_sExtraChars);
    if((_nMaxNameLen && sAlias.getLength() > _nMaxNameLen) || _rCmpFunctor(sAlias))
    {
        sal_Int32 nDiff = 1;
        do
        {
            ++nDiff;
            if(_nMaxNameLen && sAlias.getLength() >= _nMaxNameLen)
                sAlias = sAlias.copy(0,sAlias.getLength() - (sAlias.getLength()-_nMaxNameLen+nDiff));
 
            OUString sName(sAlias);
            sal_Int32 nPos = 1;
            sName += OUString::number(nPos);
 
            while(_rCmpFunctor(sName))
            {
                sName = sAlias + OUString::number(++nPos);
            }
            sAlias = sName;
            // we have to check again, it could happen that the name is already too long
        }
        while(_nMaxNameLen && sAlias.getLength() > _nMaxNameLen);
    }
    OSL_ENSURE(m_mNameMapping.find(_sColumnName) == m_mNameMapping.end(),"name doubled!");
    m_mNameMapping[_sColumnName] = sAlias;
    return sAlias;
}
 
void OCopyTableWizard::removeColumnNameFromNameMap(const OUString& _sName)
{
    m_mNameMapping.erase(_sName);
}
 
bool OCopyTableWizard::supportsType(sal_Int32 _nDataType,   sal_Int32& _rNewDataType)
{
    bool bRet = m_aDestTypeInfo.find(_nDataType) != m_aDestTypeInfo.end();
    if ( bRet )
        _rNewDataType = _nDataType;
    return bRet;
}
 
TOTypeInfoSP OCopyTableWizard::convertType(const TOTypeInfoSP& _pType, bool& _bNotConvert)
{
    if ( !m_bInterConnectionCopy )
        // no need to convert if the source and destination connection are the same
        return _pType;
 
    bool bForce;
    TOTypeInfoSP pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,_pType->nType,_pType->aTypeName,_pType->aCreateParams,_pType->nPrecision,_pType->nMaximumScale,_pType->bAutoIncrement,bForce);
    if ( !pType || bForce )
    { // no type found so we have to find the correct one ourself
        sal_Int32 nDefaultType = DataType::VARCHAR;
        switch(_pType->nType)
        {
            case DataType::TINYINT:
                if(supportsType(DataType::SMALLINT,nDefaultType))
                    break;
                [[fallthrough]];
            case DataType::SMALLINT:
                if(supportsType(DataType::INTEGER,nDefaultType))
                    break;
                [[fallthrough]];
            case DataType::INTEGER:
                if(supportsType(DataType::FLOAT,nDefaultType))
                    break;
                [[fallthrough]];
            case DataType::FLOAT:
                if(supportsType(DataType::REAL,nDefaultType))
                    break;
                [[fallthrough]];
            case DataType::DATE:
            case DataType::TIME:
                if( DataType::DATE == _pType->nType || DataType::TIME == _pType->nType )
                {
                    if(supportsType(DataType::TIMESTAMP,nDefaultType))
                        break;
                }
                [[fallthrough]];
            case DataType::TIMESTAMP:
            case DataType::REAL:
            case DataType::BIGINT:
                if ( supportsType(DataType::DOUBLE,nDefaultType) )
                    break;
                [[fallthrough]];
            case DataType::DOUBLE:
                if ( supportsType(DataType::NUMERIC,nDefaultType) )
                    break;
                [[fallthrough]];
            case DataType::NUMERIC:
                supportsType(DataType::DECIMAL,nDefaultType);
                break;
            case DataType::DECIMAL:
                if ( supportsType(DataType::NUMERIC,nDefaultType) )
                    break;
                if ( supportsType(DataType::DOUBLE,nDefaultType) )
                    break;
                break;
            case DataType::VARCHAR:
                if ( supportsType(DataType::LONGVARCHAR,nDefaultType) )
                    break;
                break;
            case DataType::LONGVARCHAR:
                if ( supportsType(DataType::CLOB,nDefaultType) )
                    break;
                break;
            case DataType::BINARY:
                if ( supportsType(DataType::VARBINARY,nDefaultType) )
                    break;
                break;
            case DataType::VARBINARY:
                if ( supportsType(DataType::LONGVARBINARY,nDefaultType) )
                    break;
                break;
            case DataType::LONGVARBINARY:
                if ( supportsType(DataType::BLOB,nDefaultType) )
                    break;
                if ( supportsType(DataType::LONGVARCHAR,nDefaultType) )
                    break;
                if ( supportsType(DataType::CLOB,nDefaultType) )
                    break;
                break;
            default:
                nDefaultType = DataType::VARCHAR;
        }
        pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,nDefaultType,_pType->aTypeName,_pType->aCreateParams,_pType->nPrecision,_pType->nMaximumScale,_pType->bAutoIncrement,bForce);
        if ( !pType )
        {
            _bNotConvert = false;
            pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,DataType::VARCHAR,_pType->aTypeName,u"x"_ustr,50,0,false,bForce);
            if ( !pType )
                pType = m_pTypeInfo;
        }
        else if ( bForce )
            _bNotConvert = false;
    }
    return pType;
}
 
OUString OCopyTableWizard::createUniqueName(const OUString& _sName)
{
    OUString sName = _sName;
    Sequence< OUString > aColumnNames( m_rSourceObject.getColumnNames() );
    if ( aColumnNames.hasElements() )
        sName = ::dbtools::createUniqueName( aColumnNames, sName, false );
    else
    {
        if ( m_vSourceColumns.find(sName) != m_vSourceColumns.end())
        {
            sal_Int32 nPos = 0;
            while(m_vSourceColumns.find(sName) != m_vSourceColumns.end())
            {
                sName = _sName + OUString::number(++nPos);
            }
        }
    }
    return sName;
}
 
void OCopyTableWizard::showColumnTypeNotSupported(std::u16string_view _rColumnName)
{
    OUString sMessage( DBA_RES( STR_UNKNOWN_TYPE_FOUND ) );
    sMessage = sMessage.replaceFirst("#1",_rColumnName);
    showError(sMessage);
}
 
void OCopyTableWizard::showError(const OUString& _sErrorMessage)
{
    SQLExceptionInfo aInfo(_sErrorMessage);
    showError(aInfo.get());
}
 
void OCopyTableWizard::showError(const Any& _aError)
{
    if ( _aError.hasValue() && m_xInteractionHandler.is() )
    {
        try
        {
            ::rtl::Reference< ::comphelper::OInteractionRequest > xRequest( new ::comphelper::OInteractionRequest( _aError ) );
            m_xInteractionHandler->handle( xRequest );
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'getSelectStatement' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'WCopyTable.cxx:254', 'WCopyTable.cxx:295', 'WCopyTable.cxx:320', 'WCopyTable.hxx:199'.

V1053 Calling the 'ActivatePage' virtual function in the constructor may lead to unexpected result at runtime.

V1053 Calling the 'ActivatePage' virtual function in the constructor may lead to unexpected result at runtime.

V1048 The 'nDefaultType' variable was assigned the same value.