/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 ::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 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 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,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 = std::move(aSQLError); } else { showError( aSQLError, _pErrorMessageParent ? _pErrorMessageParent->GetXWindow() : nullptr, _rxContext ); } } return Reference(); } Reference< XInterface > getDataSourceOrModel(const Reference< XInterface >& _xObject) { Reference< XInterface > xRet; Reference xDocumentDataSource(_xObject,UNO_QUERY); if ( xDocumentDataSource.is() ) xRet = xDocumentDataSource->getDatabaseDocument(); if ( !xRet.is() ) { Reference 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 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& _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 = Reference(xRs,UNO_QUERY_THROW)->getMetaData(); ::connectivity::ORowSetValue aValue; std::vector aTypes; std::vector aNullable; // Loop on the result set until we reach end of file while (xRs->next()) { TOTypeInfoSP pInfo = std::make_shared(); 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& _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& _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 xRes = _xMetaData->getCatalogs(); Reference 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& xAffectedCol, const Reference& 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(dbaui::mapTextAlign(eJustify)))); if (bHasFormat) xAffectedCol->setPropertyValue(PROPERTY_FORMATKEY, Any(nFormatKey)); } } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); } } static ItemInfoPackage& getItemInfoPackageColumnFormatDialog() { class ItemInfoPackageColumnFormatDialog : public ItemInfoPackage { typedef std::array ItemInfoArrayColumnFormatDialog; ItemInfoArrayColumnFormatDialog maItemInfos {{ // m_nWhich, m_pItem, m_nSlotID, m_nItemInfoFlags { SBA_DEF_RANGEFORMAT, new SfxRangeItem(SBA_DEF_RANGEFORMAT, SBA_DEF_FMTVALUE, SBA_ATTR_ALIGN_HOR_JUSTIFY), 0, SFX_ITEMINFOFLAG_NONE }, { SBA_DEF_FMTVALUE, new SfxUInt32Item(SBA_DEF_FMTVALUE), SID_ATTR_NUMBERFORMAT_VALUE, SFX_ITEMINFOFLAG_NONE }, { SBA_ATTR_ALIGN_HOR_JUSTIFY, new SvxHorJustifyItem(SvxCellHorJustify::Standard, SBA_ATTR_ALIGN_HOR_JUSTIFY), SID_ATTR_ALIGN_HOR_JUSTIFY, SFX_ITEMINFOFLAG_NONE }, }}; virtual const ItemInfoStatic& getItemInfoStatic(size_t nIndex) const override { return maItemInfos[nIndex]; } public: virtual size_t size() const override { return maItemInfos.size(); } virtual const ItemInfo& getItemInfo(size_t nIndex, SfxItemPool& /*rPool*/) override { return maItemInfos[nIndex]; } }; static std::unique_ptr g_aItemInfoPackageColumnFormatDialog; if (!g_aItemInfoPackageColumnFormatDialog) g_aItemInfoPackageColumnFormatDialog.reset(new ItemInfoPackageColumnFormatDialog); return *g_aItemInfoPackageColumnFormatDialog; } bool callColumnFormatDialog(weld::Widget* _pParent, SvNumberFormatter* _pFormatter, sal_Int32 _nDataType, sal_Int32& _nFormatKey, SvxCellHorJustify& _eJustify, bool _bHasFormat) { bool bRet = false; // UNO->ItemSet 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 >; rtl::Reference pPool(new SfxItemPool(u"GridBrowserProperties"_ustr)); pPool->registerItemInfoPackage(getItemInfoPackageColumnFormatDialog()); pPool->SetDefaultMetric( MapUnit::MapTwip ); // ripped, don't understand why std::optional 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(SBA_ATTR_ALIGN_HOR_JUSTIFY); _eJustify = pHorJustify->GetValue(); // format key if (_bHasFormat) { const SfxUInt32Item* pFormat = pSet->GetItem(SBA_DEF_FMTVALUE); _nFormatKey = static_cast(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(pItem); if (pInfoItem) { for (sal_uInt32 key : pInfoItem->GetDelFormats()) _pFormatter->DeleteEntry(key); } } } pFormatDescriptor.reset(); pPool.clear(); return bRet; } std::shared_ptr getStandardDatabaseFilter() { std::shared_ptr pFilter = SfxFilter::GetFilterByName(u"StarOffice XML (Base)"_ustr); OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!"); return pFilter; } bool appendToFilter(const Reference& _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 : 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& _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; ::tools::Long 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) { return ::dbtools::getBooleanDataSourceSetting( _xConnection, PROPERTY_ENABLESQL92CHECK ); } bool isAppendTableAliasEnabled(const Reference& _xConnection) { return ::dbtools::getBooleanDataSourceSetting( _xConnection, INFO_APPEND_TABLE_ALIAS ); } bool generateAsBeforeTableAlias(const Reference& _xConnection) { return ::dbtools::getBooleanDataSourceSetting( _xConnection, INFO_AS_BEFORE_CORRELATION_NAME ); } void fillAutoIncrementValue(const Reference& _xDatasource, bool& _rAutoIncrementValueEnabled, OUString& _rsAutoIncrementValue) { if ( !_xDatasource.is() ) return; OSL_ENSURE(_xDatasource->getPropertySetInfo()->hasPropertyByName(PROPERTY_INFO),"NO datasource supplied!"); Sequence 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, 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& _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(xSupplier); OSL_ENSURE(pSupplierImpl,"No Supplier!"); if ( pSupplierImpl ) { SvNumberFormatter* pFormatter = pSupplierImpl->GetNumberFormatter(); pFormatter->SetEvalDateFormat(NF_EVALDATEFORMAT_FORMAT); } } static bool TypeIsGreater(const TOTypeInfoSP& lhs, const TOTypeInfoSP& rhs) { assert(lhs); if (!rhs) return true; if (lhs->nNumPrecRadix == rhs->nNumPrecRadix) return lhs->nPrecision > rhs->nPrecision; if (lhs->nPrecision == rhs->nPrecision) return lhs->nNumPrecRadix > rhs->nNumPrecRadix; if ((lhs->nNumPrecRadix > rhs->nNumPrecRadix) == (lhs->nPrecision > rhs->nPrecision)) return lhs->nPrecision > rhs->nPrecision; return std::pow(lhs->nNumPrecRadix, lhs->nPrecision) > std::pow(rhs->nNumPrecRadix, rhs->nPrecision); } TOTypeInfoSP queryPrimaryKeyType(const OTypeInfoMap& _rTypeInfo) { TOTypeInfoSP pTypeInfo, pFallback; // first we search for a largest type which supports autoIncrement for (auto const& elem : _rTypeInfo) { if (elem.second->bAutoIncrement && TypeIsGreater(elem.second, pTypeInfo)) pTypeInfo = elem.second; if (pTypeInfo) continue; if (elem.second->nType == DataType::INTEGER) pFallback = elem.second; // default alternative else if (!pFallback && elem.second->nType == DataType::DOUBLE) pFallback = elem.second; // alternative else if (!pFallback && elem.second->nType == DataType::REAL) pFallback = elem.second; // alternative } if ( !pTypeInfo ) // just a fallback pTypeInfo = pFallback ? std::move(pFallback) : 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(),u"x"_ustr,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 xSup(_rxConnection,UNO_QUERY); Reference< XNameAccess > xViews; if(xSup.is()) xViews = xSup->getViews(); Reference xFact(xViews,UNO_QUERY); OSL_ENSURE(xFact.is(),"No XDataDescriptorFactory available!"); if(!xFact.is()) return nullptr; Reference 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(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 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 createView( const OUString& _rName, const Reference< XConnection >& _rxConnection ,const Reference& _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& _xNames, const OUString& _sParentFolder, bool _bForm, bool _bCollection, const Reference& _xContent, bool _bMove) { OSL_ENSURE( _xNames.is(), "insertHierarchyElement: illegal name container!" ); if ( !_xNames.is() ) return false; Reference xNameAccess( _xNames, UNO_QUERY ); if ( _xNames->hasByHierarchicalName(_sParentFolder) ) { Reference 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 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,u"S1000"_ustr,0,Any()); } try { Reference xORB( xNameAccess, UNO_QUERY_THROW ); uno::Sequence aArguments(comphelper::InitAnyPropertySequence( { {"Name", uno::Any(sNewName)}, // set as folder {"Parent", uno::Any(xNameAccess)}, {PROPERTY_EMBEDDEDOBJECT, uno::Any(_xContent)}, })); OUString sServiceName(_bCollection ? (_bForm ? SERVICE_NAME_FORM_COLLECTION : SERVICE_NAME_REPORT_COLLECTION) : SERVICE_SDB_DOCUMENTDEFINITION); Reference 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: */