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 <strings.hrc>
25 #include <strings.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/weld.hxx>
28 #include <browserids.hxx>
29 #include <comphelper/types.hxx>
30 #include <core_resource.hxx>
31 #include <connectivity/dbtools.hxx>
32 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
33 #include <com/sun/star/sdbcx/KeyType.hpp>
34 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
35 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
36 #include <com/sun/star/sdbc/SQLException.hpp>
37 #include <com/sun/star/sdbc/XRow.hpp>
38 #include <connectivity/dbexception.hxx>
39 #include <connectivity/dbmetadata.hxx>
40 #include <sqlmessage.hxx>
41 #include <RelationController.hxx>
42 #include <TableWindowData.hxx>
43 #include <UITools.hxx>
44 #include <RTableConnectionData.hxx>
45 #include <RelationTableView.hxx>
46 #include <RelationDesignView.hxx>
47 #include <comphelper/diagnose_ex.hxx>
48 #include <osl/thread.hxx>
49 #include <osl/mutex.hxx>
51 #define MAX_THREADS 10
53 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
54 org_openoffice_comp_dbu_ORelationDesign_get_implementation(
55 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
57 return cppu::acquire(new ::dbaui::ORelationController(context
));
60 using namespace ::com::sun::star::uno
;
61 using namespace ::com::sun::star::io
;
62 using namespace ::com::sun::star::beans
;
63 using namespace ::com::sun::star::frame
;
64 using namespace ::com::sun::star::lang
;
65 using namespace ::com::sun::star::container
;
66 using namespace ::com::sun::star::sdbcx
;
67 using namespace ::com::sun::star::sdbc
;
68 using namespace ::com::sun::star::sdb
;
69 using namespace ::com::sun::star::ui::dialogs
;
70 using namespace ::com::sun::star::util
;
71 using namespace ::dbtools
;
72 using namespace ::dbaui
;
73 using namespace ::comphelper
;
74 using namespace ::osl
;
76 OUString SAL_CALL
ORelationController::getImplementationName()
78 return "org.openoffice.comp.dbu.ORelationDesign";
81 Sequence
< OUString
> SAL_CALL
ORelationController::getSupportedServiceNames()
83 return { "com.sun.star.sdb.RelationDesign" };
86 ORelationController::ORelationController(const Reference
< XComponentContext
>& _rM
)
87 : OJoinController(_rM
)
89 ,m_bRelationsPossible(true)
94 ORelationController::~ORelationController()
98 FeatureState
ORelationController::GetState(sal_uInt16 _nId
) const
100 FeatureState aReturn
;
101 aReturn
.bEnabled
= m_bRelationsPossible
;
104 case SID_RELATION_ADD_RELATION
:
105 aReturn
.bEnabled
= !m_vTableData
.empty() && isConnected() && isEditable();
106 aReturn
.bChecked
= false;
108 case ID_BROWSER_SAVEDOC
:
109 aReturn
.bEnabled
= haveDataSource() && impl_isModified();
112 aReturn
= OJoinController::GetState(_nId
);
118 void ORelationController::Execute(sal_uInt16 _nId
, const Sequence
< PropertyValue
>& aArgs
)
122 case ID_BROWSER_SAVEDOC
:
124 OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!");
125 if(!::dbaui::checkDataSourceAvailable(::comphelper::getString(getDataSource()->getPropertyValue(PROPERTY_NAME
)), getORB()))
127 OUString
aMessage(DBA_RES(STR_DATASOURCE_DELETED
));
128 OSQLWarningBox
aWarning(getFrameWeld(), aMessage
);
133 // now we save the layout information
134 // create the output stream
137 if ( haveDataSource() && getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION
) )
139 ::comphelper::NamedValueCollection aWindowsData
;
140 saveTableWindows( aWindowsData
);
141 getDataSource()->setPropertyValue( PROPERTY_LAYOUTINFORMATION
, Any( aWindowsData
.getPropertyValues() ) );
145 catch ( const Exception
& )
147 DBG_UNHANDLED_EXCEPTION("dbaccess");
152 case SID_RELATION_ADD_RELATION
:
153 static_cast<ORelationTableView
*>(static_cast<ORelationDesignView
*>( getView() )->getTableView())->AddNewRelation();
156 OJoinController::Execute(_nId
,aArgs
);
159 InvalidateFeature(_nId
);
162 void ORelationController::impl_initialize()
164 OJoinController::impl_initialize();
166 if( !getSdbMetaData().supportsRelations() )
167 {// check if this database supports relations
170 m_bRelationsPossible
= false;
172 OUString
sTitle(DBA_RES(STR_RELATIONDESIGN
));
173 sTitle
= sTitle
.copy(3);
174 OSQLMessageBox
aDlg(getFrameWeld(), sTitle
, DBA_RES(STR_RELATIONDESIGN_NOT_AVAILABLE
));
178 throw SQLException();
181 if(!m_bRelationsPossible
)
184 // we need a datasource
185 OSL_ENSURE(haveDataSource(),"ORelationController::initialize: need a datasource!");
187 Reference
<XTablesSupplier
> xSup(getConnection(),UNO_QUERY
);
188 OSL_ENSURE(xSup
.is(),"Connection isn't a XTablesSupplier!");
190 m_xTables
= xSup
->getTables();
191 // load the layoutInformation
192 loadLayoutInformation();
196 if ( !m_nThreadEvent
)
197 Application::PostUserEvent(LINK(this, ORelationController
, OnThreadFinished
));
199 catch( const Exception
& )
201 DBG_UNHANDLED_EXCEPTION("dbaccess");
206 OUString
ORelationController::getPrivateTitle( ) const
208 OUString sName
= getDataSourceName();
209 return ::dbaui::getStrippedDatabaseName(getDataSource(),sName
);
212 bool ORelationController::Construct(vcl::Window
* pParent
)
214 setView( VclPtr
<ORelationDesignView
>::Create( pParent
, *this, getORB() ) );
215 OJoinController::Construct(pParent
);
219 short ORelationController::saveModified()
221 short nSaved
= RET_YES
;
222 if(haveDataSource() && isModified())
224 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/designsavemodifieddialog.ui"));
225 std::unique_ptr
<weld::MessageDialog
> xQuery(xBuilder
->weld_message_dialog("DesignSaveModifiedDialog"));
226 nSaved
= xQuery
->run();
227 if(nSaved
== RET_YES
)
228 Execute(ID_BROWSER_SAVEDOC
,Sequence
<PropertyValue
>());
233 void ORelationController::describeSupportedFeatures()
235 OJoinController::describeSupportedFeatures();
236 implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION
, CommandGroup::EDIT
);
241 class RelationLoader
: public ::osl::Thread
243 typedef std::map
<OUString
, std::shared_ptr
<OTableWindowData
>, ::comphelper::UStringMixLess
> TTableDataHelper
;
244 TTableDataHelper m_aTableData
;
245 TTableConnectionData m_vTableConnectionData
;
246 const Sequence
< OUString
> m_aTableList
;
247 ORelationController
* m_pParent
;
248 const Reference
< XDatabaseMetaData
> m_xMetaData
;
249 const Reference
< XNameAccess
> m_xTables
;
250 const sal_Int32 m_nStartIndex
;
251 const sal_Int32 m_nEndIndex
;
254 RelationLoader(ORelationController
* _pParent
255 ,const Reference
< XDatabaseMetaData
>& _xMetaData
256 ,const Reference
< XNameAccess
>& _xTables
257 ,const Sequence
< OUString
>& _aTableList
258 ,const sal_Int32 _nStartIndex
259 ,const sal_Int32 _nEndIndex
)
260 :m_aTableData(_xMetaData
.is() && _xMetaData
->supportsMixedCaseQuotedIdentifiers())
261 ,m_aTableList(_aTableList
)
263 ,m_xMetaData(_xMetaData
)
265 ,m_nStartIndex(_nStartIndex
)
266 ,m_nEndIndex(_nEndIndex
)
270 /// Working method which should be overridden.
271 virtual void SAL_CALL
run() override
;
272 virtual void SAL_CALL
onTerminated() override
;
274 virtual ~RelationLoader() override
{}
276 void loadTableData(const Any
& _aTable
);
279 void SAL_CALL
RelationLoader::run()
281 osl_setThreadName("RelationLoader");
283 for(sal_Int32 i
= m_nStartIndex
; i
< m_nEndIndex
; ++i
)
285 OUString sCatalog
,sSchema
,sTable
;
286 ::dbtools::qualifiedNameComponents(m_xMetaData
,
291 ::dbtools::EComposeRule::InDataManipulation
);
293 if ( !sCatalog
.isEmpty() )
294 aCatalog
<<= sCatalog
;
298 Reference
< XResultSet
> xResult
= m_xMetaData
->getImportedKeys(aCatalog
, sSchema
,sTable
);
299 if ( xResult
.is() && xResult
->next() )
301 ::comphelper::disposeComponent(xResult
);
302 loadTableData(m_xTables
->getByName(m_aTableList
[i
]));
305 catch( const Exception
& )
307 DBG_UNHANDLED_EXCEPTION("dbaccess");
311 void SAL_CALL
RelationLoader::onTerminated()
313 m_pParent
->mergeData(m_vTableConnectionData
);
317 void RelationLoader::loadTableData(const Any
& _aTable
)
319 Reference
<XPropertySet
> xTableProp(_aTable
,UNO_QUERY
);
320 const OUString sSourceName
= ::dbtools::composeTableName( m_xMetaData
, xTableProp
, ::dbtools::EComposeRule::InTableDefinitions
, false );
321 TTableDataHelper::const_iterator aFind
= m_aTableData
.find(sSourceName
);
322 if ( aFind
== m_aTableData
.end() )
324 aFind
= m_aTableData
.emplace(sSourceName
,std::make_shared
<OTableWindowData
>(xTableProp
,sSourceName
, sSourceName
, OUString())).first
;
325 aFind
->second
->ShowAll(false);
327 TTableWindowData::value_type pReferencingTable
= aFind
->second
;
328 Reference
<XIndexAccess
> xKeys
= pReferencingTable
->getKeys();
329 const Reference
<XKeysSupplier
> xKeySup(xTableProp
,UNO_QUERY
);
331 if ( !xKeys
.is() && xKeySup
.is() )
333 xKeys
= xKeySup
->getKeys();
339 Reference
<XPropertySet
> xKey
;
340 const sal_Int32 nCount
= xKeys
->getCount();
341 for(sal_Int32 i
= 0 ; i
< nCount
; ++i
)
343 xKeys
->getByIndex(i
) >>= xKey
;
344 sal_Int32 nKeyType
= 0;
345 xKey
->getPropertyValue(PROPERTY_TYPE
) >>= nKeyType
;
346 if ( KeyType::FOREIGN
== nKeyType
)
348 OUString sReferencedTable
;
349 xKey
->getPropertyValue(PROPERTY_REFERENCEDTABLE
) >>= sReferencedTable
;
352 TTableDataHelper::const_iterator aRefFind
= m_aTableData
.find(sReferencedTable
);
353 if ( aRefFind
== m_aTableData
.end() )
355 if ( m_xTables
->hasByName(sReferencedTable
) )
357 Reference
<XPropertySet
> xReferencedTable(m_xTables
->getByName(sReferencedTable
),UNO_QUERY
);
358 aRefFind
= m_aTableData
.emplace(sReferencedTable
,std::make_shared
<OTableWindowData
>(xReferencedTable
,sReferencedTable
, sReferencedTable
, OUString())).first
;
359 aRefFind
->second
->ShowAll(false);
362 continue; // table name could not be found so we do not show this table relation
364 TTableWindowData::value_type pReferencedTable
= aRefFind
->second
;
367 xKey
->getPropertyValue(PROPERTY_NAME
) >>= sKeyName
;
369 auto xTabConnData
= std::make_shared
<ORelationTableConnectionData
>( pReferencingTable
, pReferencedTable
, sKeyName
);
370 m_vTableConnectionData
.push_back(xTabConnData
);
372 const Reference
<XColumnsSupplier
> xColsSup(xKey
,UNO_QUERY
);
373 OSL_ENSURE(xColsSup
.is(),"Key is no XColumnsSupplier!");
374 const Reference
<XNameAccess
> xColumns
= xColsSup
->getColumns();
375 const Sequence
< OUString
> aNames
= xColumns
->getElementNames();
376 OUString sColumnName
,sRelatedName
;
377 for(sal_Int32 j
=0;j
<aNames
.getLength();++j
)
379 const Reference
<XPropertySet
> xPropSet(xColumns
->getByName(aNames
[j
]),UNO_QUERY
);
380 OSL_ENSURE(xPropSet
.is(),"Invalid column found in KeyColumns!");
383 xPropSet
->getPropertyValue(PROPERTY_NAME
) >>= sColumnName
;
384 xPropSet
->getPropertyValue(PROPERTY_RELATEDCOLUMN
) >>= sRelatedName
;
386 xTabConnData
->SetConnLine( j
, sColumnName
, sRelatedName
);
388 // set update/del flags
389 sal_Int32 nUpdateRule
= 0;
390 sal_Int32 nDeleteRule
= 0;
391 xKey
->getPropertyValue(PROPERTY_UPDATERULE
) >>= nUpdateRule
;
392 xKey
->getPropertyValue(PROPERTY_DELETERULE
) >>= nDeleteRule
;
394 xTabConnData
->SetUpdateRules( nUpdateRule
);
395 xTabConnData
->SetDeleteRules( nDeleteRule
);
398 xTabConnData
->SetCardinality();
404 void ORelationController::mergeData(const TTableConnectionData
& _aConnectionData
)
406 ::osl::MutexGuard
aGuard( getMutex() );
408 m_vTableConnectionData
.insert( m_vTableConnectionData
.end(), _aConnectionData
.begin(), _aConnectionData
.end() );
409 // here we are finished, so we can collect the table from connection data
410 for (auto const& elem
: m_vTableConnectionData
)
412 if ( !existsTable(elem
->getReferencingTable()->GetComposedName()) )
414 m_vTableData
.push_back(elem
->getReferencingTable());
416 if ( !existsTable(elem
->getReferencedTable()->GetComposedName()) )
418 m_vTableData
.push_back(elem
->getReferencedTable());
421 if ( m_nThreadEvent
)
424 if ( !m_nThreadEvent
)
425 Application::PostUserEvent(LINK(this, ORelationController
, OnThreadFinished
));
429 IMPL_LINK_NOARG( ORelationController
, OnThreadFinished
, void*, void )
431 ::SolarMutexGuard aSolarGuard
;
432 ::osl::MutexGuard
aGuard( getMutex() );
435 getView()->initialize(); // show the windows and fill with our information
436 getView()->Invalidate(InvalidateFlags::NoErase
);
438 setModified(false); // and we are not modified yet
440 if(m_vTableData
.empty())
441 Execute(ID_BROWSER_ADDTABLE
,Sequence
<PropertyValue
>());
443 catch( const Exception
& )
445 DBG_UNHANDLED_EXCEPTION("dbaccess");
447 m_xWaitObject
.reset();
450 void ORelationController::loadData()
452 m_xWaitObject
.reset(new weld::WaitObject(getFrameWeld()));
455 if ( !m_xTables
.is() )
457 DatabaseMetaData
aMeta(getConnection());
458 // this may take some time
459 const Reference
< XDatabaseMetaData
> xMetaData
= getConnection()->getMetaData();
460 const Sequence
< OUString
> aNames
= m_xTables
->getElementNames();
461 const sal_Int32 nCount
= aNames
.getLength();
462 if ( aMeta
.supportsThreads() )
464 const sal_Int32 nMaxElements
= (nCount
/ MAX_THREADS
) +1;
465 sal_Int32 nStart
= 0,nEnd
= std::min(nMaxElements
,nCount
);
466 while(nStart
!= nEnd
)
469 RelationLoader
* pThread
= new RelationLoader(this,xMetaData
,m_xTables
,aNames
,nStart
,nEnd
);
470 pThread
->createSuspended();
471 pThread
->setPriority(osl_Thread_PriorityBelowNormal
);
474 nEnd
+= nMaxElements
;
475 nEnd
= std::min(nEnd
,nCount
);
480 RelationLoader
* pThread
= new RelationLoader(this,xMetaData
,m_xTables
,aNames
,0,nCount
);
482 pThread
->onTerminated();
485 catch(SQLException
& e
)
487 showError(SQLExceptionInfo(e
));
489 catch(const Exception
&)
491 DBG_UNHANDLED_EXCEPTION("dbaccess");
495 TTableWindowData::value_type
ORelationController::existsTable(std::u16string_view _rComposedTableName
) const
497 ::comphelper::UStringMixEqual
bCase(true);
498 for (auto const& elem
: m_vTableData
)
500 if(bCase(elem
->GetComposedName(),_rComposedTableName
))
503 return TTableWindowData::value_type();
506 void ORelationController::loadLayoutInformation()
510 OSL_ENSURE(haveDataSource(),"We need a datasource from our connection!");
511 if ( haveDataSource() )
513 if ( getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION
) )
515 Sequence
<PropertyValue
> aWindows
;
516 getDataSource()->getPropertyValue(PROPERTY_LAYOUTINFORMATION
) >>= aWindows
;
517 loadTableWindows(aWindows
);
526 void ORelationController::reset()
528 loadLayoutInformation();
529 ODataView
* pView
= getView();
530 OSL_ENSURE(pView
,"No current view!");
534 pView
->Invalidate(InvalidateFlags::NoErase
);
538 bool ORelationController::allowViews() const
543 bool ORelationController::allowQueries() const
548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */