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 <WColumnSelect.hxx>
21 #include <strings.hrc>
22 #include <osl/diagnose.h>
23 #include <WCopyTable.hxx>
24 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
25 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
26 #include <com/sun/star/sdbcx/XAppend.hpp>
27 #include <core_resource.hxx>
28 #include <com/sun/star/sdbc/DataType.hpp>
29 #include <com/sun/star/sdbc/ColumnValue.hpp>
30 #include <com/sun/star/sdb/application/CopyTableOperation.hpp>
31 #include <vcl/svapp.hxx>
32 #include <stringconstants.hxx>
33 #include <functional>
34 
35 using namespace ::com::sun::star::uno;
36 using namespace ::com::sun::star::beans;
37 using namespace ::com::sun::star::container;
38 using namespace ::com::sun::star::sdbc;
39 using namespace ::com::sun::star::sdbcx;
40 using namespace dbaui;
41 
42 namespace CopyTableOperation = ::com::sun::star::sdb::application::CopyTableOperation;
43 
GetTitle() const44 OUString OWizColumnSelect::GetTitle() const { return DBA_RES(STR_WIZ_COLUMN_SELECT_TITEL); }
45 
OWizardPage(weld::Container * pPage,OCopyTableWizard * pWizard,const OUString & rUIXMLDescription,const OString & rID)46 OWizardPage::OWizardPage(weld::Container* pPage, OCopyTableWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
47     : ::vcl::OWizardPage(pPage, pWizard, rUIXMLDescription, rID)
48     , m_pParent(pWizard)
49     , m_bFirstTime(true)
50 {
51 }
52 
~OWizardPage()53 OWizardPage::~OWizardPage()
54 {
55 }
56 
57 // OWizColumnSelect
OWizColumnSelect(weld::Container * pPage,OCopyTableWizard * pWizard)58 OWizColumnSelect::OWizColumnSelect(weld::Container* pPage, OCopyTableWizard* pWizard)
59     : OWizardPage(pPage, pWizard, "dbaccess/ui/applycolpage.ui", "ApplyColPage")
60     , m_xOrgColumnNames(m_xBuilder->weld_tree_view("from"))
61     , m_xColumn_RH(m_xBuilder->weld_button("colrh"))
62     , m_xColumns_RH(m_xBuilder->weld_button("colsrh"))
63     , m_xColumn_LH(m_xBuilder->weld_button("collh"))
64     , m_xColumns_LH(m_xBuilder->weld_button("colslh"))
65     , m_xNewColumnNames(m_xBuilder->weld_tree_view("to"))
66 {
67     m_xColumn_RH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
68     m_xColumn_LH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
69     m_xColumns_RH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
70     m_xColumns_LH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
71 
72     m_xOrgColumnNames->set_selection_mode(SelectionMode::Multiple);
73     m_xNewColumnNames->set_selection_mode(SelectionMode::Multiple);
74 
75     m_xOrgColumnNames->connect_row_activated(LINK(this,OWizColumnSelect,ListDoubleClickHdl));
76     m_xNewColumnNames->connect_row_activated(LINK(this,OWizColumnSelect,ListDoubleClickHdl));
77 }
78 
~OWizColumnSelect()79 OWizColumnSelect::~OWizColumnSelect()
80 {
81     while (m_xNewColumnNames->n_children())
82     {
83         delete reinterpret_cast<OFieldDescription*>(m_xNewColumnNames->get_id(0).toInt64());
84         m_xNewColumnNames->remove(0);
85     }
86 }
87 
Reset()88 void OWizColumnSelect::Reset()
89 {
90     // restore original state
91     clearListBox(*m_xOrgColumnNames);
92     clearListBox(*m_xNewColumnNames);
93     m_pParent->m_mNameMapping.clear();
94 
95     // insert the source columns in the left listbox
96     const ODatabaseExport::TColumnVector& rSrcColumns = m_pParent->getSrcVector();
97 
98     for (auto const& column : rSrcColumns)
99     {
100         OUString sId(OUString::number(reinterpret_cast<sal_Int64>(column->second)));
101         m_xOrgColumnNames->append(sId, column->first);
102     }
103 
104     if (m_xOrgColumnNames->n_children())
105         m_xOrgColumnNames->select(0);
106 
107     m_bFirstTime = false;
108 }
109 
Activate()110 void OWizColumnSelect::Activate( )
111 {
112     // if there are no dest columns reset the left side with the original columns
113     if(m_pParent->getDestColumns().empty())
114         Reset();
115 
116     clearListBox(*m_xNewColumnNames);
117 
118     const ODatabaseExport::TColumnVector& rDestColumns = m_pParent->getDestVector();
119 
120     // tdf#113923, the added columns must exist in the table
121     // in the case where:
122     // 1: we enabled the creation of a primary key
123     // 2: we come back here from the "Back" button of the next page,
124     // we want to avoid to list the new column generated in the next page
125     const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
126 
127     for (auto const& column : rDestColumns)
128     {
129         if (rSrcColumns.find(column->first) != rSrcColumns.end())
130         {
131             OUString sId(OUString::number(reinterpret_cast<sal_Int64>(new OFieldDescription(*(column->second)))));
132             m_xNewColumnNames->append(sId, column->first);
133             int nRemove = m_xOrgColumnNames->find_text(column->first);
134             if (nRemove != -1)
135                 m_xOrgColumnNames->remove(nRemove);
136         }
137     }
138     m_pParent->GetOKButton().set_sensitive(m_xNewColumnNames->n_children() != 0);
139     m_pParent->EnableNextButton(m_xNewColumnNames->n_children() && m_pParent->getOperation() != CopyTableOperation::AppendData);
140     m_xColumns_RH->grab_focus();
141 }
142 
LeavePage()143 bool OWizColumnSelect::LeavePage()
144 {
145 
146     m_pParent->clearDestColumns();
147 
148     for(sal_Int32 i=0 ; i< m_xNewColumnNames->n_children();++i)
149     {
150         OFieldDescription* pField = reinterpret_cast<OFieldDescription*>(m_xNewColumnNames->get_id(i).toInt64());
151         OSL_ENSURE(pField,"The field information can not be null!");
152         m_pParent->insertColumn(i,pField);
153     }
154 
155     clearListBox(*m_xNewColumnNames);
156 
157     if  (   m_pParent->GetPressedButton() == OCopyTableWizard::WIZARD_NEXT
158         ||  m_pParent->GetPressedButton() == OCopyTableWizard::WIZARD_FINISH
159         )
160         return !m_pParent->getDestColumns().empty();
161     else
162         return true;
163 }
164 
IMPL_LINK(OWizColumnSelect,ButtonClickHdl,weld::Button &,rButton,void)165 IMPL_LINK(OWizColumnSelect, ButtonClickHdl, weld::Button&, rButton, void)
166 {
167     weld::TreeView *pLeft = nullptr;
168     weld::TreeView *pRight = nullptr;
169     bool bAll = false;
170 
171     if (&rButton == m_xColumn_RH.get())
172     {
173         pLeft  = m_xOrgColumnNames.get();
174         pRight = m_xNewColumnNames.get();
175     }
176     else if (&rButton == m_xColumn_LH.get())
177     {
178         pLeft  = m_xNewColumnNames.get();
179         pRight = m_xOrgColumnNames.get();
180     }
181     else if (&rButton == m_xColumns_RH.get())
182     {
183         pLeft  = m_xOrgColumnNames.get();
184         pRight = m_xNewColumnNames.get();
185         bAll   = true;
186     }
187     else if (&rButton == m_xColumns_LH.get())
188     {
189         pLeft  = m_xNewColumnNames.get();
190         pRight = m_xOrgColumnNames.get();
191         bAll   = true;
192     }
193 
194     if (!pLeft || !pRight)
195         return;
196 
197     Reference< XDatabaseMetaData > xMetaData( m_pParent->m_xDestConnection->getMetaData() );
198     OUString sExtraChars = xMetaData->getExtraNameCharacters();
199     sal_Int32 nMaxNameLen       = m_pParent->getMaxColumnNameLength();
200 
201     ::comphelper::UStringMixEqual aCase(xMetaData->supportsMixedCaseQuotedIdentifiers());
202     std::vector< OUString> aRightColumns;
203     fillColumns(pRight,aRightColumns);
204 
205     if(!bAll)
206     {
207         auto aRows = pLeft->get_selected_rows();
208         std::sort(aRows.begin(), aRows.end());
209 
210         for (auto it = aRows.begin(); it != aRows.end(); ++it)
211             moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(*it),sExtraChars,nMaxNameLen,aCase);
212 
213         for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
214             pLeft->remove(*it);
215     }
216     else
217     {
218         const sal_Int32 nEntries = pLeft->n_children();
219         for(sal_Int32 i=0; i < nEntries; ++i)
220             moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(i),sExtraChars,nMaxNameLen,aCase);
221         for(sal_Int32 j=pLeft->n_children(); j ; )
222             pLeft->remove(--j);
223     }
224 
225     enableButtons();
226 
227     if (m_xOrgColumnNames->n_children())
228         m_xOrgColumnNames->select(0);
229 }
230 
IMPL_LINK(OWizColumnSelect,ListDoubleClickHdl,weld::TreeView &,rListBox,bool)231 IMPL_LINK( OWizColumnSelect, ListDoubleClickHdl, weld::TreeView&, rListBox, bool )
232 {
233     weld::TreeView *pLeft,*pRight;
234     if (&rListBox == m_xOrgColumnNames.get())
235     {
236         pLeft  = m_xOrgColumnNames.get();
237         pRight = m_xNewColumnNames.get();
238     }
239     else
240     {
241         pRight = m_xOrgColumnNames.get();
242         pLeft  = m_xNewColumnNames.get();
243     }
244 
245     // If database is able to process PrimaryKeys, set PrimaryKey
246     Reference< XDatabaseMetaData >  xMetaData( m_pParent->m_xDestConnection->getMetaData() );
247     OUString sExtraChars = xMetaData->getExtraNameCharacters();
248     sal_Int32 nMaxNameLen       = m_pParent->getMaxColumnNameLength();
249 
250     ::comphelper::UStringMixEqual aCase(xMetaData->supportsMixedCaseQuotedIdentifiers());
251     std::vector< OUString> aRightColumns;
252     fillColumns(pRight,aRightColumns);
253 
254     auto aRows = pLeft->get_selected_rows();
255     std::sort(aRows.begin(), aRows.end());
256 
257     for (auto it = aRows.begin(); it != aRows.end(); ++it)
258         moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(*it),sExtraChars,nMaxNameLen,aCase);
259 
260     for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
261         pLeft->remove(*it);
262 
263     enableButtons();
264 
265     return true;
266 }
267 
clearListBox(weld::TreeView & rListBox)268 void OWizColumnSelect::clearListBox(weld::TreeView& rListBox)
269 {
270     rListBox.clear();
271 }
272 
fillColumns(weld::TreeView const * pRight,std::vector<OUString> & _rRightColumns)273 void OWizColumnSelect::fillColumns(weld::TreeView const * pRight,std::vector< OUString> &_rRightColumns)
274 {
275     const sal_Int32 nCount = pRight->n_children();
276     _rRightColumns.reserve(nCount);
277     for (sal_Int32 i=0; i < nCount; ++i)
278         _rRightColumns.push_back(pRight->get_text(i));
279 }
280 
createNewColumn(weld::TreeView * _pListbox,OFieldDescription const * _pSrcField,std::vector<OUString> & _rRightColumns,const OUString & _sColumnName,const OUString & _sExtraChars,sal_Int32 _nMaxNameLen,const::comphelper::UStringMixEqual & _aCase)281 void OWizColumnSelect::createNewColumn( weld::TreeView* _pListbox,
282                                         OFieldDescription const * _pSrcField,
283                                         std::vector< OUString>& _rRightColumns,
284                                         const OUString&  _sColumnName,
285                                         const OUString&  _sExtraChars,
286                                         sal_Int32               _nMaxNameLen,
287                                         const ::comphelper::UStringMixEqual& _aCase)
288 {
289     OUString sConvertedName = m_pParent->convertColumnName(TMultiListBoxEntryFindFunctor(&_rRightColumns,_aCase),
290                                                                 _sColumnName,
291                                                                 _sExtraChars,
292                                                                 _nMaxNameLen);
293     OFieldDescription* pNewField = new OFieldDescription(*_pSrcField);
294     pNewField->SetName(sConvertedName);
295     bool bNotConvert = true;
296     pNewField->SetType(m_pParent->convertType(_pSrcField->getSpecialTypeInfo(),bNotConvert));
297     if ( !m_pParent->supportsPrimaryKey() )
298         pNewField->SetPrimaryKey(false);
299 
300     _pListbox->append(OUString::number(reinterpret_cast<sal_Int64>(pNewField)), sConvertedName);
301     _rRightColumns.push_back(sConvertedName);
302 
303     if ( !bNotConvert )
304         m_pParent->showColumnTypeNotSupported(sConvertedName);
305 }
306 
moveColumn(weld::TreeView * _pRight,weld::TreeView const * _pLeft,std::vector<OUString> & _rRightColumns,const OUString & _sColumnName,const OUString & _sExtraChars,sal_Int32 _nMaxNameLen,const::comphelper::UStringMixEqual & _aCase)307 void OWizColumnSelect::moveColumn(  weld::TreeView* _pRight,
308                                     weld::TreeView const * _pLeft,
309                                     std::vector< OUString>& _rRightColumns,
310                                     const OUString&  _sColumnName,
311                                     const OUString&  _sExtraChars,
312                                     sal_Int32               _nMaxNameLen,
313                                     const ::comphelper::UStringMixEqual& _aCase)
314 {
315     if(_pRight == m_xNewColumnNames.get())
316     {
317         // we copy the column into the new format for the dest
318         OFieldDescription* pSrcField = reinterpret_cast<OFieldDescription*>(_pLeft->get_id(_pLeft->find_text(_sColumnName)).toInt64());
319         createNewColumn(_pRight,pSrcField,_rRightColumns,_sColumnName,_sExtraChars,_nMaxNameLen,_aCase);
320     }
321     else
322     {
323         // find the new column in the dest name mapping to obtain the old column
324         OCopyTableWizard::TNameMapping::const_iterator aIter = std::find_if(m_pParent->m_mNameMapping.begin(),m_pParent->m_mNameMapping.end(),
325             [&_aCase, &_sColumnName] (const OCopyTableWizard::TNameMapping::value_type& nameMap) {
326                 return _aCase(nameMap.second, _sColumnName);
327             });
328 
329         OSL_ENSURE(aIter != m_pParent->m_mNameMapping.end(),"Column must be defined");
330         if ( aIter == m_pParent->m_mNameMapping.end() )
331             return; // do nothing
332         const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
333         ODatabaseExport::TColumns::const_iterator aSrcIter = rSrcColumns.find((*aIter).first);
334         if ( aSrcIter != rSrcColumns.end() )
335         {
336             // we need also the old position of this column to insert it back on that position again
337             const ODatabaseExport::TColumnVector& rSrcVector = m_pParent->getSrcVector();
338             ODatabaseExport::TColumnVector::const_iterator aPos = std::find(rSrcVector.begin(), rSrcVector.end(), aSrcIter);
339             OSL_ENSURE( aPos != rSrcVector.end(),"Invalid position for the iterator here!");
340             ODatabaseExport::TColumnVector::size_type nPos = (aPos - rSrcVector.begin()) - adjustColumnPosition(_pLeft, _sColumnName, (aPos - rSrcVector.begin()), _aCase);
341 
342             OUString sId(OUString::number(reinterpret_cast<sal_Int64>(aSrcIter->second)));
343             const OUString& rStr = (*aIter).first;
344             _pRight->insert(nullptr, nPos, &rStr, &sId, nullptr, nullptr, nullptr, false, nullptr);
345             _rRightColumns.push_back(rStr);
346             m_pParent->removeColumnNameFromNameMap(_sColumnName);
347         }
348     }
349 }
350 
351 // Simply returning fields back to their original position is
352 // not enough. We need to take into account what fields have
353 // been removed earlier and adjust accordingly. Based on the
354 // algorithm employed in moveColumn().
adjustColumnPosition(weld::TreeView const * _pLeft,const OUString & _sColumnName,ODatabaseExport::TColumnVector::size_type nCurrentPos,const::comphelper::UStringMixEqual & _aCase)355 sal_Int32 OWizColumnSelect::adjustColumnPosition(weld::TreeView const * _pLeft,
356                                                  const OUString&   _sColumnName,
357                                                  ODatabaseExport::TColumnVector::size_type nCurrentPos,
358                                                  const ::comphelper::UStringMixEqual& _aCase)
359 {
360     sal_Int32 nAdjustedPos = 0;
361 
362     // if returning all entries to their original position,
363     // then there is no need to adjust the positions.
364     if (m_xColumns_LH->has_focus())
365         return nAdjustedPos;
366 
367     const sal_Int32 nCount = _pLeft->n_children();
368     OUString sColumnString;
369     for(sal_Int32 i=0; i < nCount; ++i)
370     {
371         sColumnString = _pLeft->get_text(i);
372         if(_sColumnName != sColumnString)
373         {
374             // find the new column in the dest name mapping to obtain the old column
375             OCopyTableWizard::TNameMapping::const_iterator aIter = std::find_if(m_pParent->m_mNameMapping.begin(),m_pParent->m_mNameMapping.end(),
376                 [&_aCase, &sColumnString] (const OCopyTableWizard::TNameMapping::value_type& nameMap) {
377                     return _aCase(nameMap.second, sColumnString);
378                 });
379 
380             OSL_ENSURE(aIter != m_pParent->m_mNameMapping.end(),"Column must be defined");
381             const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
382             ODatabaseExport::TColumns::const_iterator aSrcIter = rSrcColumns.find((*aIter).first);
383             if ( aSrcIter != rSrcColumns.end() )
384             {
385                 // we need also the old position of this column to insert it back on that position again
386                 const ODatabaseExport::TColumnVector& rSrcVector = m_pParent->getSrcVector();
387                 ODatabaseExport::TColumnVector::const_iterator aPos = std::find(rSrcVector.begin(), rSrcVector.end(), aSrcIter);
388                 ODatabaseExport::TColumnVector::size_type nPos = aPos - rSrcVector.begin();
389                 if( nPos < nCurrentPos)
390                 {
391                     nAdjustedPos++;
392                 }
393             }
394         }
395     }
396 
397     return nAdjustedPos;
398 }
399 
enableButtons()400 void OWizColumnSelect::enableButtons()
401 {
402     bool bEntries = m_xNewColumnNames->n_children() != 0;
403     if (!bEntries)
404         m_pParent->m_mNameMapping.clear();
405 
406     m_pParent->GetOKButton().set_sensitive(bEntries);
407     m_pParent->EnableNextButton(bEntries && m_pParent->getOperation() != CopyTableOperation::AppendData);
408 }
409 
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
411