/* -*- 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 <UITools.hxx>
#include <sfx2/docfilt.hxx>
#include <core_resource.hxx>
#include <dlgsave.hxx>
#include <defaultobjectnamecheck.hxx>
#include <strings.hxx>
#include <comphelper/extract.hxx>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdb/XSingleSelectQueryAnalyzer.hpp>
#include <com/sun/star/sdb/XCompletedConnection.hpp>
#include <com/sun/star/sdbc/XDataSource.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XViewsSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
#include <com/sun/star/sdbcx/XAppend.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/ucb/InteractiveIOException.hpp>
#include <com/sun/star/sdb/XDocumentDataSource.hpp>
#include <com/sun/star/ucb/IOErrorCode.hpp>
#include <vcl/syswin.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XHierarchicalNameContainer.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/awt/TextAlign.hpp>
#include <TypeInfo.hxx>
#include <FieldDescriptions.hxx>
#include <comphelper/stl_types.hxx>
#include <comphelper/types.hxx>
#include <comphelper/propertysequence.hxx>

#include <svx/svxids.hrc>

#include <sal/log.hxx>
#include <svl/numformat.hxx>
#include <svl/itempool.hxx>
#include <helpids.h>
#include <svl/itemset.hxx>
#include <sbagrid.hrc>
#include <svl/rngitem.hxx>
#include <svl/intitem.hxx>
#include <svx/numinf.hxx>
#include <svl/zforlist.hxx>
#include <dlgattr.hxx>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/util/XNumberFormatter.hpp>
#include <strings.hrc>
#include <sqlmessage.hxx>
#include <dlgsize.hxx>
#include <svtools/editbrowsebox.hxx>
#include <tools/urlobj.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <svl/numuno.hxx>
#include <svl/filenotation.hxx>
#include <connectivity/FValue.hxx>

#include <editeng/justifyitem.hxx>
#include <memory>

namespace dbaui
{
using namespace ::dbtools;
using namespace ::comphelper;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::ui::dialogs;
using namespace ::svt;
using ::com::sun::star::ucb::InteractiveIOException;
using ::com::sun::star::ucb::IOErrorCode_NO_FILE;
using ::com::sun::star::ucb::IOErrorCode_NOT_EXISTING;

SQLExceptionInfo createConnection(  const OUString& _rsDataSourceName,
                                     const Reference< css::container::XNameAccess >& _xDatabaseContext,
                                    const Reference< css::uno::XComponentContext >& _rxContext,
                                    Reference< css::lang::XEventListener> const & _rEvtLst,
                                    Reference< css::sdbc::XConnection>& _rOUTConnection )
{
    Reference<XPropertySet> xProp;
    try
    {
        xProp.set(_xDatabaseContext->getByName(_rsDataSourceName),UNO_QUERY);
    }
    catch(const Exception&)
    {
    }

    return createConnection(xProp,_rxContext,_rEvtLst,_rOUTConnection);
}

SQLExceptionInfo createConnection(  const Reference< css::beans::XPropertySet>& _xDataSource,
                                    const Reference< css::uno::XComponentContext >& _rxContext,
                                    Reference< css::lang::XEventListener> const & _rEvtLst,
                                    Reference< css::sdbc::XConnection>& _rOUTConnection )
{
    SQLExceptionInfo aInfo;
    if ( !_xDataSource.is() )
    {
        SAL_WARN("dbaccess.ui", "createConnection: could not retrieve the data source!");
        return aInfo;
    }

    OUString sPwd, sUser;
    bool bPwdReq = false;
    try
    {
        _xDataSource->getPropertyValue(PROPERTY_PASSWORD) >>= sPwd;
        bPwdReq = ::cppu::any2bool(_xDataSource->getPropertyValue(PROPERTY_ISPASSWORDREQUIRED));
        _xDataSource->getPropertyValue(PROPERTY_USER) >>= sUser;
    }
    catch(const Exception&)
    {
        SAL_WARN("dbaccess.ui", "createConnection: error while retrieving data source properties!");
    }

    try
    {
        if(bPwdReq && sPwd.isEmpty())
        {   // password required, but empty -> connect using an interaction handler
            Reference<XCompletedConnection> xConnectionCompletion(_xDataSource, UNO_QUERY);
            if (!xConnectionCompletion.is())
            {
                SAL_WARN("dbaccess.ui", "createConnection: missing an interface ... need an error message here!");
            }
            else
            {   // instantiate the default SDB interaction handler
                Reference< XInteractionHandler > xHandler = InteractionHandler::createWithParent(_rxContext, nullptr);
                _rOUTConnection = xConnectionCompletion->connectWithCompletion(xHandler);
            }
        }
        else
        {
            Reference<XDataSource> xDataSource(_xDataSource,UNO_QUERY);
            _rOUTConnection = xDataSource->getConnection(sUser, sPwd);
        }
        // be notified when connection is in disposing
        Reference< XComponent >  xComponent(_rOUTConnection, UNO_QUERY);
        if (xComponent.is() && _rEvtLst.is())
            xComponent->addEventListener(_rEvtLst);
    }
    catch(const SQLContext& e) { aInfo = SQLExceptionInfo(e); }
    catch(const SQLWarning& e) { aInfo = SQLExceptionInfo(e); }
    catch(const SQLException& e) { aInfo = SQLExceptionInfo(e); }
    catch(const Exception&) {
        TOOLS_WARN_EXCEPTION("dbaccess.ui", "SbaTableQueryBrowser::OnExpandEntry: could not connect - unknown exception");
    }

    return aInfo;
}

Reference< XDataSource > getDataSourceByName( const OUString& _rDataSourceName,
    weld::Window* _pErrorMessageParent, const Reference< XComponentContext >& _rxContext, ::dbtools::SQLExceptionInfo* _pErrorInfo )
{
    Reference< XDatabaseContext > xDatabaseContext = DatabaseContext::create(_rxContext);

    Reference< XDataSource > xDatasource;
    SQLExceptionInfo aSQLError;
    try
    {
        xDatabaseContext->getByName( _rDataSourceName ) >>= xDatasource;
    }
    catch(const WrappedTargetException& e)
    {
        InteractiveIOException aIOException;
        if  (   ( e.TargetException >>= aIOException )
            &&  (   ( aIOException.Code == IOErrorCode_NO_FILE )
                ||  ( aIOException.Code == IOErrorCode_NOT_EXISTING )
                )
            )
        {
            OUString sErrorMessage( DBA_RES( STR_FILE_DOES_NOT_EXIST ) );
            OFileNotation aTransformer( e.Message );
            sErrorMessage = sErrorMessage.replaceFirst( "$file$", aTransformer.get( OFileNotation::N_SYSTEM ) );
            aSQLError = SQLExceptionInfo( sErrorMessage ).get();
        }
        else
        {
            aSQLError = SQLExceptionInfo( e.TargetException );
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }

    if ( xDatasource.is() )
        return xDatasource;

    if ( aSQLError.isValid() )
    {
        if ( _pErrorInfo )
        {
            *_pErrorInfo = aSQLError;
        }
        else
        {
            showError( aSQLError, _pErrorMessageParent ? _pErrorMessageParent->GetXWindow() : nullptr, _rxContext );
        }
    }

    return Reference<XDataSource>();
}

Reference< XInterface > getDataSourceOrModel(const Reference< XInterface >& _xObject)
{
    Reference< XInterface > xRet;
    Reference<XDocumentDataSource> xDocumentDataSource(_xObject,UNO_QUERY);
    if ( xDocumentDataSource.is() )
        xRet = xDocumentDataSource->getDatabaseDocument();

    if ( !xRet.is() )
    {
        Reference<XOfficeDatabaseDocument> xOfficeDoc(_xObject,UNO_QUERY);
        if ( xOfficeDoc.is() )
            xRet = xOfficeDoc->getDataSource();
    }

    return xRet;
}

TOTypeInfoSP getTypeInfoFromType(const OTypeInfoMap& _rTypeInfo,
                               sal_Int32 _nType,
                               const OUString& _sTypeName,
                               const OUString& _sCreateParams,
                               sal_Int32 _nPrecision,
                               sal_Int32 _nScale,
                               bool _bAutoIncrement,
                               bool& _brForceToType)
{
    TOTypeInfoSP pTypeInfo;
    _brForceToType = false;
    // search for type
    std::pair<OTypeInfoMap::const_iterator, OTypeInfoMap::const_iterator> aPair = _rTypeInfo.equal_range(_nType);
    OTypeInfoMap::const_iterator aIter = aPair.first;
    if(aIter != _rTypeInfo.end()) // compare with end is correct here
    {
        for(;aIter != aPair.second;++aIter)
        {
            // search the best matching type
    #ifdef DBG_UTIL
            OUString sDBTypeName         = aIter->second->aTypeName;         (void)sDBTypeName;
    #endif
            if  (   (
                        _sTypeName.isEmpty()
                    ||  (aIter->second->aTypeName.equalsIgnoreAsciiCase(_sTypeName))
                    )
                &&  (
                        (
                                !aIter->second->aCreateParams.getLength()
                            &&  _sCreateParams.isEmpty()
                        )
                    ||  (
                                (aIter->second->nPrecision      >= _nPrecision)
                            &&  (aIter->second->nMaximumScale   >= _nScale)
                            &&  ( (_bAutoIncrement && aIter->second->bAutoIncrement) || !_bAutoIncrement )
                        )
                    )
                )
                break;
        }

        if (aIter == aPair.second)
        {
            for(aIter = aPair.first; aIter != aPair.second; ++aIter)
            {
                sal_Int32 nPrec = aIter->second->nPrecision;
                sal_Int32 nScale = aIter->second->nMaximumScale;
                // search the best matching type (now comparing the local names)
                if  (   (aIter->second->aLocalTypeName.equalsIgnoreAsciiCase(_sTypeName))
                    &&  (nPrec  >= _nPrecision)
                    &&  (nScale >= _nScale)
                    &&  ( (_bAutoIncrement && aIter->second->bAutoIncrement) || !_bAutoIncrement )
                    )
                {
                    SAL_WARN("dbaccess.ui", "getTypeInfoFromType: assuming column type " <<
                             aIter->second->aTypeName <<  "\" (expected type name " <<
                             _sTypeName << " matches the type's local name).");
                    break;
                }
            }
        }

        if (aIter == aPair.second)
        {   // no match for the names, no match for the local names
            // -> drop the precision and the scale restriction, accept any type with the property
            // type id (nType)

            for(aIter = aPair.first; aIter != aPair.second; ++aIter)
            {
                // search the best matching type (now comparing the local names)
                sal_Int32 nPrec = aIter->second->nPrecision;
                sal_Int32 nScale = aIter->second->nMaximumScale;
                if  (   (nPrec  >= _nPrecision)
                    &&  (nScale >= _nScale)
                    &&  ( (_bAutoIncrement && aIter->second->bAutoIncrement) || !_bAutoIncrement )
                    )
                    break;
            }
        }
        if (aIter == aPair.second)
        {
            if ( _bAutoIncrement )
            {
                for(aIter = aPair.first; aIter != aPair.second; ++aIter)
                {
                    // search the best matching type (now comparing the local names)
                    sal_Int32 nScale = aIter->second->nMaximumScale;
                    if  (   (nScale >= _nScale)
                        &&  (aIter->second->bAutoIncrement  == _bAutoIncrement)
                        )
                        break;
                }
                if ( aIter == aPair.second )
                {
                    // try it without the auto increment flag
                    pTypeInfo = getTypeInfoFromType(_rTypeInfo,
                                   _nType,
                                   _sTypeName,
                                   _sCreateParams,
                                   _nPrecision,
                                   _nScale,
                                   false,
                                   _brForceToType);
                }
                else
                    pTypeInfo = aIter->second;
            }
            else
            {
                pTypeInfo = aPair.first->second;
                _brForceToType = true;
            }
        }
        else
            pTypeInfo = aIter->second;
    }
    else
    {
        ::comphelper::UStringMixEqual aCase(false);
        // search for typeinfo where the typename is equal _sTypeName
        for (auto const& elem : _rTypeInfo)
        {
            if ( aCase( elem.second->getDBName() , _sTypeName ) )
            {
                pTypeInfo = elem.second;
                break;
            }
        }
    }

    OSL_ENSURE(pTypeInfo, "getTypeInfoFromType: no type info found for this type!");
    return pTypeInfo;
}

void fillTypeInfo(  const Reference< css::sdbc::XConnection>& _rxConnection,
                    std::u16string_view _rsTypeNames,
                    OTypeInfoMap& _rTypeInfoMap,
                    std::vector<OTypeInfoMap::iterator>& _rTypeInfoIters)
{
    if(!_rxConnection.is())
        return;
    Reference< XResultSet> xRs = _rxConnection->getMetaData ()->getTypeInfo ();
    Reference< XRow> xRow(xRs,UNO_QUERY);
    // Information for a single SQL type
    if(!xRs.is())
        return;

    Reference<XResultSetMetaData> xResultSetMetaData = Reference<XResultSetMetaDataSupplier>(xRs,UNO_QUERY_THROW)->getMetaData();
    ::connectivity::ORowSetValue aValue;
    std::vector<sal_Int32> aTypes;
    std::vector<bool> aNullable;
    // Loop on the result set until we reach end of file
    while (xRs->next())
    {
        TOTypeInfoSP pInfo = std::make_shared<OTypeInfo>();
        sal_Int32 nPos = 1;
        if ( aTypes.empty() )
        {
            sal_Int32 nCount = xResultSetMetaData->getColumnCount();
            if ( nCount < 1 )
                nCount = 18;
            aTypes.reserve(nCount+1);
            aTypes.push_back(-1);
            aNullable.push_back(false);
            for (sal_Int32 j = 1; j <= nCount ; ++j)
            {
                aTypes.push_back(xResultSetMetaData->getColumnType(j));
                aNullable.push_back(xResultSetMetaData->isNullable(j) != ColumnValue::NO_NULLS);
            }
        }

        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->aTypeName        = aValue.getString();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nType            = aValue.getInt32();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nPrecision       = aValue.getInt32();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow); // LiteralPrefix
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow); //LiteralSuffix
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->aCreateParams    = aValue.getString();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->bNullable        = aValue.getInt32() == ColumnValue::NULLABLE;
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        // bCaseSensitive
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nSearchType      = aValue.getInt16();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        // bUnsigned
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->bCurrency        = aValue.getBool();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->bAutoIncrement   = aValue.getBool();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->aLocalTypeName   = aValue.getString();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nMinimumScale    = aValue.getInt16();
        ++nPos;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nMaximumScale    = aValue.getInt16();
        assert(nPos == 15);
        // 16 and 17 are unused
        nPos = 18;
        aValue.fill(nPos,aTypes[nPos],aNullable[nPos],xRow);
        pInfo->nNumPrecRadix    = aValue.getInt32();

        // check if values are less than zero like it happens in a oracle jdbc driver
        if( pInfo->nPrecision < 0)
            pInfo->nPrecision = 0;
        if( pInfo->nMinimumScale < 0)
            pInfo->nMinimumScale = 0;
        if( pInfo->nMaximumScale < 0)
            pInfo->nMaximumScale = 0;
        if( pInfo->nNumPrecRadix <= 1)
            pInfo->nNumPrecRadix = 10;

        std::u16string_view aName;
        switch(pInfo->nType)
        {
            case DataType::CHAR:
                aName = o3tl::getToken(_rsTypeNames, TYPE_CHAR, ';');
                break;
            case DataType::VARCHAR:
                aName = o3tl::getToken(_rsTypeNames, TYPE_TEXT, ';');
                break;
            case DataType::DECIMAL:
                aName = o3tl::getToken(_rsTypeNames, TYPE_DECIMAL, ';');
                break;
            case DataType::NUMERIC:
                aName = o3tl::getToken(_rsTypeNames, TYPE_NUMERIC, ';');
                break;
            case DataType::BIGINT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_BIGINT, ';');
                break;
            case DataType::FLOAT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_FLOAT, ';');
                break;
            case DataType::DOUBLE:
                aName = o3tl::getToken(_rsTypeNames, TYPE_DOUBLE, ';');
                break;
            case DataType::LONGVARCHAR:
                aName = o3tl::getToken(_rsTypeNames, TYPE_MEMO, ';');
                break;
            case DataType::LONGVARBINARY:
                aName = o3tl::getToken(_rsTypeNames, TYPE_IMAGE, ';');
                break;
            case DataType::DATE:
                aName = o3tl::getToken(_rsTypeNames, TYPE_DATE, ';');
                break;
            case DataType::TIME:
                aName = o3tl::getToken(_rsTypeNames, TYPE_TIME, ';');
                break;
            case DataType::TIMESTAMP:
                aName = o3tl::getToken(_rsTypeNames, TYPE_DATETIME, ';');
                break;
            case DataType::BIT:
                if ( !pInfo->aCreateParams.isEmpty() )
                {
                    aName = o3tl::getToken(_rsTypeNames, TYPE_BIT, ';');
                    break;
                }
                [[fallthrough]];
            case DataType::BOOLEAN:
                aName = o3tl::getToken(_rsTypeNames, TYPE_BOOL, ';');
                break;
            case DataType::TINYINT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_TINYINT, ';');
                break;
            case DataType::SMALLINT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_SMALLINT, ';');
                break;
            case DataType::INTEGER:
                aName = o3tl::getToken(_rsTypeNames, TYPE_INTEGER, ';');
                break;
            case DataType::REAL:
                aName = o3tl::getToken(_rsTypeNames, TYPE_REAL, ';');
                break;
            case DataType::BINARY:
                aName = o3tl::getToken(_rsTypeNames, TYPE_BINARY, ';');
                break;
            case DataType::VARBINARY:
                aName = o3tl::getToken(_rsTypeNames, TYPE_VARBINARY, ';');
                break;
            case DataType::SQLNULL:
                aName = o3tl::getToken(_rsTypeNames, TYPE_SQLNULL, ';');
                break;
            case DataType::OBJECT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_OBJECT, ';');
                break;
            case DataType::DISTINCT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_DISTINCT, ';');
                break;
            case DataType::STRUCT:
                aName = o3tl::getToken(_rsTypeNames, TYPE_STRUCT, ';');
                break;
            case DataType::ARRAY:
                aName = o3tl::getToken(_rsTypeNames, TYPE_ARRAY, ';');
                break;
            case DataType::BLOB:
                aName = o3tl::getToken(_rsTypeNames, TYPE_BLOB, ';');
                break;
            case DataType::CLOB:
                aName = o3tl::getToken(_rsTypeNames, TYPE_CLOB, ';');
                break;
            case DataType::REF:
                aName = o3tl::getToken(_rsTypeNames, TYPE_REF, ';');
                break;
            case DataType::OTHER:
                aName = o3tl::getToken(_rsTypeNames, TYPE_OTHER, ';');
                break;
        }
        if ( !aName.empty() )
        {
            pInfo->aUIName = aName;
            pInfo->aUIName += " [ ";
        }
        pInfo->aUIName += pInfo->aTypeName;
        if ( !aName.empty() )
            pInfo->aUIName += " ]";
        // Now that we have the type info, save it in the multimap
        _rTypeInfoMap.emplace(pInfo->nType,pInfo);
    }
    // for a faster index access
    _rTypeInfoIters.reserve(_rTypeInfoMap.size());

    OTypeInfoMap::iterator aIter = _rTypeInfoMap.begin();
    OTypeInfoMap::const_iterator aEnd = _rTypeInfoMap.end();
    for(;aIter != aEnd;++aIter)
        _rTypeInfoIters.push_back(aIter);

    // Close the result set/statement.

    ::comphelper::disposeComponent(xRs);
}

void setColumnProperties(const Reference<XPropertySet>& _rxColumn,const OFieldDescription* _pFieldDesc)
{
    _rxColumn->setPropertyValue(PROPERTY_NAME,Any(_pFieldDesc->GetName()));
    _rxColumn->setPropertyValue(PROPERTY_TYPENAME,Any(_pFieldDesc->getTypeInfo()->aTypeName));
    _rxColumn->setPropertyValue(PROPERTY_TYPE,Any(_pFieldDesc->GetType()));
    _rxColumn->setPropertyValue(PROPERTY_PRECISION,Any(_pFieldDesc->GetPrecision()));
    _rxColumn->setPropertyValue(PROPERTY_SCALE,Any(_pFieldDesc->GetScale()));
    _rxColumn->setPropertyValue(PROPERTY_ISNULLABLE, Any(_pFieldDesc->GetIsNullable()));
    _rxColumn->setPropertyValue(PROPERTY_ISAUTOINCREMENT, css::uno::Any(_pFieldDesc->IsAutoIncrement()));
    _rxColumn->setPropertyValue(PROPERTY_DESCRIPTION,Any(_pFieldDesc->GetDescription()));
    if ( _rxColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_ISCURRENCY) && _pFieldDesc->IsCurrency() )
        _rxColumn->setPropertyValue(PROPERTY_ISCURRENCY, css::uno::Any(_pFieldDesc->IsCurrency()));
    // set autoincrement value when available
    // and only set when the entry is not empty, that lets the value in the column untouched
    if ( _pFieldDesc->IsAutoIncrement() && !_pFieldDesc->GetAutoIncrementValue().isEmpty() && _rxColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_AUTOINCREMENTCREATION) )
        _rxColumn->setPropertyValue(PROPERTY_AUTOINCREMENTCREATION,Any(_pFieldDesc->GetAutoIncrementValue()));
}

OUString createDefaultName(const Reference< XDatabaseMetaData>& _xMetaData,const Reference<XNameAccess>& _xTables,const OUString& _sName)
{
    OSL_ENSURE(_xMetaData.is(),"No MetaData!");
    OUString sDefaultName = _sName;
    try
    {
        OUString sCatalog,sSchema,sCompsedName;
        if(_xMetaData->supportsCatalogsInTableDefinitions())
        {
            try
            {
                Reference< XConnection> xCon = _xMetaData->getConnection();
                if ( xCon.is() )
                    sCatalog = xCon->getCatalog();
                if ( sCatalog.isEmpty() )
                {
                    Reference<XResultSet> xRes = _xMetaData->getCatalogs();
                    Reference<XRow> xRow(xRes,UNO_QUERY);
                    while(xRes.is() && xRes->next())
                    {
                        sCatalog = xRow->getString(1);
                        if(!xRow->wasNull())
                            break;
                    }
                }
            }
            catch(const SQLException&)
            {
            }
        }
        if(_xMetaData->supportsSchemasInTableDefinitions())
        {
            sSchema = _xMetaData->getUserName();
        }
        sCompsedName = ::dbtools::composeTableName( _xMetaData, sCatalog, sSchema, _sName, false, ::dbtools::EComposeRule::InDataManipulation );
        sDefaultName = ::dbtools::createUniqueName(_xTables,sCompsedName);
    }
    catch(const SQLException&)
    {
    }
    return sDefaultName;
}

bool checkDataSourceAvailable(const OUString& _sDataSourceName,const Reference< css::uno::XComponentContext >& _xContext)
{
    Reference< XDatabaseContext > xDataBaseContext = DatabaseContext::create(_xContext);
    bool bRet = xDataBaseContext->hasByName(_sDataSourceName);
    if ( !bRet )
    { // try if this one is a URL
        try
        {
            bRet = xDataBaseContext->getByName(_sDataSourceName).hasValue();
        }
        catch(const Exception&)
        {
        }
    }
    return bRet;
}

sal_Int32 mapTextAlign(const SvxCellHorJustify& _eAlignment)
{
    sal_Int32 nAlignment = css::awt::TextAlign::LEFT;
    switch (_eAlignment)
    {
        case SvxCellHorJustify::Standard:
        case SvxCellHorJustify::Left:      nAlignment = css::awt::TextAlign::LEFT;    break;
        case SvxCellHorJustify::Center:    nAlignment = css::awt::TextAlign::CENTER;  break;
        case SvxCellHorJustify::Right:     nAlignment = css::awt::TextAlign::RIGHT;   break;
        default:
            SAL_WARN("dbaccess.ui", "Invalid TextAlign!");
    }
    return nAlignment;
}

SvxCellHorJustify mapTextJustify(sal_Int32 _nAlignment)
{
    SvxCellHorJustify eJustify = SvxCellHorJustify::Left;
    switch (_nAlignment)
    {
        case css::awt::TextAlign::LEFT     : eJustify = SvxCellHorJustify::Left; break;
        case css::awt::TextAlign::CENTER   : eJustify = SvxCellHorJustify::Center; break;
        case css::awt::TextAlign::RIGHT    : eJustify = SvxCellHorJustify::Right; break;
        default:
            SAL_WARN("dbaccess.ui", "Invalid TextAlign!");
    }
    return eJustify;
}

void callColumnFormatDialog(const Reference<XPropertySet>& xAffectedCol,
                            const Reference<XPropertySet>& xField,
                            SvNumberFormatter* _pFormatter,
                            weld::Widget* _pParent)
{
    if (!(xAffectedCol.is() && xField.is()))
        return;

    try
    {
        Reference< XPropertySetInfo >  xInfo = xAffectedCol->getPropertySetInfo();
        bool bHasFormat = xInfo->hasPropertyByName(PROPERTY_FORMATKEY);
        sal_Int32 nDataType = ::comphelper::getINT32(xField->getPropertyValue(PROPERTY_TYPE));

        SvxCellHorJustify eJustify(SvxCellHorJustify::Standard);
        Any aAlignment = xAffectedCol->getPropertyValue(PROPERTY_ALIGN);
        if (aAlignment.hasValue())
            eJustify = dbaui::mapTextJustify(::comphelper::getINT16(aAlignment));
        sal_Int32  nFormatKey = 0;
        if ( bHasFormat )
            nFormatKey = ::comphelper::getINT32(xAffectedCol->getPropertyValue(PROPERTY_FORMATKEY));

        if(callColumnFormatDialog(_pParent,_pFormatter,nDataType,nFormatKey,eJustify,bHasFormat))
        {
            xAffectedCol->setPropertyValue(PROPERTY_ALIGN, Any(static_cast<sal_Int16>(dbaui::mapTextAlign(eJustify))));
            if (bHasFormat)
                xAffectedCol->setPropertyValue(PROPERTY_FORMATKEY, Any(nFormatKey));

        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}

bool callColumnFormatDialog(weld::Widget* _pParent,
                                SvNumberFormatter* _pFormatter,
                                sal_Int32 _nDataType,
                                sal_Int32& _nFormatKey,
                                SvxCellHorJustify& _eJustify,
                                bool  _bHasFormat)
{
    bool bRet = false;

    // UNO->ItemSet
    static SfxItemInfo aItemInfos[] =
    {
        { 0, false },
        { SID_ATTR_NUMBERFORMAT_VALUE,      true },
        { SID_ATTR_ALIGN_HOR_JUSTIFY,       true },
        { SID_ATTR_NUMBERFORMAT_INFO,       true },
        { SID_ATTR_NUMBERFORMAT_ONE_AREA,   true }
    };
    static const auto aAttrMap = svl::Items<
        SBA_DEF_RANGEFORMAT, SBA_ATTR_ALIGN_HOR_JUSTIFY,
        SID_ATTR_NUMBERFORMAT_INFO, SID_ATTR_NUMBERFORMAT_INFO,
        SID_ATTR_NUMBERFORMAT_ONE_AREA, SID_ATTR_NUMBERFORMAT_ONE_AREA
    >;

    std::vector<SfxPoolItem*> pDefaults
    {
        new SfxRangeItem(SBA_DEF_RANGEFORMAT, SBA_DEF_FMTVALUE, SBA_ATTR_ALIGN_HOR_JUSTIFY),
        new SfxUInt32Item(SBA_DEF_FMTVALUE),
        new SvxHorJustifyItem(SvxCellHorJustify::Standard, SBA_ATTR_ALIGN_HOR_JUSTIFY),
        new SvxNumberInfoItem(SID_ATTR_NUMBERFORMAT_INFO),
        new SfxBoolItem(SID_ATTR_NUMBERFORMAT_ONE_AREA, false)
    };

    rtl::Reference<SfxItemPool> pPool(new SfxItemPool("GridBrowserProperties", SBA_DEF_RANGEFORMAT, SBA_ATTR_ALIGN_HOR_JUSTIFY, aItemInfos, &pDefaults));
    pPool->SetDefaultMetric( MapUnit::MapTwip );    // ripped, don't understand why
    pPool->FreezeIdRanges();                        // the same

    std::optional<SfxItemSet> pFormatDescriptor(SfxItemSet(*pPool, aAttrMap));
    // fill it
    pFormatDescriptor->Put(SvxHorJustifyItem(_eJustify, SBA_ATTR_ALIGN_HOR_JUSTIFY));
    bool bText = false;
    if (_bHasFormat)
    {
        // if the col is bound to a text field we have to disallow all non-text formats
        if ((DataType::CHAR == _nDataType) || (DataType::VARCHAR == _nDataType) || (DataType::LONGVARCHAR == _nDataType) || (DataType::CLOB == _nDataType))
        {
            bText = true;
            pFormatDescriptor->Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_ONE_AREA, true));
            if (!_pFormatter->IsTextFormat(_nFormatKey))
                // text fields can only have text formats
                _nFormatKey = _pFormatter->GetStandardFormat(SvNumFormatType::TEXT, Application::GetSettings().GetLanguageTag().getLanguageType());
        }

        pFormatDescriptor->Put(SfxUInt32Item(SBA_DEF_FMTVALUE, _nFormatKey));
    }

    if (!bText)
    {
        SvxNumberInfoItem aFormatter(_pFormatter, 1234.56789, SID_ATTR_NUMBERFORMAT_INFO);
        pFormatDescriptor->Put(aFormatter);
    }

    {   // want the dialog to be destroyed before our set
        SbaSbAttrDlg aDlg(_pParent, &*pFormatDescriptor, _pFormatter, _bHasFormat);
        if (RET_OK == aDlg.run())
        {
            // ItemSet->UNO
            // UNO-properties
            const SfxItemSet* pSet = aDlg.GetExampleSet();
            // (of course we could put the modified items directly into the column, but then the UNO-model
            // won't reflect these changes, and why do we have a model, then ?)

            // horizontal justify
            const SvxHorJustifyItem* pHorJustify = pSet->GetItem<SvxHorJustifyItem>(SBA_ATTR_ALIGN_HOR_JUSTIFY);

            _eJustify = pHorJustify->GetValue();

            // format key
            if (_bHasFormat)
            {
                const SfxUInt32Item* pFormat = pSet->GetItem<SfxUInt32Item>(SBA_DEF_FMTVALUE);
                _nFormatKey = static_cast<sal_Int32>(pFormat->GetValue());
            }
            bRet = true;
        }
            // deleted formats
        const SfxItemSet* pResult = aDlg.GetOutputItemSet();
        if (pResult)
        {
            const SfxPoolItem* pItem = pResult->GetItem( SID_ATTR_NUMBERFORMAT_INFO );
            const SvxNumberInfoItem* pInfoItem = static_cast<const SvxNumberInfoItem*>(pItem);
            if (pInfoItem)
            {
                for (sal_uInt32 key : pInfoItem->GetDelFormats())
                    _pFormatter->DeleteEntry(key);
            }
        }
    }

    pFormatDescriptor.reset();
    pPool.clear();
    for (SfxPoolItem* pDefault : pDefaults)
        delete pDefault;

    return bRet;
}

std::shared_ptr<const SfxFilter> getStandardDatabaseFilter()
{
    std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName("StarOffice XML (Base)");
    OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!");
    return pFilter;
}

bool appendToFilter(const Reference<XConnection>& _xConnection,
                    const OUString& _sName,
                    const Reference< XComponentContext >& _rxContext,
                    weld::Window* pParent)
{
    bool bRet = false;
    Reference< XChild> xChild(_xConnection,UNO_QUERY);
    if(xChild.is())
    {
        Reference< XPropertySet> xProp(xChild->getParent(),UNO_QUERY);
        if(xProp.is())
        {
            Sequence< OUString > aFilter;
            xProp->getPropertyValue(PROPERTY_TABLEFILTER) >>= aFilter;
            // first check if we have something like SCHEMA.%
            bool bHasToInsert = true;
            for (const OUString& rItem : std::as_const(aFilter))
            {
                if(rItem.indexOf('%') != -1)
                {
                    sal_Int32 nLen = rItem.lastIndexOf('.');
                    if(nLen != -1 && !rItem.compareTo(_sName,nLen))
                        bHasToInsert = false;
                    else if(rItem.getLength() == 1)
                        bHasToInsert = false;
                }
            }

            bRet = true;
            if(bHasToInsert)
            {
                if(! ::dbaui::checkDataSourceAvailable(::comphelper::getString(xProp->getPropertyValue(PROPERTY_NAME)),_rxContext))
                {
                    OUString aMessage(DBA_RES(STR_TABLEDESIGN_DATASOURCE_DELETED));
                    OSQLWarningBox aWarning(pParent, aMessage);
                    aWarning.run();
                    bRet = false;
                }
                else
                {
                    aFilter.realloc(aFilter.getLength()+1);
                    aFilter.getArray()[aFilter.getLength()-1] = _sName;
                    xProp->setPropertyValue(PROPERTY_TABLEFILTER,Any(aFilter));
                }
            }
        }
    }
    return bRet;
}

void notifySystemWindow(vcl::Window const * _pWindow, vcl::Window* _pToRegister, const ::comphelper::mem_fun1_t<TaskPaneList,vcl::Window*>& _rMemFunc)
{
    OSL_ENSURE(_pWindow,"Window can not be null!");
    SystemWindow* pSystemWindow = _pWindow ? _pWindow->GetSystemWindow() : nullptr;
    if ( pSystemWindow )
    {
        _rMemFunc( pSystemWindow->GetTaskPaneList(), _pToRegister );
    }
}

void adjustBrowseBoxColumnWidth( ::svt::EditBrowseBox* _pBox, sal_uInt16 _nColId )
{
    sal_Int32 nColSize = -1;
    sal_uInt32 nDefaultWidth = _pBox->GetDefaultColumnWidth( _pBox->GetColumnTitle( _nColId ) );
    if ( nDefaultWidth != _pBox->GetColumnWidth( _nColId ) )
    {
        Size aSizeMM = _pBox->PixelToLogic( Size( _pBox->GetColumnWidth( _nColId ), 0 ), MapMode( MapUnit::MapMM ) );
        nColSize = aSizeMM.Width() * 10;
    }

    Size aDefaultMM = _pBox->PixelToLogic( Size( nDefaultWidth, 0 ), MapMode( MapUnit::MapMM ) );

    DlgSize aColumnSizeDlg(_pBox->GetFrameWeld(), nColSize, false, aDefaultMM.Width() * 10);
    if (aColumnSizeDlg.run() != RET_OK)
        return;

    sal_Int32 nValue = aColumnSizeDlg.GetValue();
    if ( -1 == nValue )
    {   // default width
        nValue = _pBox->GetDefaultColumnWidth( _pBox->GetColumnTitle( _nColId ) );
    }
    else
    {
        Size aSizeMM( nValue / 10, 0 );
        nValue = _pBox->LogicToPixel( aSizeMM, MapMode( MapUnit::MapMM ) ).Width();
    }
    _pBox->SetColumnWidth( _nColId, nValue );
}

// check if SQL92 name checking is enabled
bool isSQL92CheckEnabled(const Reference<XConnection>& _xConnection)
{
    return ::dbtools::getBooleanDataSourceSetting( _xConnection, PROPERTY_ENABLESQL92CHECK );
}

bool isAppendTableAliasEnabled(const Reference<XConnection>& _xConnection)
{
    return ::dbtools::getBooleanDataSourceSetting( _xConnection, INFO_APPEND_TABLE_ALIAS );
}

bool generateAsBeforeTableAlias(const Reference<XConnection>& _xConnection)
{
    return ::dbtools::getBooleanDataSourceSetting( _xConnection, INFO_AS_BEFORE_CORRELATION_NAME );
}

void fillAutoIncrementValue(const Reference<XPropertySet>& _xDatasource,
                            bool& _rAutoIncrementValueEnabled,
                            OUString& _rsAutoIncrementValue)
{
    if ( !_xDatasource.is() )
        return;

    OSL_ENSURE(_xDatasource->getPropertySetInfo()->hasPropertyByName(PROPERTY_INFO),"NO datasource supplied!");
    Sequence<PropertyValue> aInfo;
    _xDatasource->getPropertyValue(PROPERTY_INFO) >>= aInfo;

    // search the right propertyvalue
    const PropertyValue* pValue =std::find_if(std::cbegin(aInfo), std::cend(aInfo),
                                        [](const PropertyValue& lhs)
                                        {return lhs.Name == PROPERTY_AUTOINCREMENTCREATION;} );

    if ( pValue != std::cend(aInfo) )
        pValue->Value >>= _rsAutoIncrementValue;
    pValue =std::find_if(std::cbegin(aInfo), std::cend(aInfo),
                         [](const PropertyValue& lhs)
                         {return lhs.Name == "IsAutoRetrievingEnabled";} );

    if ( pValue != std::cend(aInfo) )
        pValue->Value >>= _rAutoIncrementValueEnabled;
}

void fillAutoIncrementValue(const Reference<XConnection>& _xConnection,
                            bool& _rAutoIncrementValueEnabled,
                            OUString& _rsAutoIncrementValue)
{
    Reference< XChild> xChild(_xConnection,UNO_QUERY);
    if(xChild.is())
    {
        Reference< XPropertySet> xProp(xChild->getParent(),UNO_QUERY);
        fillAutoIncrementValue(xProp,_rAutoIncrementValueEnabled,_rsAutoIncrementValue);
    }
}

OUString getStrippedDatabaseName(const Reference<XPropertySet>& _xDataSource,OUString& _rsDatabaseName)
{
    if ( _rsDatabaseName.isEmpty() && _xDataSource.is() )
    {
        try
        {
            _xDataSource->getPropertyValue(PROPERTY_NAME) >>= _rsDatabaseName;
        }
        catch(const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }
    OUString sName = _rsDatabaseName;
    INetURLObject aURL(sName);
    if ( aURL.GetProtocol() != INetProtocol::NotValid )
        sName = aURL.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::Unambiguous);
    return sName;
}

void setEvalDateFormatForFormatter(Reference< css::util::XNumberFormatter > const & _rxFormatter)
{
    OSL_ENSURE( _rxFormatter.is(),"setEvalDateFormatForFormatter: Formatter is NULL!");
    if ( !_rxFormatter.is() )
        return;

    Reference< css::util::XNumberFormatsSupplier >  xSupplier = _rxFormatter->getNumberFormatsSupplier();

    auto pSupplierImpl = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(xSupplier);
    OSL_ENSURE(pSupplierImpl,"No Supplier!");

    if ( pSupplierImpl )
    {
        SvNumberFormatter* pFormatter = pSupplierImpl->GetNumberFormatter();
        pFormatter->SetEvalDateFormat(NF_EVALDATEFORMAT_FORMAT);
    }
}

TOTypeInfoSP queryPrimaryKeyType(const OTypeInfoMap& _rTypeInfo)
{
    TOTypeInfoSP pTypeInfo;
    // first we search for a type which supports autoIncrement
    for (auto const& elem : _rTypeInfo)
    {
        // OJ: we don't want to set an autoincrement column to be key
        // because we don't have the possibility to know how to create
        // such auto increment column later on
        // so until we know how to do it, we create a column without autoincrement
        // therefore we have searched
        if ( elem.second->nType == DataType::INTEGER )
        {
            pTypeInfo = elem.second; // alternative
            break;
        }
        else if ( !pTypeInfo && elem.second->nType == DataType::DOUBLE )
            pTypeInfo = elem.second; // alternative
        else if ( !pTypeInfo && elem.second->nType == DataType::REAL )
            pTypeInfo = elem.second; // alternative
    }
    if ( !pTypeInfo ) // just a fallback
        pTypeInfo = queryTypeInfoByType(DataType::VARCHAR,_rTypeInfo);

    OSL_ENSURE(pTypeInfo,"checkColumns: can't find a type which is usable as a key!");
    return pTypeInfo;
}

TOTypeInfoSP queryTypeInfoByType(sal_Int32 _nDataType,const OTypeInfoMap& _rTypeInfo)
{
    OTypeInfoMap::const_iterator aIter = _rTypeInfo.find(_nDataType);
    if(aIter != _rTypeInfo.end())
        return aIter->second;
    // fall back if the type is unknown
    TOTypeInfoSP pTypeInfo;
    switch(_nDataType)
    {
        case DataType::TINYINT:
            if( (pTypeInfo = queryTypeInfoByType(DataType::SMALLINT,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::SMALLINT:
            if( (pTypeInfo = queryTypeInfoByType(DataType::INTEGER,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::INTEGER:
            if( (pTypeInfo = queryTypeInfoByType(DataType::FLOAT,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::FLOAT:
            if( (pTypeInfo = queryTypeInfoByType(DataType::REAL,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::DATE:
        case DataType::TIME:
            if( DataType::DATE == _nDataType || DataType::TIME == _nDataType )
            {
                if( (pTypeInfo = queryTypeInfoByType(DataType::TIMESTAMP,_rTypeInfo) ) )
                    break;
            }
            [[fallthrough]];
        case DataType::TIMESTAMP:
        case DataType::REAL:
        case DataType::BIGINT:
            if (  (pTypeInfo = queryTypeInfoByType(DataType::DOUBLE,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::DOUBLE:
            if (  (pTypeInfo = queryTypeInfoByType(DataType::NUMERIC,_rTypeInfo) ) )
                break;
            [[fallthrough]];
        case DataType::NUMERIC:
             pTypeInfo = queryTypeInfoByType(DataType::DECIMAL,_rTypeInfo);
            break;
        case DataType::DECIMAL:
            if (  (pTypeInfo = queryTypeInfoByType(DataType::NUMERIC,_rTypeInfo) ) )
                break;
            if (  (pTypeInfo = queryTypeInfoByType(DataType::DOUBLE,_rTypeInfo) ) )
                break;
            break;
        case DataType::VARCHAR:
            if (  (pTypeInfo = queryTypeInfoByType(DataType::LONGVARCHAR,_rTypeInfo) ) )
                break;
            break;
        case DataType::LONGVARCHAR:
            if (  (pTypeInfo = queryTypeInfoByType(DataType::CLOB,_rTypeInfo) ) )
                break;
            break;
        default:
            ;
    }
    if ( !pTypeInfo )
    {
        bool bForce = true;
        pTypeInfo = ::dbaui::getTypeInfoFromType(_rTypeInfo,DataType::VARCHAR,OUString(),"x",50,0,false,bForce);
    }
    OSL_ENSURE(pTypeInfo,"Wrong DataType supplied!");
    return pTypeInfo;
}

sal_Int32 askForUserAction(weld::Window* pParent, TranslateId pTitle, TranslateId pText, bool _bAll, std::u16string_view _sName)
{
    SolarMutexGuard aGuard;
    OUString aMsg = DBA_RES(pText);
    aMsg = aMsg.replaceFirst("%1", _sName);
    OSQLMessageBox aAsk(pParent, DBA_RES(pTitle), aMsg, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, MessageType::Query);
    if ( _bAll )
    {
        aAsk.add_button(DBA_RES(STR_BUTTON_TEXT_ALL), RET_ALL, HID_CONFIRM_DROP_BUTTON_ALL);
    }
    return aAsk.run();
}

namespace
{
    OUString lcl_createSDBCLevelStatement( const OUString& _rStatement, const Reference< XConnection >& _rxConnection )
    {
        OUString sSDBCLevelStatement( _rStatement );
        try
        {
            Reference< XMultiServiceFactory > xAnalyzerFactory( _rxConnection, UNO_QUERY_THROW );
            Reference< XSingleSelectQueryAnalyzer > xAnalyzer( xAnalyzerFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW );
            xAnalyzer->setQuery( _rStatement );
            sSDBCLevelStatement = xAnalyzer->getQueryWithSubstitution();
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
        return sSDBCLevelStatement;
    }
}

Reference< XPropertySet > createView( const OUString& _rName, const Reference< XConnection >& _rxConnection,
                                    const OUString& _rCommand )
{
    Reference<XViewsSupplier> xSup(_rxConnection,UNO_QUERY);
    Reference< XNameAccess > xViews;
    if(xSup.is())
        xViews = xSup->getViews();
    Reference<XDataDescriptorFactory> xFact(xViews,UNO_QUERY);
    OSL_ENSURE(xFact.is(),"No XDataDescriptorFactory available!");
    if(!xFact.is())
        return nullptr;

    Reference<XPropertySet> xView = xFact->createDataDescriptor();
    if ( !xView.is() )
        return nullptr;

    OUString sCatalog,sSchema,sTable;
    ::dbtools::qualifiedNameComponents(_rxConnection->getMetaData(),
                                        _rName,
                                        sCatalog,
                                        sSchema,
                                        sTable,
                                        ::dbtools::EComposeRule::InDataManipulation);

    xView->setPropertyValue(PROPERTY_CATALOGNAME,Any(sCatalog));
    xView->setPropertyValue(PROPERTY_SCHEMANAME,Any(sSchema));
    xView->setPropertyValue(PROPERTY_NAME,Any(sTable));

    xView->setPropertyValue( PROPERTY_COMMAND, Any( _rCommand ) );

    Reference<XAppend> xAppend(xViews,UNO_QUERY);
    if(xAppend.is())
        xAppend->appendByDescriptor(xView);

    xView = nullptr;
    // we need to reget the view because after appending it, it is no longer valid
    // but this time it isn't a view object it is a table object with type "VIEW"
    Reference<XTablesSupplier> xTabSup(_rxConnection,UNO_QUERY);
    Reference< XNameAccess > xTables;
    if ( xTabSup.is() )
    {
        xTables = xTabSup->getTables();
        if ( xTables.is() && xTables->hasByName( _rName ) )
            xTables->getByName( _rName ) >>= xView;
    }

    return xView;
}

Reference<XPropertySet> createView( const OUString& _rName, const Reference< XConnection >& _rxConnection
                                   ,const Reference<XPropertySet>& _rxSourceObject)
{
    OUString sCommand;
    Reference< XPropertySetInfo > xPSI( _rxSourceObject->getPropertySetInfo(), UNO_SET_THROW );
    if ( xPSI->hasPropertyByName( PROPERTY_COMMAND ) )
    {
        _rxSourceObject->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand;

        bool bEscapeProcessing( false );
        OSL_VERIFY( _rxSourceObject->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
        if ( bEscapeProcessing )
            sCommand = lcl_createSDBCLevelStatement( sCommand, _rxConnection );
    }
    else
    {
        sCommand =  "SELECT * FROM " + composeTableNameForSelect( _rxConnection, _rxSourceObject );
    }
    return createView( _rName, _rxConnection, sCommand );
}

bool insertHierarchyElement(weld::Window* pParent, const Reference< XComponentContext >& _rxContext,
                           const Reference<XHierarchicalNameContainer>& _xNames,
                           const OUString& _sParentFolder,
                           bool _bForm,
                           bool _bCollection,
                           const Reference<XContent>& _xContent,
                           bool _bMove)
{
    OSL_ENSURE( _xNames.is(), "insertHierarchyElement: illegal name container!" );
    if ( !_xNames.is() )
        return false;

    Reference<XNameAccess> xNameAccess( _xNames, UNO_QUERY );
    if ( _xNames->hasByHierarchicalName(_sParentFolder) )
    {
        Reference<XChild> xChild(_xNames->getByHierarchicalName(_sParentFolder),UNO_QUERY);
        xNameAccess.set(xChild,UNO_QUERY);
        if ( !xNameAccess.is() && xChild.is() )
            xNameAccess.set(xChild->getParent(),UNO_QUERY);
    }

    OSL_ENSURE( xNameAccess.is(), "insertHierarchyElement: could not find the proper name container!" );
    if ( !xNameAccess.is() )
        return false;

    OUString sNewName;
    Reference<XPropertySet> xProp(_xContent,UNO_QUERY);
    if ( xProp.is() )
        xProp->getPropertyValue(PROPERTY_NAME) >>= sNewName;

    if ( !_bMove || sNewName.isEmpty() )
    {
        if ( sNewName.isEmpty() || xNameAccess->hasByName(sNewName) )
        {
            OUString sLabel, sTargetName;
            if ( !sNewName.isEmpty() )
                sTargetName = sNewName;
            else
                sTargetName = DBA_RES( _bCollection ? STR_NEW_FOLDER : ((_bForm) ? RID_STR_FORM : RID_STR_REPORT));
            sLabel = DBA_RES( _bCollection ? STR_FOLDER_LABEL  : ((_bForm) ? STR_FRM_LABEL : STR_RPT_LABEL));
            sTargetName = ::dbtools::createUniqueName(xNameAccess,sTargetName);

            // here we have everything needed to create a new query object ...
            HierarchicalNameCheck aNameChecker( _xNames, _sParentFolder );
            // ... ehm, except a new name
            OSaveAsDlg aAskForName(pParent,
                                   _rxContext,
                                   sTargetName,
                                   sLabel,
                                   aNameChecker,
                                   SADFlags::AdditionalDescription | SADFlags::TitlePasteAs);
            if ( RET_OK != aAskForName.run() )
                // cancelled by the user
                return false;

            sNewName = aAskForName.getName();
        }
    }
    else if ( xNameAccess->hasByName(sNewName) )
    {
        OUString sError(DBA_RES(STR_NAME_ALREADY_EXISTS));
        sError = sError.replaceFirst("#",sNewName);
        throw SQLException(sError,nullptr,"S1000",0,Any());
    }

    try
    {
        Reference<XMultiServiceFactory> xORB( xNameAccess, UNO_QUERY_THROW );
        uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
        {
            {"Name", uno::Any(sNewName)}, // set as folder
            {"Parent", uno::Any(xNameAccess)},
            {PROPERTY_EMBEDDEDOBJECT, uno::Any(_xContent)},
        }));
        OUString sServiceName(_bCollection ? (_bForm ? OUString(SERVICE_NAME_FORM_COLLECTION) : OUString(SERVICE_NAME_REPORT_COLLECTION)) : OUString(SERVICE_SDB_DOCUMENTDEFINITION));

        Reference<XContent > xNew( xORB->createInstanceWithArguments( sServiceName, aArguments ), UNO_QUERY_THROW );
        Reference< XNameContainer > xNameContainer( xNameAccess, UNO_QUERY_THROW );
        xNameContainer->insertByName( sNewName, Any( xNew ) );
    }
    catch( const IllegalArgumentException& e )
    {
        ::dbtools::throwGenericSQLException( e.Message, e.Context );
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
        return false;
    }

    return true;
}

Reference< XNumberFormatter > getNumberFormatter(const Reference< XConnection >& _rxConnection, const Reference< css::uno::XComponentContext >& _rxContext )
{
    // create a formatter working with the connections format supplier
    Reference< XNumberFormatter > xFormatter;

    try
    {
        Reference< css::util::XNumberFormatsSupplier >  xSupplier(::dbtools::getNumberFormats(_rxConnection, true, _rxContext));

        if ( xSupplier.is() )
        {
            // create a new formatter
            xFormatter.set(util::NumberFormatter::create( _rxContext ), UNO_QUERY_THROW);
            xFormatter->attachNumberFormatsSupplier(xSupplier);
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    return xFormatter;
}

} // dbaui

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