/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_dbaccess.hxx" #include "dbu_reghelper.hxx" #include #include "dbu_rel.hrc" #include #include "browserids.hxx" #include #include "dbustrings.hrc" #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 "sqlmessage.hxx" #include "RelationController.hxx" #include #include "TableWindowData.hxx" #include "dbustrings.hrc" #include "UITools.hxx" #include "RTableConnectionData.hxx" #include "RelationTableView.hxx" #include "RelationDesignView.hxx" #include #include #include #include #include #define MAX_THREADS 10 extern "C" void SAL_CALL createRegistryInfo_ORelationControl() { static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::ORelationController > aAutoRegistration; } using namespace ::com::sun::star::uno; using namespace ::com::sun::star::io; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::util; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::container; using namespace ::com::sun::star::sdbcx; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::sdb; using namespace ::com::sun::star::ui::dialogs; using namespace ::com::sun::star::util; // using namespace ::com::sun::star::sdbcx; // using namespace ::connectivity; using namespace ::dbtools; using namespace ::dbaui; using namespace ::comphelper; using namespace ::osl; //------------------------------------------------------------------------------ ::rtl::OUString SAL_CALL ORelationController::getImplementationName() throw( RuntimeException ) { return getImplementationName_Static(); } //------------------------------------------------------------------------------ ::rtl::OUString ORelationController::getImplementationName_Static() throw( RuntimeException ) { return ::rtl::OUString::createFromAscii("org.openoffice.comp.dbu.ORelationDesign"); } //------------------------------------------------------------------------------ Sequence< ::rtl::OUString> ORelationController::getSupportedServiceNames_Static(void) throw( RuntimeException ) { Sequence< ::rtl::OUString> aSupported(1); aSupported.getArray()[0] = ::rtl::OUString::createFromAscii("com.sun.star.sdb.RelationDesign"); return aSupported; } //------------------------------------------------------------------------- Sequence< ::rtl::OUString> SAL_CALL ORelationController::getSupportedServiceNames() throw(RuntimeException) { return getSupportedServiceNames_Static(); } // ------------------------------------------------------------------------- Reference< XInterface > SAL_CALL ORelationController::Create(const Reference& _rxFactory) { return *(new ORelationController(_rxFactory)); } DBG_NAME(ORelationController); // ----------------------------------------------------------------------------- ORelationController::ORelationController(const Reference< XMultiServiceFactory >& _rM) : OJoinController(_rM) ,m_nThreadEvent(0) ,m_bRelationsPossible(sal_True) { DBG_CTOR(ORelationController,NULL); InvalidateAll(); } // ----------------------------------------------------------------------------- ORelationController::~ORelationController() { DBG_DTOR(ORelationController,NULL); } // ----------------------------------------------------------------------------- FeatureState ORelationController::GetState(sal_uInt16 _nId) const { FeatureState aReturn; aReturn.bEnabled = m_bRelationsPossible; switch (_nId) { case SID_RELATION_ADD_RELATION: aReturn.bEnabled = !m_vTableData.empty() && isConnected() && isEditable(); aReturn.bChecked = false; break; case ID_BROWSER_SAVEDOC: aReturn.bEnabled = haveDataSource() && impl_isModified(); break; default: aReturn = OJoinController::GetState(_nId); break; } return aReturn; } // ----------------------------------------------------------------------------- void ORelationController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) { switch(_nId) { case ID_BROWSER_SAVEDOC: { OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!"); if(!::dbaui::checkDataSourceAvailable(::comphelper::getString(getDataSource()->getPropertyValue(PROPERTY_NAME)),getORB())) { String aMessage(ModuleRes(STR_DATASOURCE_DELETED)); OSQLWarningBox( getView(), aMessage ).Execute(); } else { // now we save the layout information // create the output stream try { if ( haveDataSource() && getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) ) { ::comphelper::NamedValueCollection aWindowsData; saveTableWindows( aWindowsData ); getDataSource()->setPropertyValue( PROPERTY_LAYOUTINFORMATION, makeAny( aWindowsData.getPropertyValues() ) ); setModified(sal_False); } } catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } } } break; case SID_RELATION_ADD_RELATION: static_cast(static_cast( getView() )->getTableView())->AddNewRelation(); break; default: OJoinController::Execute(_nId,aArgs); return; } InvalidateFeature(_nId); } // ----------------------------------------------------------------------------- void ORelationController::impl_initialize() { OJoinController::impl_initialize(); if( !getSdbMetaData().supportsRelations() ) {// check if this database supports relations setEditable(sal_False); m_bRelationsPossible = sal_False; { String sTitle(ModuleRes(STR_RELATIONDESIGN)); sTitle.Erase(0,3); OSQLMessageBox aDlg(NULL,sTitle,ModuleRes(STR_RELATIONDESIGN_NOT_AVAILABLE)); aDlg.Execute(); } disconnect(); throw SQLException(); } if(!m_bRelationsPossible) InvalidateAll(); // we need a datasource OSL_ENSURE(haveDataSource(),"ORelationController::initialize: need a datasource!"); Reference xSup(getConnection(),UNO_QUERY); OSL_ENSURE(xSup.is(),"Connection isn't a XTablesSupplier!"); if(xSup.is()) m_xTables = xSup->getTables(); // load the layoutInformation loadLayoutInformation(); try { loadData(); if ( !m_nThreadEvent ) Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished)); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } } // ----------------------------------------------------------------------------- ::rtl::OUString ORelationController::getPrivateTitle( ) const { ::rtl::OUString sName = getDataSourceName(); return ::dbaui::getStrippedDatabaseName(getDataSource(),sName); } // ----------------------------------------------------------------------------- sal_Bool ORelationController::Construct(Window* pParent) { setView( * new ORelationDesignView( pParent, *this, getORB() ) ); OJoinController::Construct(pParent); return sal_True; } // ----------------------------------------------------------------------------- short ORelationController::saveModified() { short nSaved = RET_YES; if(haveDataSource() && isModified()) { QueryBox aQry(getView(), ModuleRes(RELATION_DESIGN_SAVEMODIFIED)); nSaved = aQry.Execute(); if(nSaved == RET_YES) Execute(ID_BROWSER_SAVEDOC,Sequence()); } return nSaved; } // ----------------------------------------------------------------------------- void ORelationController::describeSupportedFeatures() { OJoinController::describeSupportedFeatures(); implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION, CommandGroup::EDIT ); } namespace { class RelationLoader : public ::osl::Thread { DECLARE_STL_MAP(::rtl::OUString,::boost::shared_ptr,::comphelper::UStringMixLess,TTableDataHelper); TTableDataHelper m_aTableData; TTableConnectionData m_vTableConnectionData; const Sequence< ::rtl::OUString> m_aTableList; ORelationController* m_pParent; const Reference< XDatabaseMetaData> m_xMetaData; const Reference< XNameAccess > m_xTables; const sal_Int32 m_nStartIndex; const sal_Int32 m_nEndIndex; public: RelationLoader(ORelationController* _pParent ,const Reference< XDatabaseMetaData>& _xMetaData ,const Reference< XNameAccess >& _xTables ,const Sequence< ::rtl::OUString>& _aTableList ,const sal_Int32 _nStartIndex ,const sal_Int32 _nEndIndex) :m_aTableData(_xMetaData.is() && _xMetaData->supportsMixedCaseQuotedIdentifiers()) ,m_aTableList(_aTableList) ,m_pParent(_pParent) ,m_xMetaData(_xMetaData) ,m_xTables(_xTables) ,m_nStartIndex(_nStartIndex) ,m_nEndIndex(_nEndIndex) { } /// Working method which should be overridden. virtual void SAL_CALL run(); virtual void SAL_CALL onTerminated(); protected: virtual ~RelationLoader(){} void loadTableData(const Any& _aTable); }; void SAL_CALL RelationLoader::run() { const ::rtl::OUString* pIter = m_aTableList.getConstArray() + m_nStartIndex; for(sal_Int32 i = m_nStartIndex; i < m_nEndIndex;++i,++pIter) { ::rtl::OUString sCatalog,sSchema,sTable; ::dbtools::qualifiedNameComponents(m_xMetaData, *pIter, sCatalog, sSchema, sTable, ::dbtools::eInDataManipulation); Any aCatalog; if ( sCatalog.getLength() ) aCatalog <<= sCatalog; try { Reference< XResultSet > xResult = m_xMetaData->getImportedKeys(aCatalog, sSchema,sTable); if ( xResult.is() && xResult->next() ) { ::comphelper::disposeComponent(xResult); loadTableData(m_xTables->getByName(*pIter)); } // if ( xResult.is() && xResult->next() ) } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } } } void SAL_CALL RelationLoader::onTerminated() { m_pParent->mergeData(m_vTableConnectionData); delete this; } void RelationLoader::loadTableData(const Any& _aTable) { Reference xTableProp(_aTable,UNO_QUERY); const ::rtl::OUString sSourceName = ::dbtools::composeTableName( m_xMetaData, xTableProp, ::dbtools::eInTableDefinitions, false, false, false ); TTableDataHelper::iterator aFind = m_aTableData.find(sSourceName); bool bNotFound = true, bAdded = false; if ( aFind == m_aTableData.end() ) { aFind = m_aTableData.insert(TTableDataHelper::value_type(sSourceName,::boost::shared_ptr(new OTableWindowData(xTableProp,sSourceName, sSourceName)))).first; aFind->second->ShowAll(sal_False); bAdded = true; } TTableWindowData::value_type pReferencingTable = aFind->second; Reference xKeys = pReferencingTable->getKeys(); const Reference xKeySup(xTableProp,UNO_QUERY); if ( !xKeys.is() && xKeySup.is() ) { xKeys = xKeySup->getKeys(); } if ( xKeys.is() ) { Reference xKey; const sal_Int32 nCount = xKeys->getCount(); for(sal_Int32 i = 0 ; i < nCount ; ++i) { xKeys->getByIndex(i) >>= xKey; sal_Int32 nKeyType = 0; xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; if ( KeyType::FOREIGN == nKeyType ) { bNotFound = false; ::rtl::OUString sReferencedTable; xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable; ////////////////////////////////////////////////////////////////////// // insert windows TTableDataHelper::iterator aRefFind = m_aTableData.find(sReferencedTable); if ( aRefFind == m_aTableData.end() ) { if ( m_xTables->hasByName(sReferencedTable) ) { Reference xReferencedTable(m_xTables->getByName(sReferencedTable),UNO_QUERY); aRefFind = m_aTableData.insert(TTableDataHelper::value_type(sReferencedTable,::boost::shared_ptr(new OTableWindowData(xReferencedTable,sReferencedTable, sReferencedTable)))).first; aRefFind->second->ShowAll(sal_False); } else continue; // table name could not be found so we do not show this table releation } // if ( aFind == m_aTableData.end() ) TTableWindowData::value_type pReferencedTable = aRefFind->second; ::rtl::OUString sKeyName; xKey->getPropertyValue(PROPERTY_NAME) >>= sKeyName; ////////////////////////////////////////////////////////////////////// // insert connection ORelationTableConnectionData* pTabConnData = new ORelationTableConnectionData( pReferencingTable, pReferencedTable, sKeyName ); m_vTableConnectionData.push_back(TTableConnectionData::value_type(pTabConnData)); ////////////////////////////////////////////////////////////////////// // insert columns const Reference xColsSup(xKey,UNO_QUERY); OSL_ENSURE(xColsSup.is(),"Key is no XColumnsSupplier!"); const Reference xColumns = xColsSup->getColumns(); const Sequence< ::rtl::OUString> aNames = xColumns->getElementNames(); const ::rtl::OUString* pIter = aNames.getConstArray(); const ::rtl::OUString* pEnd = pIter + aNames.getLength(); ::rtl::OUString sColumnName,sRelatedName; for(sal_uInt16 j=0;pIter != pEnd;++pIter,++j) { const Reference xPropSet(xColumns->getByName(*pIter),UNO_QUERY); OSL_ENSURE(xPropSet.is(),"Invalid column found in KeyColumns!"); if ( xPropSet.is() ) { xPropSet->getPropertyValue(PROPERTY_NAME) >>= sColumnName; xPropSet->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedName; } pTabConnData->SetConnLine( j, sColumnName, sRelatedName ); } ////////////////////////////////////////////////////////////////////// // Update/Del-Flags setzen sal_Int32 nUpdateRule = 0; sal_Int32 nDeleteRule = 0; xKey->getPropertyValue(PROPERTY_UPDATERULE) >>= nUpdateRule; xKey->getPropertyValue(PROPERTY_DELETERULE) >>= nDeleteRule; pTabConnData->SetUpdateRules( nUpdateRule ); pTabConnData->SetDeleteRules( nDeleteRule ); ////////////////////////////////////////////////////////////////////// // Kardinalitaet setzen pTabConnData->SetCardinality(); } } } // if ( xKeys.is() ) } } void ORelationController::mergeData(const TTableConnectionData& _aConnectionData) { ::osl::MutexGuard aGuard( getMutex() ); ::std::copy( _aConnectionData.begin(), _aConnectionData.end(), ::std::back_inserter( m_vTableConnectionData )); //const Reference< XDatabaseMetaData> xMetaData = getConnection()->getMetaData(); const sal_Bool bCase = sal_True;//xMetaData.is() && xMetaData->supportsMixedCaseQuotedIdentifiers(); // here we are finished, so we can collect the table from connection data TTableConnectionData::iterator aConnDataIter = m_vTableConnectionData.begin(); TTableConnectionData::iterator aConnDataEnd = m_vTableConnectionData.end(); for(;aConnDataIter != aConnDataEnd;++aConnDataIter) { if ( !existsTable((*aConnDataIter)->getReferencingTable()->GetComposedName(),bCase) ) { m_vTableData.push_back((*aConnDataIter)->getReferencingTable()); } if ( !existsTable((*aConnDataIter)->getReferencedTable()->GetComposedName(),bCase) ) { m_vTableData.push_back((*aConnDataIter)->getReferencedTable()); } } // for(;aConnDataIter != aConnDataEnd;++aConnDataIter) if ( m_nThreadEvent ) { --m_nThreadEvent; if ( !m_nThreadEvent ) Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished)); } } // ----------------------------------------------------------------------------- IMPL_LINK( ORelationController, OnThreadFinished, void*, /*NOTINTERESTEDIN*/ ) { vos::OGuard aSolarGuard( Application::GetSolarMutex() ); ::osl::MutexGuard aGuard( getMutex() ); try { getView()->initialize(); // show the windows and fill with our informations getView()->Invalidate(INVALIDATE_NOERASE); ClearUndoManager(); setModified(sal_False); // and we are not modified yet if(m_vTableData.empty()) Execute(ID_BROWSER_ADDTABLE,Sequence()); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } m_pWaitObject.reset(); return 0L; } // ----------------------------------------------------------------------------- void ORelationController::loadData() { m_pWaitObject.reset( new WaitObject(getView()) ); try { if ( !m_xTables.is() ) return; DatabaseMetaData aMeta(getConnection()); // this may take some time const Reference< XDatabaseMetaData> xMetaData = getConnection()->getMetaData(); const Sequence< ::rtl::OUString> aNames = m_xTables->getElementNames(); const sal_Int32 nCount = aNames.getLength(); if ( aMeta.supportsThreads() ) { const sal_Int32 nMaxElements = (nCount / MAX_THREADS) +1; sal_Int32 nStart = 0,nEnd = ::std::min(nMaxElements,nCount); while(nStart != nEnd) { ++m_nThreadEvent; RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,nStart,nEnd); pThread->createSuspended(); pThread->setPriority(osl_Thread_PriorityBelowNormal); pThread->resume(); nStart = nEnd; nEnd += nMaxElements; nEnd = ::std::min(nEnd,nCount); } // for(;pIter != pEnd;++pIter) } // if ( aMeta.supportsThreads() ) else { RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,0,nCount); pThread->run(); pThread->onTerminated(); } } catch(SQLException& e) { showError(SQLExceptionInfo(e)); } catch(const Exception&) { DBG_UNHANDLED_EXCEPTION(); } } // ----------------------------------------------------------------------------- TTableWindowData::value_type ORelationController::existsTable(const ::rtl::OUString& _rComposedTableName,sal_Bool _bCase) const { ::comphelper::UStringMixEqual bCase(_bCase); TTableWindowData::const_iterator aIter = m_vTableData.begin(); TTableWindowData::const_iterator aEnd = m_vTableData.end(); for(;aIter != aEnd;++aIter) { if(bCase((*aIter)->GetComposedName(),_rComposedTableName)) break; } return ( aIter != aEnd) ? *aIter : TTableWindowData::value_type(); } // ----------------------------------------------------------------------------- void ORelationController::loadLayoutInformation() { try { OSL_ENSURE(haveDataSource(),"We need a datasource from our connection!"); if ( haveDataSource() ) { if ( getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) ) { Sequence aWindows; getDataSource()->getPropertyValue(PROPERTY_LAYOUTINFORMATION) >>= aWindows; loadTableWindows(aWindows); } } } catch(Exception&) { } } // ----------------------------------------------------------------------------- void ORelationController::reset() { loadLayoutInformation(); ODataView* pView = getView(); OSL_ENSURE(pView,"No current view!"); if(pView) { pView->initialize(); pView->Invalidate(INVALIDATE_NOERASE); } } // ----------------------------------------------------------------------------- bool ORelationController::allowViews() const { return false; } // ----------------------------------------------------------------------------- bool ORelationController::allowQueries() const { return false; } // -----------------------------------------------------------------------------