1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include "dbu_reghelper.hxx"
25 #include "uiservices.hxx"
26 #include <sfx2/sfxsids.hrc>
27 #include "dbu_rel.hrc"
28 #include <vcl/svapp.hxx>
29 #include "browserids.hxx"
30 #include <comphelper/types.hxx>
31 #include "dbustrings.hrc"
32 #include <connectivity/dbtools.hxx>
33 #include <com/sun/star/frame/FrameSearchFlag.hpp>
34 #include <comphelper/extract.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <com/sun/star/container/XChild.hpp>
37 #include <com/sun/star/container/XNameContainer.hpp>
38 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
39 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
40 #include <com/sun/star/sdbcx/KeyType.hpp>
41 #include <com/sun/star/sdbcx/XDrop.hpp>
42 #include <com/sun/star/sdbcx/XAlterTable.hpp>
43 #include <com/sun/star/sdbcx/XAppend.hpp>
44 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
45 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
46 #include <com/sun/star/sdb/SQLContext.hpp>
47 #include <com/sun/star/sdbc/SQLWarning.hpp>
48 #include <com/sun/star/sdbc/ColumnValue.hpp>
49 #include <com/sun/star/sdbc/XRow.hpp>
50 #include <connectivity/dbexception.hxx>
51 #include <connectivity/dbmetadata.hxx>
52 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
53 #include <comphelper/streamsection.hxx>
54 #include <comphelper/seqstream.hxx>
55 #include <com/sun/star/io/XActiveDataSource.hpp>
56 #include <com/sun/star/io/XActiveDataSink.hpp>
57 #include "sqlmessage.hxx"
58 #include "RelationController.hxx"
59 #include <vcl/layout.hxx>
60 #include "TableWindowData.hxx"
61 #include "UITools.hxx"
62 #include "RTableConnectionData.hxx"
63 #include "RelationTableView.hxx"
64 #include "RelationDesignView.hxx"
65 #include <tools/debug.hxx>
66 #include <tools/diagnose_ex.h>
67 #include <vcl/waitobj.hxx>
68 #include <osl/thread.hxx>
69 #include <osl/mutex.hxx>
71 #define MAX_THREADS 10
73 extern "C" void SAL_CALL
createRegistryInfo_ORelationControl()
75 static ::dbaui::OMultiInstanceAutoRegistration
< ::dbaui::ORelationController
> aAutoRegistration
;
78 using namespace ::com::sun::star::uno
;
79 using namespace ::com::sun::star::io
;
80 using namespace ::com::sun::star::beans
;
81 using namespace ::com::sun::star::frame
;
82 using namespace ::com::sun::star::lang
;
83 using namespace ::com::sun::star::container
;
84 using namespace ::com::sun::star::sdbcx
;
85 using namespace ::com::sun::star::sdbc
;
86 using namespace ::com::sun::star::sdb
;
87 using namespace ::com::sun::star::ui::dialogs
;
88 using namespace ::com::sun::star::util
;
89 using namespace ::dbtools
;
90 using namespace ::dbaui
;
91 using namespace ::comphelper
;
92 using namespace ::osl
;
94 OUString SAL_CALL
ORelationController::getImplementationName() throw( RuntimeException
, std::exception
)
96 return getImplementationName_Static();
99 OUString
ORelationController::getImplementationName_Static() throw( RuntimeException
)
101 return OUString("org.openoffice.comp.dbu.ORelationDesign");
104 Sequence
< OUString
> ORelationController::getSupportedServiceNames_Static() throw( RuntimeException
)
106 Sequence
< OUString
> aSupported(1);
107 aSupported
[0] = "com.sun.star.sdb.RelationDesign";
111 Sequence
< OUString
> SAL_CALL
ORelationController::getSupportedServiceNames() throw(RuntimeException
, std::exception
)
113 return getSupportedServiceNames_Static();
116 Reference
< XInterface
> SAL_CALL
ORelationController::Create(const Reference
<XMultiServiceFactory
>& _rxFactory
)
118 return *(new ORelationController(comphelper::getComponentContext(_rxFactory
)));
121 ORelationController::ORelationController(const Reference
< XComponentContext
>& _rM
)
122 : OJoinController(_rM
)
124 ,m_bRelationsPossible(true)
129 ORelationController::~ORelationController()
133 FeatureState
ORelationController::GetState(sal_uInt16 _nId
) const
135 FeatureState aReturn
;
136 aReturn
.bEnabled
= m_bRelationsPossible
;
139 case SID_RELATION_ADD_RELATION
:
140 aReturn
.bEnabled
= !m_vTableData
.empty() && isConnected() && isEditable();
141 aReturn
.bChecked
= false;
143 case ID_BROWSER_SAVEDOC
:
144 aReturn
.bEnabled
= haveDataSource() && impl_isModified();
147 aReturn
= OJoinController::GetState(_nId
);
153 void ORelationController::Execute(sal_uInt16 _nId
, const Sequence
< PropertyValue
>& aArgs
)
157 case ID_BROWSER_SAVEDOC
:
159 OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!");
160 if(!::dbaui::checkDataSourceAvailable(::comphelper::getString(getDataSource()->getPropertyValue(PROPERTY_NAME
)), getORB()))
162 OUString
aMessage(ModuleRes(STR_DATASOURCE_DELETED
));
163 ScopedVclPtr
<OSQLWarningBox
>::Create( getView(), aMessage
)->Execute();
167 // now we save the layout information
168 // create the output stream
171 if ( haveDataSource() && getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION
) )
173 ::comphelper::NamedValueCollection aWindowsData
;
174 saveTableWindows( aWindowsData
);
175 getDataSource()->setPropertyValue( PROPERTY_LAYOUTINFORMATION
, makeAny( aWindowsData
.getPropertyValues() ) );
176 setModified(sal_False
);
179 catch ( const Exception
& )
181 DBG_UNHANDLED_EXCEPTION();
186 case SID_RELATION_ADD_RELATION
:
187 static_cast<ORelationTableView
*>(static_cast<ORelationDesignView
*>( getView() )->getTableView())->AddNewRelation();
190 OJoinController::Execute(_nId
,aArgs
);
193 InvalidateFeature(_nId
);
196 void ORelationController::impl_initialize()
198 OJoinController::impl_initialize();
200 if( !getSdbMetaData().supportsRelations() )
201 {// check if this database supports relations
204 m_bRelationsPossible
= false;
206 OUString
sTitle(ModuleRes(STR_RELATIONDESIGN
));
207 sTitle
= sTitle
.copy(3);
208 ScopedVclPtrInstance
< OSQLMessageBox
> aDlg(nullptr,sTitle
,ModuleRes(STR_RELATIONDESIGN_NOT_AVAILABLE
));
212 throw SQLException();
215 if(!m_bRelationsPossible
)
218 // we need a datasource
219 OSL_ENSURE(haveDataSource(),"ORelationController::initialize: need a datasource!");
221 Reference
<XTablesSupplier
> xSup(getConnection(),UNO_QUERY
);
222 OSL_ENSURE(xSup
.is(),"Connection isn't a XTablesSupplier!");
224 m_xTables
= xSup
->getTables();
225 // load the layoutInformation
226 loadLayoutInformation();
230 if ( !m_nThreadEvent
)
231 Application::PostUserEvent(LINK(this, ORelationController
, OnThreadFinished
));
233 catch( const Exception
& )
235 DBG_UNHANDLED_EXCEPTION();
240 OUString
ORelationController::getPrivateTitle( ) const
242 OUString sName
= getDataSourceName();
243 return ::dbaui::getStrippedDatabaseName(getDataSource(),sName
);
246 bool ORelationController::Construct(vcl::Window
* pParent
)
248 setView( VclPtr
<ORelationDesignView
>::Create( pParent
, *this, getORB() ) );
249 OJoinController::Construct(pParent
);
253 short ORelationController::saveModified()
255 short nSaved
= RET_YES
;
256 if(haveDataSource() && isModified())
258 ScopedVclPtrInstance
<MessageDialog
> aQry(getView(), "DesignSaveModifiedDialog",
259 "dbaccess/ui/designsavemodifieddialog.ui");
260 nSaved
= aQry
->Execute();
261 if(nSaved
== RET_YES
)
262 Execute(ID_BROWSER_SAVEDOC
,Sequence
<PropertyValue
>());
267 void ORelationController::describeSupportedFeatures()
269 OJoinController::describeSupportedFeatures();
270 implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION
, CommandGroup::EDIT
);
275 class RelationLoader
: public ::osl::Thread
277 typedef std::map
<OUString
, ::boost::shared_ptr
<OTableWindowData
>, ::comphelper::UStringMixLess
> TTableDataHelper
;
278 TTableDataHelper m_aTableData
;
279 TTableConnectionData m_vTableConnectionData
;
280 const Sequence
< OUString
> m_aTableList
;
281 ORelationController
* m_pParent
;
282 const Reference
< XDatabaseMetaData
> m_xMetaData
;
283 const Reference
< XNameAccess
> m_xTables
;
284 const sal_Int32 m_nStartIndex
;
285 const sal_Int32 m_nEndIndex
;
288 RelationLoader(ORelationController
* _pParent
289 ,const Reference
< XDatabaseMetaData
>& _xMetaData
290 ,const Reference
< XNameAccess
>& _xTables
291 ,const Sequence
< OUString
>& _aTableList
292 ,const sal_Int32 _nStartIndex
293 ,const sal_Int32 _nEndIndex
)
294 :m_aTableData(_xMetaData
.is() && _xMetaData
->supportsMixedCaseQuotedIdentifiers())
295 ,m_aTableList(_aTableList
)
297 ,m_xMetaData(_xMetaData
)
299 ,m_nStartIndex(_nStartIndex
)
300 ,m_nEndIndex(_nEndIndex
)
304 /// Working method which should be overridden.
305 virtual void SAL_CALL
run() SAL_OVERRIDE
;
306 virtual void SAL_CALL
onTerminated() SAL_OVERRIDE
;
308 virtual ~RelationLoader(){}
310 void loadTableData(const Any
& _aTable
);
313 void SAL_CALL
RelationLoader::run()
315 osl_setThreadName("RelationLoader");
317 const OUString
* pIter
= m_aTableList
.getConstArray() + m_nStartIndex
;
318 for(sal_Int32 i
= m_nStartIndex
; i
< m_nEndIndex
;++i
,++pIter
)
320 OUString sCatalog
,sSchema
,sTable
;
321 ::dbtools::qualifiedNameComponents(m_xMetaData
,
326 ::dbtools::eInDataManipulation
);
328 if ( !sCatalog
.isEmpty() )
329 aCatalog
<<= sCatalog
;
333 Reference
< XResultSet
> xResult
= m_xMetaData
->getImportedKeys(aCatalog
, sSchema
,sTable
);
334 if ( xResult
.is() && xResult
->next() )
336 ::comphelper::disposeComponent(xResult
);
337 loadTableData(m_xTables
->getByName(*pIter
));
340 catch( const Exception
& )
342 DBG_UNHANDLED_EXCEPTION();
346 void SAL_CALL
RelationLoader::onTerminated()
348 m_pParent
->mergeData(m_vTableConnectionData
);
352 void RelationLoader::loadTableData(const Any
& _aTable
)
354 Reference
<XPropertySet
> xTableProp(_aTable
,UNO_QUERY
);
355 const OUString sSourceName
= ::dbtools::composeTableName( m_xMetaData
, xTableProp
, ::dbtools::eInTableDefinitions
, false, false, false );
356 TTableDataHelper::iterator aFind
= m_aTableData
.find(sSourceName
);
357 if ( aFind
== m_aTableData
.end() )
359 aFind
= m_aTableData
.insert(TTableDataHelper::value_type(sSourceName
,::boost::shared_ptr
<OTableWindowData
>(new OTableWindowData(xTableProp
,sSourceName
, sSourceName
)))).first
;
360 aFind
->second
->ShowAll(false);
362 TTableWindowData::value_type pReferencingTable
= aFind
->second
;
363 Reference
<XIndexAccess
> xKeys
= pReferencingTable
->getKeys();
364 const Reference
<XKeysSupplier
> xKeySup(xTableProp
,UNO_QUERY
);
366 if ( !xKeys
.is() && xKeySup
.is() )
368 xKeys
= xKeySup
->getKeys();
373 Reference
<XPropertySet
> xKey
;
374 const sal_Int32 nCount
= xKeys
->getCount();
375 for(sal_Int32 i
= 0 ; i
< nCount
; ++i
)
377 xKeys
->getByIndex(i
) >>= xKey
;
378 sal_Int32 nKeyType
= 0;
379 xKey
->getPropertyValue(PROPERTY_TYPE
) >>= nKeyType
;
380 if ( KeyType::FOREIGN
== nKeyType
)
382 OUString sReferencedTable
;
383 xKey
->getPropertyValue(PROPERTY_REFERENCEDTABLE
) >>= sReferencedTable
;
386 TTableDataHelper::iterator aRefFind
= m_aTableData
.find(sReferencedTable
);
387 if ( aRefFind
== m_aTableData
.end() )
389 if ( m_xTables
->hasByName(sReferencedTable
) )
391 Reference
<XPropertySet
> xReferencedTable(m_xTables
->getByName(sReferencedTable
),UNO_QUERY
);
392 aRefFind
= m_aTableData
.insert(TTableDataHelper::value_type(sReferencedTable
,::boost::shared_ptr
<OTableWindowData
>(new OTableWindowData(xReferencedTable
,sReferencedTable
, sReferencedTable
)))).first
;
393 aRefFind
->second
->ShowAll(false);
396 continue; // table name could not be found so we do not show this table releation
398 TTableWindowData::value_type pReferencedTable
= aRefFind
->second
;
401 xKey
->getPropertyValue(PROPERTY_NAME
) >>= sKeyName
;
403 ORelationTableConnectionData
* pTabConnData
= new ORelationTableConnectionData( pReferencingTable
, pReferencedTable
, sKeyName
);
404 m_vTableConnectionData
.push_back(TTableConnectionData::value_type(pTabConnData
));
406 const Reference
<XColumnsSupplier
> xColsSup(xKey
,UNO_QUERY
);
407 OSL_ENSURE(xColsSup
.is(),"Key is no XColumnsSupplier!");
408 const Reference
<XNameAccess
> xColumns
= xColsSup
->getColumns();
409 const Sequence
< OUString
> aNames
= xColumns
->getElementNames();
410 const OUString
* pIter
= aNames
.getConstArray();
411 const OUString
* pEnd
= pIter
+ aNames
.getLength();
412 OUString sColumnName
,sRelatedName
;
413 for(sal_uInt16 j
=0;pIter
!= pEnd
;++pIter
,++j
)
415 const Reference
<XPropertySet
> xPropSet(xColumns
->getByName(*pIter
),UNO_QUERY
);
416 OSL_ENSURE(xPropSet
.is(),"Invalid column found in KeyColumns!");
419 xPropSet
->getPropertyValue(PROPERTY_NAME
) >>= sColumnName
;
420 xPropSet
->getPropertyValue(PROPERTY_RELATEDCOLUMN
) >>= sRelatedName
;
422 pTabConnData
->SetConnLine( j
, sColumnName
, sRelatedName
);
424 // Update/Del-Flags setzen
425 sal_Int32 nUpdateRule
= 0;
426 sal_Int32 nDeleteRule
= 0;
427 xKey
->getPropertyValue(PROPERTY_UPDATERULE
) >>= nUpdateRule
;
428 xKey
->getPropertyValue(PROPERTY_DELETERULE
) >>= nDeleteRule
;
430 pTabConnData
->SetUpdateRules( nUpdateRule
);
431 pTabConnData
->SetDeleteRules( nDeleteRule
);
433 // Kardinalitaet setzen
434 pTabConnData
->SetCardinality();
441 void ORelationController::mergeData(const TTableConnectionData
& _aConnectionData
)
443 ::osl::MutexGuard
aGuard( getMutex() );
445 ::std::copy( _aConnectionData
.begin(), _aConnectionData
.end(), ::std::back_inserter( m_vTableConnectionData
));
446 //const Reference< XDatabaseMetaData> xMetaData = getConnection()->getMetaData();
447 const bool bCase
= true;//xMetaData.is() && xMetaData->supportsMixedCaseQuotedIdentifiers();
448 // here we are finished, so we can collect the table from connection data
449 TTableConnectionData::iterator aConnDataIter
= m_vTableConnectionData
.begin();
450 TTableConnectionData::iterator aConnDataEnd
= m_vTableConnectionData
.end();
451 for(;aConnDataIter
!= aConnDataEnd
;++aConnDataIter
)
453 if ( !existsTable((*aConnDataIter
)->getReferencingTable()->GetComposedName(),bCase
) )
455 m_vTableData
.push_back((*aConnDataIter
)->getReferencingTable());
457 if ( !existsTable((*aConnDataIter
)->getReferencedTable()->GetComposedName(),bCase
) )
459 m_vTableData
.push_back((*aConnDataIter
)->getReferencedTable());
462 if ( m_nThreadEvent
)
465 if ( !m_nThreadEvent
)
466 Application::PostUserEvent(LINK(this, ORelationController
, OnThreadFinished
));
470 IMPL_LINK_NOARG( ORelationController
, OnThreadFinished
)
472 ::SolarMutexGuard aSolarGuard
;
473 ::osl::MutexGuard
aGuard( getMutex() );
476 getView()->initialize(); // show the windows and fill with our information
477 getView()->Invalidate(INVALIDATE_NOERASE
);
479 setModified(sal_False
); // and we are not modified yet
481 if(m_vTableData
.empty())
482 Execute(ID_BROWSER_ADDTABLE
,Sequence
<PropertyValue
>());
484 catch( const Exception
& )
486 DBG_UNHANDLED_EXCEPTION();
488 m_pWaitObject
.reset();
492 void ORelationController::loadData()
494 m_pWaitObject
.reset( new WaitObject(getView()) );
497 if ( !m_xTables
.is() )
499 DatabaseMetaData
aMeta(getConnection());
500 // this may take some time
501 const Reference
< XDatabaseMetaData
> xMetaData
= getConnection()->getMetaData();
502 const Sequence
< OUString
> aNames
= m_xTables
->getElementNames();
503 const sal_Int32 nCount
= aNames
.getLength();
504 if ( aMeta
.supportsThreads() )
506 const sal_Int32 nMaxElements
= (nCount
/ MAX_THREADS
) +1;
507 sal_Int32 nStart
= 0,nEnd
= ::std::min(nMaxElements
,nCount
);
508 while(nStart
!= nEnd
)
511 RelationLoader
* pThread
= new RelationLoader(this,xMetaData
,m_xTables
,aNames
,nStart
,nEnd
);
512 pThread
->createSuspended();
513 pThread
->setPriority(osl_Thread_PriorityBelowNormal
);
516 nEnd
+= nMaxElements
;
517 nEnd
= ::std::min(nEnd
,nCount
);
522 RelationLoader
* pThread
= new RelationLoader(this,xMetaData
,m_xTables
,aNames
,0,nCount
);
524 pThread
->onTerminated();
527 catch(SQLException
& e
)
529 showError(SQLExceptionInfo(e
));
531 catch(const Exception
&)
533 DBG_UNHANDLED_EXCEPTION();
537 TTableWindowData::value_type
ORelationController::existsTable(const OUString
& _rComposedTableName
,bool _bCase
) const
539 ::comphelper::UStringMixEqual
bCase(_bCase
);
540 TTableWindowData::const_iterator aIter
= m_vTableData
.begin();
541 TTableWindowData::const_iterator aEnd
= m_vTableData
.end();
542 for(;aIter
!= aEnd
;++aIter
)
544 if(bCase((*aIter
)->GetComposedName(),_rComposedTableName
))
547 return ( aIter
!= aEnd
) ? *aIter
: TTableWindowData::value_type();
550 void ORelationController::loadLayoutInformation()
554 OSL_ENSURE(haveDataSource(),"We need a datasource from our connection!");
555 if ( haveDataSource() )
557 if ( getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION
) )
559 Sequence
<PropertyValue
> aWindows
;
560 getDataSource()->getPropertyValue(PROPERTY_LAYOUTINFORMATION
) >>= aWindows
;
561 loadTableWindows(aWindows
);
570 void ORelationController::reset()
572 loadLayoutInformation();
573 ODataView
* pView
= getView();
574 OSL_ENSURE(pView
,"No current view!");
578 pView
->Invalidate(INVALIDATE_NOERASE
);
582 bool ORelationController::allowViews() const
587 bool ORelationController::allowQueries() const
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */