1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "tablespage.hxx"
21 #include <dsitems.hxx>
22 #include <datasourceconnector.hxx>
23 #include <comphelper/types.hxx>
24 #include <connectivity/dbtools.hxx>
25 #include <connectivity/dbexception.hxx>
26 #include <stringlistitem.hxx>
27 #include <svl/stritem.hxx>
28 #include <strings.hxx>
29 #include <com/sun/star/sdbc/SQLException.hpp>
30 #include <com/sun/star/util/XModifiable.hpp>
31 #include <sqlmessage.hxx>
32 #include <UITools.hxx>
33 #include <osl/diagnose.h>
34 #include <TablesSingleDlg.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <cppuhelper/exc_hlp.hxx>
37 
38 namespace dbaui
39 {
40 
41     using namespace ::com::sun::star::uno;
42     using namespace ::com::sun::star::sdbc;
43     using namespace ::com::sun::star::sdbcx;
44     using namespace ::com::sun::star::sdb;
45     using namespace ::com::sun::star::beans;
46     using namespace ::com::sun::star::lang;
47     using namespace ::com::sun::star::container;
48     using namespace ::com::sun::star::util;
49     using namespace ::dbtools;
50     using namespace ::comphelper;
51 
52     // OTableSubscriptionPage
OTableSubscriptionPage(weld::Container * pPage,OTableSubscriptionDialog * pTablesDlg,const SfxItemSet & _rCoreAttrs)53     OTableSubscriptionPage::OTableSubscriptionPage(weld::Container* pPage, OTableSubscriptionDialog* pTablesDlg, const SfxItemSet& _rCoreAttrs)
54         : OGenericAdministrationPage(pPage, pTablesDlg, "dbaccess/ui/tablesfilterpage.ui", "TablesFilterPage", _rCoreAttrs)
55         , m_bCatalogAtStart(true)
56         , m_pTablesDlg(pTablesDlg)
57         , m_xTables(m_xBuilder->weld_widget("TablesFilterPage"))
58         , m_xTablesList(new OTableTreeListBox(m_xBuilder->weld_tree_view("treeview"), true))
59     {
60         m_xTablesList->init();
61 
62         weld::TreeView& rWidget = m_xTablesList->GetWidget();
63 
64         rWidget.set_size_request(rWidget.get_approximate_digit_width() * 48,
65                                  rWidget.get_height_rows(12));
66 
67         // initialize the TabListBox
68         rWidget.set_selection_mode(SelectionMode::Multiple);
69 
70         rWidget.connect_toggled(LINK(this, OTableSubscriptionPage, OnTreeEntryChecked));
71     }
72 
~OTableSubscriptionPage()73     OTableSubscriptionPage::~OTableSubscriptionPage()
74     {
75         // just to make sure that our connection will be removed
76         try
77         {
78             ::comphelper::disposeComponent(m_xCurrentConnection);
79         }
80         catch (RuntimeException&) { }
81     }
82 
implCheckTables(const Sequence<OUString> & _rTables)83     void OTableSubscriptionPage::implCheckTables(const Sequence< OUString >& _rTables)
84     {
85         // the meta data for the current connection, used for splitting up table names
86         Reference< XDatabaseMetaData > xMeta;
87         try
88         {
89             if (m_xCurrentConnection.is())
90                 xMeta = m_xCurrentConnection->getMetaData();
91         }
92         catch(SQLException&)
93         {
94             OSL_FAIL("OTableSubscriptionPage::implCheckTables : could not retrieve the current connection's meta data!");
95         }
96 
97         // uncheck all
98         CheckAll(false);
99 
100         // check the ones which are in the list
101         OUString sCatalog, sSchema, sName;
102 
103         std::unique_ptr<weld::TreeIter> xRootEntry(m_xTablesList->getAllObjectsEntry());
104 
105         for (const OUString& rIncludeTable : _rTables)
106         {
107             if (xMeta.is())
108                 qualifiedNameComponents(xMeta, rIncludeTable, sCatalog, sSchema, sName,::dbtools::EComposeRule::InDataManipulation);
109             else
110                 sName = rIncludeTable;
111 
112             bool bAllTables = (1 == sName.getLength()) && ('%' == sName[0]);
113             bool bAllSchemas = (1 == sSchema.getLength()) && ('%' == sSchema[0]);
114 
115             // the catalog entry
116             std::unique_ptr<weld::TreeIter> xCatalog(m_xTablesList->GetEntryPosByName(sCatalog, xRootEntry.get()));
117             if (!(xCatalog || sCatalog.isEmpty()))
118                 // the table (resp. its catalog) referred in this filter entry does not exist anymore
119                 continue;
120 
121             if (bAllSchemas && xCatalog)
122             {
123                 m_xTablesList->checkWildcard(*xCatalog);
124                 continue;
125             }
126 
127             // the schema entry
128             std::unique_ptr<weld::TreeIter> xSchema = m_xTablesList->GetEntryPosByName(sSchema, (xCatalog ? xCatalog.get() : xRootEntry.get()));
129             if (!(xSchema || sSchema.isEmpty()))
130                 // the table (resp. its schema) referred in this filter entry does not exist anymore
131                 continue;
132 
133             if (bAllTables && xSchema)
134             {
135                 m_xTablesList->checkWildcard(*xSchema);
136                 continue;
137             }
138 
139             std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetEntryPosByName(sName, xSchema ? xSchema.get() : (xCatalog ? xCatalog.get() : xRootEntry.get())));
140             if (xEntry)
141                 m_xTablesList->GetWidget().set_toggle(*xEntry, TRISTATE_TRUE);
142         }
143         m_xTablesList->CheckButtons();
144     }
145 
implCompleteTablesCheck(const css::uno::Sequence<OUString> & _rTableFilter)146     void OTableSubscriptionPage::implCompleteTablesCheck( const css::uno::Sequence< OUString >& _rTableFilter )
147     {
148         if (!_rTableFilter.hasElements())
149         {   // no tables visible
150             CheckAll(false);
151         }
152         else
153         {
154             if ((1 == _rTableFilter.getLength()) && _rTableFilter[0] == "%")
155             {   // all tables visible
156                 CheckAll();
157             }
158             else
159                 implCheckTables( _rTableFilter );
160         }
161     }
162 
implInitControls(const SfxItemSet & _rSet,bool _bSaveValue)163     void OTableSubscriptionPage::implInitControls(const SfxItemSet& _rSet, bool _bSaveValue)
164     {
165         // check whether or not the selection is invalid or readonly (invalid implies readonly, but not vice versa)
166         bool bValid, bReadonly;
167         getFlags(_rSet, bValid, bReadonly);
168 
169         // get the name of the data source we're working for
170         const SfxStringItem* pNameItem = _rSet.GetItem<SfxStringItem>(DSID_NAME);
171         OSL_ENSURE(pNameItem, "OTableSubscriptionPage::implInitControls: missing the name attribute!");
172         OUString sDSName = pNameItem->GetValue();
173 
174         if (bValid && !sDSName.isEmpty() && !m_xCurrentConnection.is() )
175         {   // get the current table list from the connection for the current settings
176 
177             // the PropertyValues for the current dialog settings
178             Sequence< PropertyValue > aConnectionParams;
179             OSL_ENSURE(m_pTablesDlg, "OTableSubscriptionPage::implInitControls: need a parent dialog doing the translation!");
180             if ( m_pTablesDlg )
181             {
182                 if (!m_pTablesDlg->getCurrentSettings(aConnectionParams))
183                 {
184                     m_xTablesList->GetWidget().clear();
185                     m_pTablesDlg->endExecution();
186                     return;
187                 }
188             }
189 
190             // fill the table list with this connection information
191             SQLExceptionInfo aErrorInfo;
192 
193             try
194             {
195                 weld::WaitObject aWaitCursor(GetFrameWeld());
196 
197                 Reference<XPropertySet> xProp = m_pTablesDlg->getCurrentDataSource();
198                 OSL_ENSURE(xProp.is(),"No data source set!");
199                 if ( xProp.is() )
200                 {
201                     Any aTableFilter = xProp->getPropertyValue(PROPERTY_TABLEFILTER);
202                     Any aTableTypeFilter = xProp->getPropertyValue(PROPERTY_TABLETYPEFILTER);
203 
204                     Reference<XModifiable> xModi(getDataSourceOrModel(xProp),UNO_QUERY);
205                     bool bModified = ( xModi.is() && xModi->isModified() );
206 
207                     Sequence< OUString > aNewTableFilter { "%" };
208                     xProp->setPropertyValue(PROPERTY_TABLEFILTER,makeAny(aNewTableFilter));
209 
210                     xProp->setPropertyValue( PROPERTY_TABLETYPEFILTER, makeAny( Sequence< OUString >() ) );
211                     Reference< css::lang::XEventListener> xEvt;
212                     aErrorInfo = ::dbaui::createConnection(xProp, m_xORB, xEvt, m_xCurrentConnection);
213 
214                     xProp->setPropertyValue(PROPERTY_TABLEFILTER,aTableFilter);
215                     xProp->setPropertyValue(PROPERTY_TABLETYPEFILTER,aTableTypeFilter);
216 
217                     if ( xModi.is() && !bModified )
218                         xModi->setModified(false);
219 
220                 }
221 
222                 if ( m_xCurrentConnection.is() )
223                 {
224                     m_xTablesList->UpdateTableList( m_xCurrentConnection );
225                     if (m_pTablesDlg)
226                         m_pTablesDlg->successfullyConnected();
227                 }
228             }
229             catch (const SQLException&)
230             {
231                 aErrorInfo = ::cppu::getCaughtException();
232             }
233 
234             if (aErrorInfo.isValid())
235             {
236                 // establishing the connection failed. Show an error window and exit.
237                 OSQLMessageBox aMessageBox(GetFrameWeld(), aErrorInfo);
238                 aMessageBox.run();
239                 m_xTables->set_sensitive(false);
240                 m_xTablesList->GetWidget().clear();
241 
242                 if ( m_pTablesDlg )
243                 {
244                     m_pTablesDlg->clearPassword();
245                     m_pTablesDlg->endExecution();
246                 }
247             }
248             else
249             {
250                 // in addition, we need some infos about the connection used
251                 m_sCatalogSeparator = ".";    // (default)
252                 m_bCatalogAtStart = true;   // (default)
253                 try
254                 {
255                     Reference< XDatabaseMetaData > xMeta;
256                     if (m_xCurrentConnection.is())
257                         xMeta = m_xCurrentConnection->getMetaData();
258                     if (xMeta.is() && xMeta->supportsCatalogsInDataManipulation())
259                     {
260                         m_sCatalogSeparator = xMeta->getCatalogSeparator();
261                         m_bCatalogAtStart = xMeta->isCatalogAtStart();
262                     }
263                 }
264                 catch(Exception&)
265                 {
266                     DBG_UNHANDLED_EXCEPTION("dbaccess");
267                 }
268             }
269         }
270 
271         // get the current table filter
272         const OStringListItem* pTableFilter = _rSet.GetItem<OStringListItem>(DSID_TABLEFILTER);
273         Sequence< OUString > aTableFilter;
274         if (pTableFilter)
275             aTableFilter = pTableFilter->getList();
276 
277         implCompleteTablesCheck( aTableFilter );
278 
279         // expand the first entry by default
280         std::unique_ptr<weld::TreeIter> xExpand = m_xTablesList->getAllObjectsEntry();
281         while (xExpand)
282         {
283             m_xTablesList->GetWidget().expand_row(*xExpand);
284             if (!m_xTablesList->GetWidget().iter_children(*xExpand))
285                 break;
286             std::unique_ptr<weld::TreeIter> xSibling(m_xTablesList->GetWidget().make_iterator(xExpand.get()));
287             if (m_xTablesList->GetWidget().iter_next_sibling(*xSibling))
288                 xExpand.reset();
289         }
290 
291         // update the toolbox according the current selection and check state
292         OGenericAdministrationPage::implInitControls(_rSet, _bSaveValue);
293     }
294 
CheckAll(bool _bCheck)295     void OTableSubscriptionPage::CheckAll( bool _bCheck )
296     {
297         std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator());
298         if (m_xTablesList->GetWidget().get_iter_first(*xEntry))
299         {
300             do
301             {
302                 m_xTablesList->GetWidget().set_toggle(*xEntry, _bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
303             }
304             while (m_xTablesList->GetWidget().iter_next(*xEntry));
305         }
306 
307         if (_bCheck)
308         {
309             auto xRoot = m_xTablesList->getAllObjectsEntry();
310             if (xRoot)
311                 m_xTablesList->checkWildcard(*xRoot);
312         }
313     }
314 
DeactivatePage(SfxItemSet * _pSet)315     DeactivateRC OTableSubscriptionPage::DeactivatePage(SfxItemSet* _pSet)
316     {
317         DeactivateRC nResult = OGenericAdministrationPage::DeactivatePage(_pSet);
318 
319         // dispose the connection, we don't need it anymore, so we're not wasting resources
320         try
321         {
322             ::comphelper::disposeComponent(m_xCurrentConnection);
323         }
324         catch (RuntimeException&) { }
325 
326         return nResult;
327     }
328 
IMPL_LINK(OTableSubscriptionPage,OnTreeEntryChecked,const weld::TreeView::iter_col &,rRowCol,void)329     IMPL_LINK(OTableSubscriptionPage, OnTreeEntryChecked, const weld::TreeView::iter_col&, rRowCol, void)
330     {
331         m_xTablesList->checkedButton_noBroadcast(rRowCol.first);
332         callModifiedHdl();
333     }
334 
collectDetailedSelection() const335     Sequence< OUString > OTableSubscriptionPage::collectDetailedSelection() const
336     {
337         Sequence< OUString > aTableFilter;
338         constexpr OUStringLiteral sWildcard = u"%";
339 
340         std::unique_ptr<weld::TreeIter> xAllObjectsEntry(m_xTablesList->getAllObjectsEntry());
341         if (!xAllObjectsEntry)
342             return aTableFilter;
343         std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator(xAllObjectsEntry.get()));
344         if (!m_xTablesList->GetWidget().iter_next(*xEntry))
345             xEntry.reset();
346         while (xEntry)
347         {
348             bool bCatalogWildcard = false;
349             bool bSchemaWildcard =  false;
350             std::unique_ptr<weld::TreeIter> xSchema;
351             std::unique_ptr<weld::TreeIter> xCatalog;
352 
353             if (m_xTablesList->GetWidget().get_toggle(*xEntry) == TRISTATE_TRUE && !m_xTablesList->GetWidget().iter_has_child(*xEntry))
354             {   // checked and a leaf, which means it's no catalog, no schema, but a real table
355                 OUStringBuffer sComposedName;
356                 OUString sCatalog;
357                 if (m_xTablesList->GetWidget().get_iter_depth(*xEntry))
358                 {
359                     xSchema = m_xTablesList->GetWidget().make_iterator(xEntry.get());
360                     m_xTablesList->GetWidget().iter_parent(*xSchema);
361                     if (xAllObjectsEntry->equal(*xSchema))
362                     {
363                         // do not want to have the root entry
364                         xSchema.reset();
365                     }
366 
367                     if (xSchema)
368                     {   // it's a real schema entry, not the "all objects" root
369                         if (m_xTablesList->GetWidget().get_iter_depth(*xSchema))
370                         {
371                             xCatalog = m_xTablesList->GetWidget().make_iterator(xSchema.get());
372                             m_xTablesList->GetWidget().iter_parent(*xCatalog);
373                             if (xAllObjectsEntry->equal(*xCatalog))
374                             {
375                                 // do not want to have the root entry
376                                 xCatalog.reset();
377                             }
378 
379                             if (xCatalog)
380                             {   // it's a real catalog entry, not the "all objects" root
381                                 bCatalogWildcard = m_xTablesList->isWildcardChecked(*xCatalog);
382                                 if (m_bCatalogAtStart)
383                                 {
384                                     sComposedName.append(m_xTablesList->GetWidget().get_text(*xCatalog) + m_sCatalogSeparator);
385                                     if (bCatalogWildcard)
386                                         sComposedName.append(sWildcard);
387                                 }
388                                 else
389                                 {
390                                     if (bCatalogWildcard)
391                                         sCatalog = sWildcard;
392                                     else
393                                         sCatalog.clear();
394                                     sCatalog += m_sCatalogSeparator + m_xTablesList->GetWidget().get_text(*xCatalog) ;
395                                 }
396                             }
397                         }
398                         bSchemaWildcard = m_xTablesList->isWildcardChecked(*xSchema);
399                         sComposedName.append(m_xTablesList->GetWidget().get_text(*xSchema) + ".");
400                     }
401 
402                     if (bSchemaWildcard)
403                         sComposedName.append(sWildcard);
404                 }
405                 if (!bSchemaWildcard && !bCatalogWildcard)
406                     sComposedName.append(m_xTablesList->GetWidget().get_text(*xEntry));
407 
408                 if (!m_bCatalogAtStart && !bCatalogWildcard)
409                     sComposedName.append(sCatalog);
410 
411                 // need some space
412                 sal_Int32 nOldLen = aTableFilter.getLength();
413                 aTableFilter.realloc(nOldLen + 1);
414                 // add the new name
415                 aTableFilter[nOldLen] = sComposedName.makeStringAndClear();
416             }
417 
418             if (bCatalogWildcard)
419                 xEntry = implNextSibling(xCatalog.get());
420             else if (bSchemaWildcard)
421                 xEntry = implNextSibling(xSchema.get());
422             else
423             {
424                 if (!m_xTablesList->GetWidget().iter_next(*xEntry))
425                     xEntry.reset();
426             }
427         }
428 
429         return aTableFilter;
430     }
431 
implNextSibling(const weld::TreeIter * pEntry) const432     std::unique_ptr<weld::TreeIter> OTableSubscriptionPage::implNextSibling(const weld::TreeIter* pEntry) const
433     {
434         std::unique_ptr<weld::TreeIter> xReturn;
435         if (pEntry)
436         {
437             xReturn = m_xTablesList->GetWidget().make_iterator(pEntry);
438             if (!m_xTablesList->GetWidget().iter_next_sibling(*xReturn))
439             {
440                 std::unique_ptr<weld::TreeIter> xParent = m_xTablesList->GetWidget().make_iterator(pEntry);
441                 if (m_xTablesList->GetWidget().iter_parent(*xParent))
442                     xReturn = implNextSibling(xParent.get());
443                 else
444                     xReturn.reset();
445             }
446         }
447         return xReturn;
448     }
449 
FillItemSet(SfxItemSet * _rCoreAttrs)450     bool OTableSubscriptionPage::FillItemSet( SfxItemSet* _rCoreAttrs )
451     {
452         bool bValid, bReadonly;
453         getFlags(*_rCoreAttrs, bValid, bReadonly);
454 
455         if (!bValid || bReadonly)
456             // don't store anything if the data we're working with is invalid or readonly
457             return true;
458 
459         // create the output string which contains all the table names
460         if ( m_xCurrentConnection.is() )
461         {   // collect the table filter data only if we have a connection - else no tables are displayed at all
462             Sequence< OUString > aTableFilter;
463             auto xRoot = m_xTablesList->getAllObjectsEntry();
464             if (xRoot && m_xTablesList->isWildcardChecked(*xRoot))
465             {
466                 aTableFilter.realloc(1);
467                 aTableFilter[0] = "%";
468             }
469             else
470             {
471                 aTableFilter = collectDetailedSelection();
472             }
473             _rCoreAttrs->Put( OStringListItem(DSID_TABLEFILTER, aTableFilter) );
474         }
475 
476         return true;
477     }
478 
fillControls(std::vector<std::unique_ptr<ISaveValueWrapper>> &)479     void OTableSubscriptionPage::fillControls(std::vector< std::unique_ptr<ISaveValueWrapper> >& /*_rControlList*/)
480     {
481     }
482 
fillWindows(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)483     void OTableSubscriptionPage::fillWindows(std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList)
484     {
485         _rControlList.emplace_back(new ODisableWidgetWrapper<weld::Widget>(m_xTables.get()));
486     }
487 }   // namespace dbaui
488 
489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
490