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 <FieldDescriptions.hxx>
21 #include "TEditControl.hxx"
22 #include <TableController.hxx>
23 #include <TableDesignView.hxx>
24 #include <TableRow.hxx>
25 #include <TypeInfo.hxx>
26 #include <UITools.hxx>
27 #include <browserids.hxx>
28 #include <core_resource.hxx>
29 #include <strings.hrc>
30 #include <strings.hxx>
31 #include <defaultobjectnamecheck.hxx>
32 #include <dlgsave.hxx>
33 #include <indexdialog.hxx>
34 #include <sqlmessage.hxx>
35 
36 #include <com/sun/star/frame/XTitleChangeListener.hpp>
37 #include <com/sun/star/sdb/CommandType.hpp>
38 #include <com/sun/star/sdb/SQLContext.hpp>
39 #include <com/sun/star/sdbc/ColumnValue.hpp>
40 #include <com/sun/star/sdbc/SQLWarning.hpp>
41 #include <com/sun/star/sdbcx/KeyType.hpp>
42 #include <com/sun/star/sdbcx/XAlterTable.hpp>
43 #include <com/sun/star/sdbcx/XAppend.hpp>
44 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
45 #include <com/sun/star/sdbcx/XDrop.hpp>
46 #include <com/sun/star/sdbcx/XIndexesSupplier.hpp>
47 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
48 
49 #include <comphelper/processfactory.hxx>
50 #include <connectivity/dbexception.hxx>
51 #include <connectivity/dbtools.hxx>
52 #include <connectivity/dbmetadata.hxx>
53 #include <cppuhelper/exc_hlp.hxx>
54 #include <tools/diagnose_ex.h>
55 #include <vcl/svapp.hxx>
56 #include <vcl/weld.hxx>
57 
58 #include <algorithm>
59 #include <functional>
60 
61 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
62 org_openoffice_comp_dbu_OTableDesign_get_implementation(
63     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
64 {
65     return cppu::acquire(new ::dbaui::OTableController(context));
66 }
67 
68 using namespace ::com::sun::star;
69 using namespace ::com::sun::star::uno;
70 using namespace ::com::sun::star::io;
71 using namespace ::com::sun::star::beans;
72 using namespace ::com::sun::star::frame;
73 using namespace ::com::sun::star::lang;
74 using namespace ::com::sun::star::container;
75 using namespace ::com::sun::star::sdbcx;
76 using namespace ::com::sun::star::sdbc;
77 using namespace ::com::sun::star::sdb;
78 using namespace ::com::sun::star::ui;
79 using namespace ::com::sun::star::util;
80 using namespace ::dbtools;
81 using namespace ::dbaui;
82 using namespace ::comphelper;
83 
84 // number of columns when creating it from scratch
85 #define NEWCOLS        128
86 
87 namespace
88 {
89     void dropTable(const Reference<XNameAccess>& _rxTable,const OUString& _sTableName)
90     {
91         if ( _rxTable->hasByName(_sTableName) )
92         {
93             Reference<XDrop> xNameCont(_rxTable,UNO_QUERY);
94             OSL_ENSURE(xNameCont.is(),"No drop interface for tables!");
95             if ( xNameCont.is() )
96                 xNameCont->dropByName(_sTableName);
97         }
98     }
99 }
100 
101 OUString SAL_CALL OTableController::getImplementationName()
102 {
103     return "org.openoffice.comp.dbu.OTableDesign";
104 }
105 
106 Sequence< OUString> OTableController::getSupportedServiceNames()
107 {
108     return { "com.sun.star.sdb.TableDesign" };
109 }
110 
111 OTableController::OTableController(const Reference< XComponentContext >& _rM) : OTableController_BASE(_rM)
112     ,m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
113     ,m_pTypeInfo()
114     ,m_bAllowAutoIncrementValue(false)
115     ,m_bNew(true)
116 {
117 
118     InvalidateAll();
119     m_pTypeInfo = std::make_shared<OTypeInfo>();
120     m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';');
121 }
122 
123 OTableController::~OTableController()
124 {
125     m_aTypeInfoIndex.clear();
126     m_aTypeInfo.clear();
127 
128 }
129 
130 void OTableController::startTableListening()
131 {
132     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
133     if (xComponent.is())
134         xComponent->addEventListener(static_cast<XModifyListener*>(this));
135 }
136 
137 void OTableController::stopTableListening()
138 {
139     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
140     if (xComponent.is())
141         xComponent->removeEventListener(static_cast<XModifyListener*>(this));
142 }
143 
144 void OTableController::disposing()
145 {
146     OTableController_BASE::disposing();
147     clearView();
148 
149     m_vRowList.clear();
150 }
151 
152 FeatureState OTableController::GetState(sal_uInt16 _nId) const
153 {
154     FeatureState aReturn;
155     // disabled automatically
156 
157     switch (_nId)
158     {
159         case ID_BROWSER_CLOSE:
160             aReturn.bEnabled = true;
161             break;
162         case ID_BROWSER_EDITDOC:
163             aReturn.bChecked = isEditable();
164             aReturn.bEnabled = true;
165             break;
166         case ID_BROWSER_SAVEDOC:
167             aReturn.bEnabled = isEditable() && std::any_of(m_vRowList.begin(),m_vRowList.end(),std::mem_fn(&OTableRow::isValid));
168             break;
169         case ID_BROWSER_SAVEASDOC:
170             aReturn.bEnabled = isConnected() && isEditable();
171             if ( aReturn.bEnabled )
172             {
173                 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
174                                                  std::mem_fn(&OTableRow::isValid));
175             }
176             break;
177 
178         case ID_BROWSER_CUT:
179             aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isCutAllowed();
180             break;
181         case ID_BROWSER_COPY:
182             aReturn.bEnabled = getView() && static_cast<OTableDesignView*>(getView())->isCopyAllowed();
183             break;
184         case ID_BROWSER_PASTE:
185             aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isPasteAllowed();
186             break;
187         case SID_INDEXDESIGN:
188             aReturn.bEnabled =
189                 (   (   ((!m_bNew && impl_isModified()) || impl_isModified())
190                     ||  Reference< XIndexesSupplier >(m_xTable, UNO_QUERY).is()
191                     )
192                 &&  isConnected()
193                 );
194             if ( aReturn.bEnabled )
195             {
196                 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
197                                                  std::mem_fn(&OTableRow::isValid));
198             }
199             break;
200         default:
201             aReturn = OTableController_BASE::GetState(_nId);
202     }
203     return aReturn;
204 }
205 
206 void OTableController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs)
207 {
208     switch(_nId)
209     {
210         case ID_BROWSER_EDITDOC:
211             setEditable(!isEditable());
212             static_cast<OTableDesignView*>(getView())->setReadOnly(!isEditable());
213             InvalidateFeature(ID_BROWSER_SAVEDOC);
214             InvalidateFeature(ID_BROWSER_PASTE);
215             InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
216             break;
217         case ID_BROWSER_SAVEASDOC:
218             doSaveDoc(true);
219             break;
220         case ID_BROWSER_SAVEDOC:
221             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->SaveCurRow();
222             doSaveDoc(false);
223             break;
224         case ID_BROWSER_CUT:
225             static_cast<OTableDesignView*>(getView())->cut();
226             break;
227         case ID_BROWSER_COPY:
228             static_cast<OTableDesignView*>(getView())->copy();
229             break;
230         case ID_BROWSER_PASTE:
231             static_cast<OTableDesignView*>(getView())->paste();
232             break;
233         case SID_INDEXDESIGN:
234             doEditIndexes();
235             break;
236         default:
237             OTableController_BASE::Execute(_nId,aArgs);
238     }
239     InvalidateFeature(_nId);
240 }
241 
242 bool OTableController::doSaveDoc(bool _bSaveAs)
243 {
244     if (!isConnected())
245         reconnect(true); // ask the user for a new connection
246     Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
247 
248     if (!xTablesSup.is())
249     {
250         OUString aMessage(DBA_RES(STR_TABLEDESIGN_CONNECTION_MISSING));
251         OSQLWarningBox aWarning(getFrameWeld(), aMessage);
252         aWarning.run();
253         return false;
254     }
255 
256     // check if a column exists
257     // TODO
258 
259     Reference<XNameAccess> xTables;
260     OUString sCatalog, sSchema;
261 
262     bool bNew = m_sName.isEmpty();
263     bNew = bNew || m_bNew || _bSaveAs;
264 
265     try
266     {
267         xTables = xTablesSup->getTables();
268         OSL_ENSURE(xTables.is(),"The tables can't be null!");
269         bNew = bNew || (xTables.is() && !xTables->hasByName(m_sName));
270 
271         // first we need a name for our query so ask the user
272         if(bNew)
273         {
274             OUString aName = DBA_RES(STR_TBL_TITLE);
275             OUString aDefaultName = aName.getToken(0,' ');
276             aDefaultName = ::dbtools::createUniqueName(xTables,aDefaultName);
277 
278             DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::TABLE );
279             OSaveAsDlg aDlg(getFrameWeld(), CommandType::TABLE, getORB(), getConnection(), aDefaultName, aNameChecker, SADFlags::NONE);
280             if (aDlg.run() != RET_OK)
281                 return false;
282 
283             m_sName = aDlg.getName();
284             sCatalog = aDlg.getCatalog();
285             sSchema  = aDlg.getSchema();
286         }
287 
288         // did we get a name
289         if(m_sName.isEmpty())
290             return false;
291     }
292     catch(Exception&)
293     {
294         OSL_FAIL("OTableController::doSaveDoc: nothing is expected to happen here!");
295     }
296 
297     bool bAlter = false;
298     bool bError = false;
299     SQLExceptionInfo aInfo;
300     try
301     {
302         // check the columns for double names
303         if(!checkColumns(bNew || !xTables->hasByName(m_sName)))
304         {
305             return false;
306         }
307 
308         Reference<XPropertySet> xTable;
309         if(bNew || !xTables->hasByName(m_sName)) // just to make sure the table already exists
310         {
311             dropTable(xTables,m_sName);
312 
313             Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY);
314             OSL_ENSURE(xFact.is(),"OTableController::doSaveDoc: No XDataDescriptorFactory available!");
315             xTable = xFact->createDataDescriptor();
316             OSL_ENSURE(xTable.is(),"OTableController::doSaveDoc: Create query failed!");
317             // to set the name is only allowed when the query is new
318             xTable->setPropertyValue(PROPERTY_CATALOGNAME,makeAny(sCatalog));
319             xTable->setPropertyValue(PROPERTY_SCHEMANAME,makeAny(sSchema));
320             xTable->setPropertyValue(PROPERTY_NAME,makeAny(m_sName));
321 
322             // now append the columns
323             Reference<XColumnsSupplier> xColSup(xTable,UNO_QUERY);
324             appendColumns(xColSup,bNew);
325             // now append the primary key
326             Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY);
327             appendPrimaryKey(xKeySup,bNew);
328         }
329         // now set the properties
330         if(bNew)
331         {
332             Reference<XAppend> xAppend(xTables,UNO_QUERY);
333             OSL_ENSURE(xAppend.is(),"OTableController::doSaveDoc: No XAppend Interface!");
334             xAppend->appendByDescriptor(xTable);
335 
336             assignTable();
337             if(!m_xTable.is()) // correct name and try again
338             {
339                 // it can be that someone inserted new data for us
340                 m_sName = ::dbtools::composeTableName( getConnection()->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false );
341                 assignTable();
342             }
343             // now check if our datasource has set a tablefilter and if append the new table name to it
344             ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld()); // we are not interested in the return value
345             Reference< frame::XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY);
346             if ( xEventListener.is() )
347             {
348                 frame::TitleChangedEvent aEvent;
349                 xEventListener->titleChanged(aEvent);
350             }
351             releaseNumberForComponent();
352         }
353         else if(m_xTable.is())
354         {
355             bAlter = true;
356             alterColumns();
357         }
358         reSyncRows();
359     }
360     catch(const SQLContext& e)
361     {
362         aInfo = SQLExceptionInfo(e);
363     }
364     catch(const SQLWarning& e)
365     {
366         aInfo = SQLExceptionInfo(e);
367     }
368     catch(const SQLException& e)
369     {
370         aInfo = SQLExceptionInfo(e);
371     }
372     catch(const ElementExistException& )
373     {
374         OUString sText( DBA_RES( STR_NAME_ALREADY_EXISTS ) );
375         sText = sText.replaceFirst( "#" , m_sName);
376         OSQLMessageBox aDlg(getFrameWeld(), DBA_RES( STR_ERROR_DURING_CREATION ), sText, MessBoxStyle::Ok, MessageType::Error);
377         aDlg.run();
378         bError = true;
379     }
380     catch( const Exception& )
381     {
382         DBG_UNHANDLED_EXCEPTION("dbaccess");
383         bError = true;
384     }
385 
386     if ( aInfo.isValid() )
387         aInfo.prepend( DBA_RES( STR_TABLEDESIGN_SAVE_ERROR ) );
388     showError(aInfo);
389 
390     if (aInfo.isValid() || bError)
391     {
392         if(!bAlter || bNew)
393         {
394             m_sName.clear();
395             stopTableListening();
396             m_xTable = nullptr;
397         }
398     }
399     return ! (aInfo.isValid() || bError);
400 }
401 
402 void OTableController::doEditIndexes()
403 {
404     // table needs to be saved before editing indexes
405     if (m_bNew || isModified())
406     {
407         std::unique_ptr<weld::MessageDialog> xAsk(Application::CreateMessageDialog(getFrameWeld(),
408                                                   VclMessageType::Question, VclButtonsType::YesNo,
409                                                   DBA_RES(STR_QUERY_SAVE_TABLE_EDIT_INDEXES)));
410         if (RET_YES != xAsk->run())
411             return;
412 
413         if (!doSaveDoc(false))
414             return;
415 
416         OSL_ENSURE(!m_bNew && !isModified(), "OTableController::doEditIndexes: what the hell did doSaveDoc do?");
417     }
418 
419     Reference< XNameAccess > xIndexes;          // will be the keys of the table
420     Sequence< OUString > aFieldNames;    // will be the column names of the table
421     try
422     {
423         // get the keys
424         Reference< XIndexesSupplier > xIndexesSupp(m_xTable, UNO_QUERY);
425         if (xIndexesSupp.is())
426         {
427             xIndexes = xIndexesSupp->getIndexes();
428             OSL_ENSURE(xIndexes.is(), "OTableController::doEditIndexes: no keys got from the indexes supplier!");
429         }
430         else
431             OSL_FAIL("OTableController::doEditIndexes: should never have reached this (no indexes supplier)!");
432 
433         // get the field names
434         Reference< XColumnsSupplier > xColSupp(m_xTable, UNO_QUERY);
435         OSL_ENSURE(xColSupp.is(), "OTableController::doEditIndexes: no columns supplier!");
436         if (xColSupp.is())
437         {
438             Reference< XNameAccess > xCols = xColSupp->getColumns();
439             OSL_ENSURE(xCols.is(), "OTableController::doEditIndexes: no columns!");
440             if (xCols.is())
441                 aFieldNames = xCols->getElementNames();
442         }
443     }
444     catch( const Exception& )
445     {
446         DBG_UNHANDLED_EXCEPTION("dbaccess");
447     }
448 
449     if (!xIndexes.is())
450         return;
451 
452     DbaIndexDialog aDialog(getFrameWeld(), aFieldNames, xIndexes, getConnection(), getORB());
453     if (RET_OK != aDialog.run())
454         return;
455 
456 }
457 
458 void OTableController::impl_initialize()
459 {
460     try
461     {
462         OTableController_BASE::impl_initialize();
463 
464         const NamedValueCollection& rArguments( getInitParams() );
465 
466         rArguments.get_ensureType( PROPERTY_CURRENTTABLE, m_sName );
467 
468         // read autoincrement value set in the datasource
469         ::dbaui::fillAutoIncrementValue(getDataSource(),m_bAllowAutoIncrementValue,m_sAutoIncrementValue);
470 
471         assignTable();
472     }
473     catch( const Exception& )
474     {
475         DBG_UNHANDLED_EXCEPTION("dbaccess");
476     }
477 
478     try
479     {
480         ::dbaui::fillTypeInfo(getConnection(),m_sTypeNames,m_aTypeInfo,m_aTypeInfoIndex);               // fill the needed type information
481     }
482     catch(const SQLException&)
483     {
484         OSQLWarningBox aWarning(getFrameWeld(), DBA_RES( STR_NO_TYPE_INFO_AVAILABLE));
485         aWarning.run();
486         throw;
487     }
488     try
489     {
490         loadData();                 // fill the column information from the table
491         getView()->initialize();    // show the windows and fill with our information
492         ClearUndoManager();
493         setModified(false);     // and we are not modified yet
494     }
495     catch( const Exception& )
496     {
497         DBG_UNHANDLED_EXCEPTION("dbaccess");
498     }
499 }
500 
501 bool OTableController::Construct(vcl::Window* pParent)
502 {
503     setView( VclPtr<OTableDesignView>::Create( pParent, getORB(), *this ) );
504     OTableController_BASE::Construct(pParent);
505     return true;
506 }
507 
508 sal_Bool SAL_CALL OTableController::suspend(sal_Bool /*_bSuspend*/)
509 {
510     if ( getBroadcastHelper().bInDispose || getBroadcastHelper().bDisposed )
511         return true;
512 
513     SolarMutexGuard aSolarGuard;
514     ::osl::MutexGuard aGuard( getMutex() );
515     if ( getView() && getView()->IsInModalMode() )
516         return false;
517     if ( getView() )
518         static_cast<OTableDesignView*>(getView())->GrabFocus();
519     bool bCheck = true;
520     if ( isModified() )
521     {
522         if ( std::any_of(m_vRowList.begin(),m_vRowList.end(),
523                            std::mem_fn(&OTableRow::isValid)) )
524         {
525             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/tabledesignsavemodifieddialog.ui"));
526             std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("TableDesignSaveModifiedDialog"));
527             switch (xQuery->run())
528             {
529                 case RET_YES:
530                     Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>());
531                     if ( isModified() )
532                         bCheck = false; // when we save the table this must be false else some press cancel
533                     break;
534                 case RET_CANCEL:
535                     bCheck = false;
536                     break;
537                 default:
538                     break;
539             }
540         }
541         else if ( !m_bNew )
542         {
543             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/deleteallrowsdialog.ui"));
544             std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("DeleteAllRowsDialog"));
545             switch (xQuery->run())
546             {
547                 case RET_YES:
548                     {
549                         try
550                         {
551                             Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
552                             Reference<XNameAccess> xTables = xTablesSup->getTables();
553                             dropTable(xTables,m_sName);
554                         }
555                         catch(const Exception&)
556                         {
557                             OSL_FAIL("OTableController::suspend: nothing is expected to happen here!");
558                         }
559 
560                     }
561                     break;
562                 case RET_CANCEL:
563                     bCheck = false;
564                     break;
565                 default:
566                     break;
567             }
568         }
569     }
570 
571     return bCheck;
572 }
573 
574 void OTableController::describeSupportedFeatures()
575 {
576     OSingleDocumentController::describeSupportedFeatures();
577 
578     implDescribeSupportedFeature( ".uno:Redo",          ID_BROWSER_REDO,        CommandGroup::EDIT );
579     implDescribeSupportedFeature( ".uno:Save",          ID_BROWSER_SAVEDOC,     CommandGroup::EDIT );
580     implDescribeSupportedFeature( ".uno:Undo",          ID_BROWSER_UNDO,        CommandGroup::EDIT );
581     implDescribeSupportedFeature( ".uno:NewDoc",        SID_NEWDOC,             CommandGroup::DOCUMENT );
582     implDescribeSupportedFeature( ".uno:SaveAs",        ID_BROWSER_SAVEASDOC,   CommandGroup::DOCUMENT );
583     implDescribeSupportedFeature( ".uno:DBIndexDesign", SID_INDEXDESIGN,        CommandGroup::APPLICATION );
584     implDescribeSupportedFeature( ".uno:EditDoc",       ID_BROWSER_EDITDOC,     CommandGroup::EDIT );
585     implDescribeSupportedFeature( ".uno:GetUndoStrings", SID_GETUNDOSTRINGS );
586     implDescribeSupportedFeature( ".uno:GetRedoStrings", SID_GETREDOSTRINGS );
587 }
588 
589 void OTableController::impl_onModifyChanged()
590 {
591     OSingleDocumentController::impl_onModifyChanged();
592     InvalidateFeature( SID_INDEXDESIGN );
593 }
594 
595 void SAL_CALL OTableController::disposing( const EventObject& _rSource )
596 {
597     if ( _rSource.Source == m_xTable )
598     {   // some deleted our table so we have a new one
599         stopTableListening();
600         m_xTable    = nullptr;
601         m_bNew      = true;
602         setModified(true);
603     }
604     else
605         OTableController_BASE::disposing( _rSource );
606 }
607 
608 void OTableController::losingConnection( )
609 {
610     // let the base class do its reconnect
611     OTableController_BASE::losingConnection( );
612 
613     // remove from the table
614     Reference< XComponent >  xComponent(m_xTable, UNO_QUERY);
615     if (xComponent.is())
616     {
617         Reference<XEventListener> xEvtL( static_cast< ::cppu::OWeakObject*>(this), UNO_QUERY);
618         xComponent->removeEventListener(xEvtL);
619     }
620     stopTableListening();
621     m_xTable    = nullptr;
622     assignTable();
623     if(!m_xTable.is())
624     {
625         m_bNew      = true;
626         setModified(true);
627     }
628     InvalidateAll();
629 }
630 
631 TOTypeInfoSP OTableController::getTypeInfoByType(sal_Int32 _nDataType) const
632 {
633     return queryTypeInfoByType(_nDataType,m_aTypeInfo);
634 }
635 
636 void OTableController::appendColumns(Reference<XColumnsSupplier> const & _rxColSup, bool _bNew, bool _bKeyColumns)
637 {
638     try
639     {
640         // now append the columns
641         OSL_ENSURE(_rxColSup.is(),"No columns supplier");
642         if(!_rxColSup.is())
643             return;
644         Reference<XNameAccess> xColumns = _rxColSup->getColumns();
645         OSL_ENSURE(xColumns.is(),"No columns");
646         Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY);
647 
648         Reference<XAppend> xAppend(xColumns,UNO_QUERY);
649         OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
650 
651         for (auto const& row : m_vRowList)
652         {
653             OSL_ENSURE(row,"OTableRow is null!");
654             OFieldDescription* pField = row->GetActFieldDescr();
655             if ( !pField || (!_bNew && row->IsReadOnly() && !_bKeyColumns) )
656                 continue;
657 
658             Reference<XPropertySet> xColumn;
659             if(pField->IsPrimaryKey() || !_bKeyColumns)
660                 xColumn = xColumnFactory->createDataDescriptor();
661             if(xColumn.is())
662             {
663                 if(!_bKeyColumns)
664                     ::dbaui::setColumnProperties(xColumn,pField);
665                 else
666                     xColumn->setPropertyValue(PROPERTY_NAME,makeAny(pField->GetName()));
667 
668                 xAppend->appendByDescriptor(xColumn);
669                 xColumn = nullptr;
670                 // now only the settings are missing
671                 if(xColumns->hasByName(pField->GetName()))
672                 {
673                     xColumns->getByName(pField->GetName()) >>= xColumn;
674                     if(xColumn.is())
675                         pField->copyColumnSettingsTo(xColumn);
676                 }
677                 else
678                 {
679                     OSL_FAIL("OTableController::appendColumns: invalid field name!");
680                 }
681 
682             }
683         }
684     }
685     catch(const SQLException& )
686     {
687         showError( SQLExceptionInfo( ::cppu::getCaughtException() ) );
688     }
689     catch( const Exception& )
690     {
691         DBG_UNHANDLED_EXCEPTION("dbaccess");
692     }
693 }
694 
695 void OTableController::appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup, bool _bNew)
696 {
697     if(!_rxSup.is())
698         return; // the database doesn't support keys
699 
700     OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!");
701     Reference<XIndexAccess> xKeys = _rxSup->getKeys();
702     Reference<XPropertySet> xProp;
703     if (!xKeys.is())
704         return;
705     const sal_Int32 nCount = xKeys->getCount();
706     for(sal_Int32 i=0;i< nCount ;++i)
707     {
708         xKeys->getByIndex(i) >>= xProp;
709         sal_Int32 nKeyType = 0;
710         xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
711         if(KeyType::PRIMARY == nKeyType)
712         {
713             return; // primary key already exists after appending a column
714         }
715     }
716     Reference<XDataDescriptorFactory> xKeyFactory(xKeys,UNO_QUERY);
717     OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!");
718     if ( !xKeyFactory.is() )
719         return;
720     Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY);
721     OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
722 
723     Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor();
724     OSL_ENSURE(xKey.is(),"Key is null!");
725     xKey->setPropertyValue(PROPERTY_TYPE,makeAny(KeyType::PRIMARY));
726 
727     Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY);
728     if(xColSup.is())
729     {
730         appendColumns(xColSup,_bNew,true);
731         Reference<XNameAccess> xColumns = xColSup->getColumns();
732         if(xColumns->hasElements())
733             xAppend->appendByDescriptor(xKey);
734     }
735 }
736 
737 void OTableController::loadData()
738 {
739     // if the data structure already exists, empty it
740     m_vRowList.clear();
741 
742     std::shared_ptr<OTableRow>  pTabEdRow;
743     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
744     // fill data structure with data from DataDefinitionObject
745     if(m_xTable.is() && xMetaData.is())
746     {
747         Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY);
748         OSL_ENSURE(xColSup.is(),"No XColumnsSupplier!");
749         Reference<XNameAccess> xColumns = xColSup->getColumns();
750         // ReadOnly-Flag
751         // For Drop no row may be editable
752         // For Add only the empty rows may be editable
753         // For Add and Drop all rows can be edited
754         //  sal_Bool bReadOldRow = xMetaData->supportsAlterTableWithAddColumn() && xMetaData->supportsAlterTableWithDropColumn();
755         bool bIsAlterAllowed = isAlterAllowed();
756 
757         const Sequence<OUString> aColNames = xColumns->getElementNames();
758         for(const OUString& rColumn : aColNames)
759         {
760             Reference<XPropertySet> xColumn;
761             xColumns->getByName(rColumn) >>= xColumn;
762             sal_Int32 nType         = 0;
763             sal_Int32 nScale        = 0;
764             sal_Int32 nPrecision    = 0;
765             sal_Int32 nNullable     = 0;
766             sal_Int32 nFormatKey    = 0;
767             sal_Int32 nAlign        = 0;
768 
769             bool bIsAutoIncrement = false, bIsCurrency = false;
770             OUString sName,sDescription,sTypeName,sHelpText;
771             Any aControlDefault;
772 
773             // get the properties from the column
774             xColumn->getPropertyValue(PROPERTY_NAME)            >>= sName;
775             xColumn->getPropertyValue(PROPERTY_TYPENAME)        >>= sTypeName;
776             xColumn->getPropertyValue(PROPERTY_ISNULLABLE)      >>= nNullable;
777             xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bIsAutoIncrement;
778             xColumn->getPropertyValue(PROPERTY_ISCURRENCY)      >>= bIsCurrency;
779             xColumn->getPropertyValue(PROPERTY_TYPE)            >>= nType;
780             xColumn->getPropertyValue(PROPERTY_SCALE)           >>= nScale;
781             xColumn->getPropertyValue(PROPERTY_PRECISION)       >>= nPrecision;
782             xColumn->getPropertyValue(PROPERTY_DESCRIPTION)     >>= sDescription;
783 
784             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_HELPTEXT))
785                 xColumn->getPropertyValue(PROPERTY_HELPTEXT)    >>= sHelpText;
786 
787             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
788                 aControlDefault = xColumn->getPropertyValue(PROPERTY_CONTROLDEFAULT);
789             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_FORMATKEY))
790                 xColumn->getPropertyValue(PROPERTY_FORMATKEY)   >>= nFormatKey;
791             if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_ALIGN))
792                 xColumn->getPropertyValue(PROPERTY_ALIGN)       >>= nAlign;
793 
794             pTabEdRow = std::make_shared<OTableRow>();
795             pTabEdRow->SetReadOnly(!bIsAlterAllowed);
796             // search for type
797             bool bForce;
798             TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,"x",nPrecision,nScale,bIsAutoIncrement,bForce);
799             if ( !pTypeInfo )
800                 pTypeInfo = m_pTypeInfo;
801             pTabEdRow->SetFieldType( pTypeInfo, bForce );
802 
803             OFieldDescription* pActFieldDescr = pTabEdRow->GetActFieldDescr();
804             OSL_ENSURE(pActFieldDescr, "OTableController::loadData: invalid field description generated by the table row!");
805             if ( pActFieldDescr )
806             {
807                 pActFieldDescr->SetName(sName);
808                 pActFieldDescr->SetFormatKey(nFormatKey);
809                 pActFieldDescr->SetDescription(sDescription);
810                 pActFieldDescr->SetHelpText(sHelpText);
811                 pActFieldDescr->SetAutoIncrement(bIsAutoIncrement);
812                 pActFieldDescr->SetHorJustify(dbaui::mapTextJustify(nAlign));
813                 pActFieldDescr->SetCurrency(bIsCurrency);
814 
815                 // special data
816                 pActFieldDescr->SetIsNullable(nNullable);
817                 pActFieldDescr->SetControlDefault(aControlDefault);
818                 pActFieldDescr->SetPrecision(nPrecision);
819                 pActFieldDescr->SetScale(nScale);
820             }
821             m_vRowList.push_back( pTabEdRow);
822         }
823         // fill the primary  key information
824         Reference<XNameAccess> xKeyColumns  = getKeyColumns();
825         if(xKeyColumns.is())
826         {
827             const Sequence<OUString> aKeyColumnNames = xKeyColumns->getElementNames();
828             for(const OUString& rKeyColumn : aKeyColumnNames)
829             {
830                 for(std::shared_ptr<OTableRow> const& pRow : m_vRowList)
831                 {
832                     if(pRow->GetActFieldDescr()->GetName() == rKeyColumn)
833                     {
834                         pRow->SetPrimaryKey(true);
835                         break;
836                     }
837                 }
838             }
839         }
840     }
841 
842     // fill empty rows
843 
844     OTypeInfoMap::const_iterator aTypeIter = m_aTypeInfo.find(DataType::VARCHAR);
845     if(aTypeIter == m_aTypeInfo.end())
846         aTypeIter = m_aTypeInfo.begin();
847 
848     OSL_ENSURE(aTypeIter != m_aTypeInfo.end(),"We have no type information!");
849 
850     bool bReadRow = !isAddAllowed();
851     for(sal_Int32 i=m_vRowList.size(); i < NEWCOLS; i++ )
852     {
853         pTabEdRow = std::make_shared<OTableRow>();
854         pTabEdRow->SetReadOnly(bReadRow);
855         m_vRowList.push_back( pTabEdRow);
856     }
857 }
858 
859 Reference<XNameAccess> OTableController::getKeyColumns() const
860 {
861     return getPrimaryKeyColumns_throw(m_xTable);
862 }
863 
864 bool OTableController::checkColumns(bool _bNew)
865 {
866     bool bOk = true;
867     bool bFoundPKey = false;
868     Reference< XDatabaseMetaData > xMetaData = getMetaData( );
869     DatabaseMetaData aMetaData( getConnection() );
870 
871     ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
872     std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
873     std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
874     for(;aIter != aEnd;++aIter)
875     {
876         OFieldDescription* pFieldDesc = (*aIter)->GetActFieldDescr();
877         if (pFieldDesc && !pFieldDesc->GetName().isEmpty())
878         {
879             bFoundPKey |=  (*aIter)->IsPrimaryKey();
880             // first check for duplicate names
881             bool bDuplicateNameFound = std::any_of(aIter+1, aEnd,
882                 [&bCase, &pFieldDesc](const std::shared_ptr<OTableRow>& rxRow) {
883                     OFieldDescription* pCompareDesc = rxRow->GetActFieldDescr();
884                     return pCompareDesc && bCase(pCompareDesc->GetName(),pFieldDesc->GetName());
885                 });
886             if (bDuplicateNameFound)
887             {
888                 OUString strMessage = DBA_RES(STR_TABLEDESIGN_DUPLICATE_NAME);
889                 strMessage = strMessage.replaceFirst("$column$", pFieldDesc->GetName());
890                 OSQLWarningBox aWarning(getFrameWeld(), strMessage);
891                 aWarning.run();
892                 return false;
893             }
894         }
895     }
896     if ( _bNew && !bFoundPKey && aMetaData.supportsPrimaryKeys() )
897     {
898         OUString sTitle(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY_HEAD));
899         OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY));
900         OSQLMessageBox aBox(getFrameWeld(), sTitle,sMsg, MessBoxStyle::YesNoCancel | MessBoxStyle::DefaultYes);
901 
902         switch (aBox.run())
903         {
904         case RET_YES:
905         {
906             auto pNewRow = std::make_shared<OTableRow>();
907             TOTypeInfoSP pTypeInfo = ::dbaui::queryPrimaryKeyType(m_aTypeInfo);
908             if ( !pTypeInfo )
909                 break;
910 
911             pNewRow->SetFieldType( pTypeInfo );
912             OFieldDescription* pActFieldDescr = pNewRow->GetActFieldDescr();
913 
914             pActFieldDescr->SetAutoIncrement(false);
915             pActFieldDescr->SetIsNullable(ColumnValue::NO_NULLS);
916 
917             pActFieldDescr->SetName( createUniqueName("ID" ));
918             pActFieldDescr->SetPrimaryKey( true );
919             m_vRowList.insert(m_vRowList.begin(),pNewRow);
920 
921             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->Invalidate();
922             static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->RowInserted(0);
923         }
924         break;
925         case RET_CANCEL:
926             bOk = false;
927             break;
928         }
929     }
930     return bOk;
931 }
932 
933 void OTableController::alterColumns()
934 {
935     Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY_THROW);
936 
937     Reference<XNameAccess> xColumns = xColSup->getColumns();
938     Reference<XIndexAccess> xIdxColumns(xColumns,UNO_QUERY_THROW);
939     OSL_ENSURE(xColumns.is(),"No columns");
940     if ( !xColumns.is() )
941         return;
942     Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY);  // can be null
943 
944     sal_Int32 nColumnCount = xIdxColumns->getCount();
945     Reference<XDrop> xDrop(xColumns,UNO_QUERY);         // can be null
946     Reference<XAppend> xAppend(xColumns,UNO_QUERY);     // can be null
947     Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); // can be null
948 
949     bool bReload = false; // refresh the data
950 
951     // contains all columns names which are already handled those which are not in the list will be deleted
952     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
953 
954     std::set<OUString, comphelper::UStringMixLess> aColumns(
955         comphelper::UStringMixLess(
956             !xMetaData.is()
957             || xMetaData->supportsMixedCaseQuotedIdentifiers()));
958     std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
959     std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
960     // first look for columns where something other than the name changed
961     for(sal_Int32 nPos = 0;aIter != aEnd;++aIter,++nPos)
962     {
963         OSL_ENSURE(*aIter,"OTableRow is null!");
964         OFieldDescription* pField = (*aIter)->GetActFieldDescr();
965         if ( !pField )
966             continue;
967         if ( (*aIter)->IsReadOnly() )
968         {
969             aColumns.insert(pField->GetName());
970             continue;
971         }
972 
973         Reference<XPropertySet> xColumn;
974         if ( xColumns->hasByName(pField->GetName()) )
975         {
976             aColumns.insert(pField->GetName());
977             xColumns->getByName(pField->GetName()) >>= xColumn;
978             OSL_ENSURE(xColumn.is(),"Column is null!");
979 
980             sal_Int32 nType=0,nPrecision=0,nScale=0,nNullable=0;
981             bool bAutoIncrement = false;
982             OUString sTypeName,sDescription;
983 
984             xColumn->getPropertyValue(PROPERTY_TYPE)            >>= nType;
985             xColumn->getPropertyValue(PROPERTY_PRECISION)       >>= nPrecision;
986             xColumn->getPropertyValue(PROPERTY_SCALE)           >>= nScale;
987             xColumn->getPropertyValue(PROPERTY_ISNULLABLE)      >>= nNullable;
988             xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bAutoIncrement;
989             xColumn->getPropertyValue(PROPERTY_DESCRIPTION)     >>= sDescription;
990 
991             try { xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName; }
992             catch( const Exception& )
993             {
994                 OSL_FAIL( "no TypeName property?!" );
995                 // since this is a last minute fix for #i41785#, I want to be on the safe side,
996                 // and catch errors here as early as possible (instead of the whole process of altering
997                 // the columns failing)
998                 // Normally, sdbcx::Column objects are expected to have a TypeName property
999             }
1000 
1001             // check if something changed
1002             if((nType != pField->GetType()                  ||
1003                 sTypeName != pField->GetTypeName()         ||
1004                 (nPrecision != pField->GetPrecision() && nPrecision )       ||
1005                 nScale != pField->GetScale()                ||
1006                 nNullable != pField->GetIsNullable()        ||
1007                 sDescription != pField->GetDescription()        ||
1008                 bAutoIncrement != pField->IsAutoIncrement())&&
1009                 xColumnFactory.is())
1010             {
1011                 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1012                 ::dbaui::setColumnProperties(xNewColumn,pField);
1013                 // first try to alter the column
1014                 bool bNotOk = false;
1015                 try
1016                 {
1017                     // first try if we can alter the column
1018                     if(xAlter.is())
1019                         xAlter->alterColumnByName(pField->GetName(),xNewColumn);
1020                 }
1021                 catch(const SQLException&)
1022                 {
1023                     if(xDrop.is() && xAppend.is())
1024                     {
1025                         OUString aMessage( DBA_RES( STR_TABLEDESIGN_ALTER_ERROR ) );
1026                         aMessage = aMessage.replaceFirst( "$column$", pField->GetName() );
1027 
1028                         SQLExceptionInfo aError( ::cppu::getCaughtException() );
1029                         OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes , &aError);
1030                         bNotOk = aMsg.run() == RET_YES;
1031                     }
1032                     else
1033                         throw;
1034                 }
1035                 // if something went wrong or we can't alter columns
1036                 // drop and append a new one
1037                 if((!xAlter.is() || bNotOk) && xDrop.is() && xAppend.is())
1038                 {
1039                     xDrop->dropByName(pField->GetName());
1040                     try
1041                     {
1042                         xAppend->appendByDescriptor(xNewColumn);
1043                     }
1044                     catch(const SQLException&)
1045                     { // an error occurred so we try to reactivate the old one
1046                         xAppend->appendByDescriptor(xColumn);
1047                         throw;
1048                     }
1049                 }
1050                 // exceptions are caught outside
1051                 xNewColumn = nullptr;
1052                 if(xColumns->hasByName(pField->GetName()))
1053                     xColumns->getByName(pField->GetName()) >>= xColumn;
1054                 bReload = true;
1055             }
1056 
1057         }
1058         else if(xColumnFactory.is() && xAlter.is() && nPos < nColumnCount)
1059         { // we can't find the column so we could try it with the index before we drop and append a new column
1060             try
1061             {
1062                 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1063                 ::dbaui::setColumnProperties(xNewColumn,pField);
1064                 xAlter->alterColumnByIndex(nPos,xNewColumn);
1065                 if(xColumns->hasByName(pField->GetName()))
1066                 {   // ask for the append by name
1067                     aColumns.insert(pField->GetName());
1068                     xColumns->getByName(pField->GetName()) >>= xColumn;
1069                     if(xColumn.is())
1070                         pField->copyColumnSettingsTo(xColumn);
1071                 }
1072                 else
1073                 {
1074                     OSL_FAIL("OTableController::alterColumns: invalid column (2)!");
1075                 }
1076             }
1077             catch(const SQLException&)
1078             { // we couldn't alter the column so we have to add new columns
1079                 SQLExceptionInfo aError( ::cppu::getCaughtException() );
1080                 bReload = true;
1081                 if(xDrop.is() && xAppend.is())
1082                 {
1083                     OUString aMessage(DBA_RES(STR_TABLEDESIGN_ALTER_ERROR));
1084                     aMessage = aMessage.replaceFirst("$column$",pField->GetName());
1085                     OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, &aError);
1086                     if (aMsg.run() != RET_YES)
1087                     {
1088                         Reference<XPropertySet> xNewColumn(xIdxColumns->getByIndex(nPos),UNO_QUERY_THROW);
1089                         OUString sName;
1090                         xNewColumn->getPropertyValue(PROPERTY_NAME) >>= sName;
1091                         aColumns.insert(sName);
1092                         aColumns.insert(pField->GetName());
1093                         continue;
1094                     }
1095                 }
1096                 else
1097                     throw;
1098             }
1099         }
1100         else
1101             bReload = true;
1102     }
1103     // alter column settings
1104 
1105     // first look for columns where something other than the name changed
1106     for (auto const& row : m_vRowList)
1107     {
1108         OSL_ENSURE(row,"OTableRow is null!");
1109         OFieldDescription* pField = row->GetActFieldDescr();
1110         if ( !pField )
1111             continue;
1112         if ( row->IsReadOnly() )
1113         {
1114             aColumns.insert(pField->GetName());
1115             continue;
1116         }
1117 
1118         Reference<XPropertySet> xColumn;
1119         if ( xColumns->hasByName(pField->GetName()) )
1120         {
1121             xColumns->getByName(pField->GetName()) >>= xColumn;
1122             Reference<XPropertySetInfo> xInfo = xColumn->getPropertySetInfo();
1123             if ( xInfo->hasPropertyByName(PROPERTY_HELPTEXT) )
1124                 xColumn->setPropertyValue(PROPERTY_HELPTEXT,makeAny(pField->GetHelpText()));
1125 
1126             if(xInfo->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
1127                 xColumn->setPropertyValue(PROPERTY_CONTROLDEFAULT,pField->GetControlDefault());
1128             if(xInfo->hasPropertyByName(PROPERTY_FORMATKEY))
1129                 xColumn->setPropertyValue(PROPERTY_FORMATKEY,makeAny(pField->GetFormatKey()));
1130             if(xInfo->hasPropertyByName(PROPERTY_ALIGN))
1131                 xColumn->setPropertyValue(PROPERTY_ALIGN,makeAny(dbaui::mapTextAllign(pField->GetHorJustify())));
1132         }
1133     }
1134     // second drop all columns which could be found by name
1135     Reference<XNameAccess> xKeyColumns  = getKeyColumns();
1136     // now we have to look for the columns who could be deleted
1137     if ( xDrop.is() )
1138     {
1139         const Sequence<OUString> aColNames = xColumns->getElementNames();
1140         for(const OUString& rColumnName : aColNames)
1141         {
1142             if(aColumns.find(rColumnName) == aColumns.end()) // found a column to delete
1143             {
1144                 if(xKeyColumns.is() && xKeyColumns->hasByName(rColumnName)) // check if this column is a member of the primary key
1145                 {
1146                     OUString aMsgT(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN));
1147                     aMsgT = aMsgT.replaceFirst("$column$",rColumnName);
1148                     OUString aTitle(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN_TITLE));
1149                     OSQLMessageBox aMsg(getFrameWeld(), aTitle, aMsgT, MessBoxStyle::YesNo| MessBoxStyle::DefaultYes);
1150                     if (aMsg.run() == RET_YES)
1151                     {
1152                         xKeyColumns = nullptr;
1153                         dropPrimaryKey();
1154                     }
1155                     else
1156                     {
1157                         bReload = true;
1158                         continue;
1159                     }
1160                 }
1161                 try
1162                 {
1163                     xDrop->dropByName(rColumnName);
1164                 }
1165                 catch (const SQLException&)
1166                 {
1167                     OUString sError( DBA_RES( STR_TABLEDESIGN_COULD_NOT_DROP_COL ) );
1168                     sError = sError.replaceFirst( "$column$", rColumnName );
1169 
1170                     SQLException aNewException;
1171                     aNewException.Message = sError;
1172                     aNewException.SQLState = "S1000";
1173                     aNewException.NextException = ::cppu::getCaughtException();
1174 
1175                     throw aNewException;
1176                 }
1177             }
1178         }
1179     }
1180 
1181     // third append the new columns
1182     for(const auto& rxRow : m_vRowList)
1183     {
1184         OSL_ENSURE(rxRow,"OTableRow is null!");
1185         OFieldDescription* pField = rxRow->GetActFieldDescr();
1186         if ( !pField || rxRow->IsReadOnly() || aColumns.find(pField->GetName()) != aColumns.end() )
1187             continue;
1188 
1189         Reference<XPropertySet> xColumn;
1190         if(!xColumns->hasByName(pField->GetName()))
1191         {
1192             if(xColumnFactory.is() && xAppend.is())
1193             {// column not found by its name so we assume it is new
1194                 // Column is new
1195                 xColumn = xColumnFactory->createDataDescriptor();
1196                 ::dbaui::setColumnProperties(xColumn,pField);
1197                 xAppend->appendByDescriptor(xColumn);
1198                 if(xColumns->hasByName(pField->GetName()))
1199                 {   // ask for the append by name
1200                     aColumns.insert(pField->GetName());
1201                     xColumns->getByName(pField->GetName()) >>= xColumn;
1202                     if(xColumn.is())
1203                         pField->copyColumnSettingsTo(xColumn);
1204                 }
1205                 else
1206                 {
1207                     OSL_FAIL("OTableController::alterColumns: invalid column!");
1208                 }
1209             }
1210         }
1211     }
1212 
1213     // check if we have to do something with the primary key
1214     bool bNeedDropKey = false;
1215     bool bNeedAppendKey = false;
1216     if ( xKeyColumns.is() )
1217     {
1218         for(const auto& rxRow : m_vRowList)
1219         {
1220             OSL_ENSURE(rxRow,"OTableRow is null!");
1221             OFieldDescription* pField = rxRow->GetActFieldDescr();
1222             if ( !pField )
1223                 continue;
1224 
1225             if  (   pField->IsPrimaryKey()
1226                 &&  !xKeyColumns->hasByName( pField->GetName() )
1227                 )
1228             {   // new primary key column inserted which isn't already in the columns selection
1229                 bNeedDropKey = bNeedAppendKey = true;
1230                 break;
1231             }
1232             else if (   !pField->IsPrimaryKey()
1233                     &&  xKeyColumns->hasByName( pField->GetName() )
1234                     )
1235             {   // found a column which currently is in the primary key, but is marked not to be anymore
1236                 bNeedDropKey = bNeedAppendKey = true;
1237                 break;
1238             }
1239         }
1240     }
1241     else
1242     {   // no primary key available so we check if we should create one
1243         bNeedAppendKey = true;
1244     }
1245 
1246     if ( bNeedDropKey && xKeyColumns.is() && xKeyColumns->getElementNames().hasElements() )
1247         dropPrimaryKey();
1248 
1249     if ( bNeedAppendKey )
1250     {
1251         Reference< XKeysSupplier > xKeySup( m_xTable, UNO_QUERY );
1252         appendPrimaryKey( xKeySup ,false);
1253     }
1254 
1255     reSyncRows();
1256 
1257     if ( bReload )
1258         reload();
1259 }
1260 
1261 void OTableController::dropPrimaryKey()
1262 {
1263     SQLExceptionInfo aInfo;
1264     try
1265     {
1266         Reference<XKeysSupplier> xKeySup(m_xTable,UNO_QUERY);
1267         Reference<XIndexAccess> xKeys;
1268         if(xKeySup.is())
1269             xKeys = xKeySup->getKeys();
1270 
1271         if(xKeys.is())
1272         {
1273             Reference<XPropertySet> xProp;
1274             for(sal_Int32 i=0;i< xKeys->getCount();++i)
1275             {
1276                 xProp.set(xKeys->getByIndex(i),UNO_QUERY);
1277                 sal_Int32 nKeyType = 0;
1278                 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
1279                 if(KeyType::PRIMARY == nKeyType)
1280                 {
1281                     Reference<XDrop> xDrop(xKeys,UNO_QUERY);
1282                     xDrop->dropByIndex(i); // delete the key
1283                     break;
1284                 }
1285             }
1286         }
1287     }
1288     catch(const SQLContext& e)
1289     {
1290         aInfo = SQLExceptionInfo(e);
1291     }
1292     catch(const SQLWarning& e)
1293     {
1294         aInfo = SQLExceptionInfo(e);
1295     }
1296     catch(const SQLException& e)
1297     {
1298         aInfo = SQLExceptionInfo(e);
1299     }
1300     catch( const Exception& )
1301     {
1302         DBG_UNHANDLED_EXCEPTION("dbaccess");
1303     }
1304 
1305     showError(aInfo);
1306 }
1307 
1308 void OTableController::assignTable()
1309 {
1310     // get the table
1311     if(m_sName.isEmpty())
1312         return;
1313 
1314     Reference<XNameAccess> xNameAccess;
1315     Reference<XTablesSupplier> xSup(getConnection(),UNO_QUERY);
1316     if(!xSup.is())
1317         return;
1318 
1319     xNameAccess = xSup->getTables();
1320     OSL_ENSURE(xNameAccess.is(),"no nameaccess for the queries!");
1321 
1322     if(!xNameAccess->hasByName(m_sName))
1323         return;
1324 
1325     Reference<XPropertySet> xProp(xNameAccess->getByName(m_sName), css::uno::UNO_QUERY);
1326     if (!xProp.is())
1327         return;
1328 
1329     m_xTable = xProp;
1330     startTableListening();
1331 
1332     // check if we set the table editable
1333     Reference<XDatabaseMetaData> xMeta = getConnection()->getMetaData();
1334     setEditable( xMeta.is() && !xMeta->isReadOnly() && (isAlterAllowed() || isDropAllowed() || isAddAllowed()) );
1335     if(!isEditable())
1336     {
1337         for( const auto& rTableRow : m_vRowList )
1338         {
1339             rTableRow->SetReadOnly();
1340         }
1341     }
1342     m_bNew = false;
1343     // be notified when the table is in disposing
1344     InvalidateAll();
1345 }
1346 
1347 bool OTableController::isAddAllowed() const
1348 {
1349     Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1350     bool bAddAllowed = !m_xTable.is();
1351     if(xColsSup.is())
1352         bAddAllowed = Reference<XAppend>(xColsSup->getColumns(),UNO_QUERY).is();
1353 
1354     try
1355     {
1356         Reference< XDatabaseMetaData > xMetaData = getMetaData( );
1357         bAddAllowed = bAddAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithAddColumn());
1358     }
1359     catch(Exception&)
1360     {
1361         DBG_UNHANDLED_EXCEPTION("dbaccess");
1362         bAddAllowed = false;
1363     }
1364 
1365     return bAddAllowed;
1366 }
1367 
1368 bool OTableController::isDropAllowed() const
1369 {
1370     Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1371     bool bDropAllowed = !m_xTable.is();
1372     if(xColsSup.is())
1373     {
1374         Reference<XNameAccess> xNameAccess = xColsSup->getColumns();
1375         bDropAllowed = Reference<XDrop>(xNameAccess,UNO_QUERY).is() && xNameAccess->hasElements();
1376     }
1377 
1378     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1379     bDropAllowed = bDropAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithDropColumn());
1380 
1381     return bDropAllowed;
1382 }
1383 
1384 bool OTableController::isAlterAllowed() const
1385 {
1386     bool bAllowed(!m_xTable.is() || Reference<XAlterTable>(m_xTable,UNO_QUERY).is());
1387     return bAllowed;
1388 }
1389 
1390 void OTableController::reSyncRows()
1391 {
1392     bool bAlterAllowed  = isAlterAllowed();
1393     bool bAddAllowed    = isAddAllowed();
1394     for (auto const& row : m_vRowList)
1395     {
1396         OSL_ENSURE(row,"OTableRow is null!");
1397         OFieldDescription* pField = row->GetActFieldDescr();
1398         if ( pField )
1399             row->SetReadOnly(!bAlterAllowed);
1400         else
1401             row->SetReadOnly(!bAddAllowed);
1402 
1403     }
1404     static_cast<OTableDesignView*>(getView())->reSync();    // show the windows and fill with our information
1405 
1406     ClearUndoManager();
1407     setModified(false);     // and we are not modified yet
1408 }
1409 
1410 OUString OTableController::createUniqueName(const OUString& _rName)
1411 {
1412     OUString sName = _rName;
1413     Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1414 
1415     ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
1416 
1417     auto lHasName = [&bCase, &sName](const std::shared_ptr<OTableRow>& rxRow) {
1418         OFieldDescription* pFieldDesc = rxRow->GetActFieldDescr();
1419         return pFieldDesc && !pFieldDesc->GetName().isEmpty() && bCase(sName, pFieldDesc->GetName());
1420     };
1421 
1422     sal_Int32 i = 0;
1423     while(std::any_of(m_vRowList.begin(), m_vRowList.end(), lHasName))
1424     {
1425         // found a second name of _rName so we need another
1426         sName = _rName + OUString::number(++i);
1427     }
1428     return sName;
1429 }
1430 
1431 OUString OTableController::getPrivateTitle() const
1432 {
1433     OUString sTitle;
1434     try
1435     {
1436         // get the table
1437         if ( !m_sName.isEmpty() && getConnection().is() )
1438         {
1439             if ( m_xTable.is() )
1440                 sTitle = ::dbtools::composeTableName( getConnection()->getMetaData(), m_xTable, ::dbtools::EComposeRule::InDataManipulation, false );
1441             else
1442                 sTitle = m_sName;
1443         }
1444         if ( sTitle.isEmpty() )
1445         {
1446             OUString aName = DBA_RES(STR_TBL_TITLE);
1447             sTitle = aName.getToken(0,' ') + OUString::number(getCurrentStartNumber());
1448         }
1449     }
1450     catch( const Exception& )
1451     {
1452         DBG_UNHANDLED_EXCEPTION("dbaccess");
1453     }
1454     return sTitle;
1455 }
1456 
1457 void OTableController::reload()
1458 {
1459     loadData();                 // fill the column information from the table
1460     static_cast<OTableDesignView*>(getView())->reSync();    // show the windows and fill with our information
1461     ClearUndoManager();
1462     setModified(false);     // and we are not modified yet
1463     static_cast<OTableDesignView*>(getView())->Invalidate();
1464 }
1465 
1466 sal_Int32 OTableController::getFirstEmptyRowPosition()
1467 {
1468     sal_Int32 nRet = 0;
1469     bool bFoundElem = false;
1470     for (auto const& row : m_vRowList)
1471     {
1472         if ( !row || !row->GetActFieldDescr() || row->GetActFieldDescr()->GetName().isEmpty() )
1473         {
1474             bFoundElem = true;
1475             break;
1476         }
1477         ++nRet;
1478     }
1479     if (!bFoundElem)
1480     {
1481         bool bReadRow = !isAddAllowed();
1482         auto pTabEdRow = std::make_shared<OTableRow>();
1483         pTabEdRow->SetReadOnly(bReadRow);
1484         nRet = m_vRowList.size();
1485         m_vRowList.push_back( pTabEdRow);
1486     }
1487     return nRet;
1488 }
1489 
1490 bool OTableController::isAutoIncrementPrimaryKey() const
1491 {
1492     return getSdbMetaData().isAutoIncrementPrimaryKey();
1493 }
1494 
1495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1496