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 <indexcollection.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <com/sun/star/sdbcx/XAppend.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
25 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
26 #include <comphelper/extract.hxx>
27 #include <com/sun/star/sdbcx/XDrop.hpp>
28 #include <com/sun/star/container/XNameContainer.hpp>
29 
30 namespace dbaui
31 {
32 
33     using namespace ::com::sun::star::uno;
34     using namespace ::com::sun::star::container;
35     using namespace ::com::sun::star::beans;
36     using namespace ::com::sun::star::sdbcx;
37     using namespace ::com::sun::star::sdbc;
38 
39     // OIndexCollection
OIndexCollection()40     OIndexCollection::OIndexCollection()
41     {
42     }
43 
OIndexCollection(const OIndexCollection & _rSource)44     OIndexCollection::OIndexCollection(const OIndexCollection& _rSource)
45     {
46         *this = _rSource;
47     }
48 
operator =(const OIndexCollection & _rSource)49     OIndexCollection& OIndexCollection::operator=(const OIndexCollection& _rSource)
50     {
51         detach();
52         m_xIndexes = _rSource.m_xIndexes;
53         m_aIndexes = _rSource.m_aIndexes;
54         return *this;
55     }
56 
attach(const Reference<XNameAccess> & _rxIndexes)57     void OIndexCollection::attach(const Reference< XNameAccess >& _rxIndexes)
58     {
59         implConstructFrom(_rxIndexes);
60     }
61 
detach()62     void OIndexCollection::detach()
63     {
64         m_xIndexes.clear();
65         m_aIndexes.clear();
66     }
67 
find(const OUString & _rName) const68     Indexes::const_iterator OIndexCollection::find(const OUString& _rName) const
69     {
70         // loop'n'compare
71         return std::find_if(m_aIndexes.cbegin(), m_aIndexes.cend(),
72             [&_rName](const OIndex& rIndex) { return rIndex.sName == _rName; });
73     }
74 
find(const OUString & _rName)75     Indexes::iterator OIndexCollection::find(const OUString& _rName)
76     {
77         // loop'n'compare
78         return std::find_if(m_aIndexes.begin(), m_aIndexes.end(),
79             [&_rName](const OIndex& rIndex) { return rIndex.sName == _rName; });
80     }
81 
findOriginal(const OUString & _rName) const82     Indexes::const_iterator OIndexCollection::findOriginal(const OUString& _rName) const
83     {
84         // loop'n'compare
85         return std::find_if(m_aIndexes.cbegin(), m_aIndexes.cend(),
86             [&_rName](const OIndex& rIndex) { return rIndex.getOriginalName() == _rName; });
87     }
88 
findOriginal(const OUString & _rName)89     Indexes::iterator OIndexCollection::findOriginal(const OUString& _rName)
90     {
91         // loop'n'compare
92         return std::find_if(m_aIndexes.begin(), m_aIndexes.end(),
93             [&_rName](const OIndex& rIndex) { return rIndex.getOriginalName() == _rName; });
94     }
95 
commitNewIndex(const Indexes::iterator & _rPos)96     void OIndexCollection::commitNewIndex(const Indexes::iterator& _rPos)
97     {
98         OSL_ENSURE(_rPos->isNew(), "OIndexCollection::commitNewIndex: index must be new!");
99 
100         try
101         {
102             Reference< XDataDescriptorFactory > xIndexFactory(m_xIndexes, UNO_QUERY);
103             Reference< XAppend > xAppendIndex(xIndexFactory, UNO_QUERY);
104             if (!xAppendIndex.is())
105             {
106                 OSL_FAIL("OIndexCollection::commitNewIndex: missing an interface of the index container!");
107                 return;
108             }
109 
110             Reference< XPropertySet > xIndexDescriptor = xIndexFactory->createDataDescriptor();
111             Reference< XColumnsSupplier > xColsSupp(xIndexDescriptor, UNO_QUERY);
112             Reference< XNameAccess > xCols;
113             if (xColsSupp.is())
114                 xCols = xColsSupp->getColumns();
115 
116             Reference< XDataDescriptorFactory > xColumnFactory(xCols, UNO_QUERY);
117             Reference< XAppend > xAppendCols(xColumnFactory, UNO_QUERY);
118             if (!xAppendCols.is())
119             {
120                 OSL_FAIL("OIndexCollection::commitNewIndex: invalid index descriptor returned!");
121                 return;
122             }
123 
124             // set the properties
125             static const char s_sNamePropertyName[] = "Name";
126             // the index' own props
127             xIndexDescriptor->setPropertyValue("IsUnique", css::uno::makeAny(_rPos->bUnique));
128             xIndexDescriptor->setPropertyValue(s_sNamePropertyName, makeAny(_rPos->sName));
129 
130             // the fields
131             for (auto const& field : _rPos->aFields)
132             {
133                 OSL_ENSURE(!xCols->hasByName(field.sFieldName), "OIndexCollection::commitNewIndex: double column name (need to prevent this outside)!");
134 
135                 Reference< XPropertySet > xColDescriptor = xColumnFactory->createDataDescriptor();
136                 OSL_ENSURE(xColDescriptor.is(), "OIndexCollection::commitNewIndex: invalid column descriptor!");
137                 if (xColDescriptor.is())
138                 {
139                     xColDescriptor->setPropertyValue("IsAscending", css::uno::makeAny(field.bSortAscending));
140                     xColDescriptor->setPropertyValue(s_sNamePropertyName, makeAny(field.sFieldName));
141                     xAppendCols->appendByDescriptor(xColDescriptor);
142                 }
143             }
144 
145             xAppendIndex->appendByDescriptor(xIndexDescriptor);
146 
147             _rPos->flagAsCommitted(GrantIndexAccess());
148             _rPos->clearModified();
149         }
150         catch(SQLException&)
151         {   // allowed to pass
152             throw;
153         }
154         catch( const Exception& )
155         {
156             DBG_UNHANDLED_EXCEPTION("dbaccess");
157         }
158     }
159 
dropNoRemove(const Indexes::iterator & _rPos)160     bool OIndexCollection::dropNoRemove(const Indexes::iterator& _rPos)
161     {
162         try
163         {
164             OSL_ENSURE(m_xIndexes->hasByName(_rPos->getOriginalName()), "OIndexCollection::drop: invalid name!");
165 
166             Reference< XDrop > xDropIndex(m_xIndexes, UNO_QUERY);
167             if (!xDropIndex.is())
168             {
169                 OSL_FAIL("OIndexCollection::drop: no XDrop interface!");
170                 return false;
171             }
172 
173             xDropIndex->dropByName(_rPos->getOriginalName());
174         }
175         catch(SQLException&)
176         {   // allowed to pass
177             throw;
178         }
179         catch( const Exception& )
180         {
181             DBG_UNHANDLED_EXCEPTION("dbaccess");
182             return false;
183         }
184 
185         // adjust the OIndex structure
186         Indexes::iterator aDropped = findOriginal(_rPos->getOriginalName());
187         OSL_ENSURE(aDropped != m_aIndexes.end(), "OIndexCollection::drop: invalid original name, but successful commit?!");
188         aDropped->flagAsNew(GrantIndexAccess());
189 
190         return true;
191     }
192 
drop(const Indexes::iterator & _rPos)193     bool OIndexCollection::drop(const Indexes::iterator& _rPos)
194     {
195         OSL_ENSURE((_rPos >= m_aIndexes.begin()) && (_rPos < m_aIndexes.end()),
196             "OIndexCollection::drop: invalid position (fasten your seatbelt... this will crash)!");
197 
198         if (!_rPos->isNew())
199             if (!dropNoRemove(_rPos))
200                 return false;
201 
202         // adjust the index array
203         m_aIndexes.erase(_rPos);
204         return true;
205     }
206 
implFillIndexInfo(OIndex & _rIndex)207     void OIndexCollection::implFillIndexInfo(OIndex& _rIndex)
208     {
209         // get the UNO descriptor for the index
210         Reference< XPropertySet > xIndex;
211         m_xIndexes->getByName(_rIndex.getOriginalName()) >>= xIndex;
212         if (!xIndex.is())
213         {
214             OSL_FAIL("OIndexCollection::implFillIndexInfo: got an invalid index object!");
215         }
216         else
217             implFillIndexInfo(_rIndex, xIndex);
218     }
219 
implFillIndexInfo(OIndex & _rIndex,const Reference<XPropertySet> & _rxDescriptor)220     void OIndexCollection::implFillIndexInfo(OIndex& _rIndex, const Reference< XPropertySet >& _rxDescriptor)
221     {
222         _rIndex.bPrimaryKey = ::cppu::any2bool(_rxDescriptor->getPropertyValue("IsPrimaryKeyIndex"));
223         _rIndex.bUnique = ::cppu::any2bool(_rxDescriptor->getPropertyValue("IsUnique"));
224         _rxDescriptor->getPropertyValue("Catalog") >>= _rIndex.sDescription;
225 
226         // the columns
227         Reference< XColumnsSupplier > xSuppCols(_rxDescriptor, UNO_QUERY);
228         Reference< XNameAccess > xCols;
229         if (xSuppCols.is())
230             xCols = xSuppCols->getColumns();
231         OSL_ENSURE(xCols.is(), "OIndexCollection::implFillIndexInfo: the index does not have columns!");
232         if (xCols.is())
233         {
234             Sequence< OUString > aFieldNames = xCols->getElementNames();
235             _rIndex.aFields.resize(aFieldNames.getLength());
236 
237             const OUString* pFieldNames = aFieldNames.getConstArray();
238             const OUString* pFieldNamesEnd = pFieldNames + aFieldNames.getLength();
239             IndexFields::iterator aCopyTo = _rIndex.aFields.begin();
240 
241             Reference< XPropertySet > xIndexColumn;
242             for (;pFieldNames < pFieldNamesEnd; ++pFieldNames, ++aCopyTo)
243             {
244                 // extract the column
245                 xIndexColumn.clear();
246                 xCols->getByName(*pFieldNames) >>= xIndexColumn;
247                 if (!xIndexColumn.is())
248                 {
249                     OSL_FAIL("OIndexCollection::implFillIndexInfo: invalid index column!");
250                     --aCopyTo;
251                     continue;
252                 }
253 
254                 // get the relevant properties
255                 aCopyTo->sFieldName = *pFieldNames;
256                 aCopyTo->bSortAscending = ::cppu::any2bool(xIndexColumn->getPropertyValue("IsAscending"));
257             }
258 
259             _rIndex.aFields.resize(aCopyTo - _rIndex.aFields.begin());
260                 // (just in case some fields were invalid ...)
261         }
262     }
263 
resetIndex(const Indexes::iterator & _rPos)264     void OIndexCollection::resetIndex(const Indexes::iterator& _rPos)
265     {
266         OSL_ENSURE(_rPos >= m_aIndexes.begin() && _rPos < m_aIndexes.end(),
267             "OIndexCollection::resetIndex: invalid position!");
268 
269         try
270         {
271             _rPos->sName = _rPos->getOriginalName();
272             implFillIndexInfo(*_rPos);
273 
274             _rPos->clearModified();
275             _rPos->flagAsCommitted(GrantIndexAccess());
276         }
277         catch(SQLException&)
278         {   // allowed to pass
279             throw;
280         }
281         catch( const Exception& )
282         {
283             DBG_UNHANDLED_EXCEPTION("dbaccess");
284         }
285     }
286 
insert(const OUString & _rName)287     Indexes::iterator OIndexCollection::insert(const OUString& _rName)
288     {
289         OSL_ENSURE(end() == find(_rName), "OIndexCollection::insert: invalid new name!");
290         OIndex aNewIndex((OUString()));  // the empty string indicates the index is a new one
291         aNewIndex.sName = _rName;
292         m_aIndexes.push_back(aNewIndex);
293         return m_aIndexes.end() - 1;    // the last element is the new one ...
294     }
295 
implConstructFrom(const Reference<XNameAccess> & _rxIndexes)296     void OIndexCollection::implConstructFrom(const Reference< XNameAccess >& _rxIndexes)
297     {
298         detach();
299 
300         m_xIndexes = _rxIndexes;
301         if (m_xIndexes.is())
302         {
303             // loop through all the indexes
304             Sequence< OUString > aNames = m_xIndexes->getElementNames();
305             const OUString* pNames = aNames.getConstArray();
306             const OUString* pEnd = pNames + aNames.getLength();
307             for (; pNames < pEnd; ++pNames)
308             {
309                 // extract the index object
310                 Reference< XPropertySet > xIndex;
311                 m_xIndexes->getByName(*pNames) >>= xIndex;
312                 if (!xIndex.is())
313                 {
314                     OSL_FAIL("OIndexCollection::implConstructFrom: got an invalid index object ... ignoring!");
315                     continue;
316                 }
317 
318                 // fill the OIndex structure
319                 OIndex aCurrentIndex(*pNames);
320                 implFillIndexInfo(aCurrentIndex);
321                 m_aIndexes.push_back(aCurrentIndex);
322             }
323         }
324     }
325 
326 }   // namespace dbaui
327 
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
329