1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <com/sun/star/container/ElementExistException.hpp>
23 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
24 #include <connectivity/sdbcx/VCollection.hxx>
25 #include <connectivity/sdbcx/VDescriptor.hxx>
26 #include <com/sun/star/sdbc/SQLException.hpp>
27 #include <connectivity/dbexception.hxx>
28 #include <comphelper/enumhelper.hxx>
29 #include <comphelper/types.hxx>
30 #include <comphelper/property.hxx>
31 #include <comphelper/servicehelper.hxx>
32 #include <cppuhelper/exc_hlp.hxx>
33 #include <o3tl/unreachable.hxx>
34 #include <TConnection.hxx>
35 #include <strings.hrc>
36 #include <resource/sharedresources.hxx>
38 using namespace connectivity::sdbcx
;
39 using namespace connectivity
;
40 using namespace comphelper
;
41 using namespace ::cppu
;
42 using namespace ::com::sun::star::beans
;
43 using namespace ::com::sun::star::uno
;
44 using namespace ::com::sun::star::lang
;
45 using namespace ::com::sun::star::sdbc
;
46 using namespace ::com::sun::star::container
;
47 using namespace ::com::sun::star::util
;
51 template < typename T
> class OHardRefMap
: public connectivity::sdbcx::IObjectCollection
53 typedef std::multimap
< OUString
, T
, ::comphelper::UStringMixLess
> ObjectMap
;
54 typedef typename
ObjectMap::iterator ObjectIter
;
55 typedef typename
ObjectMap::value_type ObjectEntry
;
58 // this combination of map and vector is used to have a fast name and index access
59 std::vector
< ObjectIter
> m_aElements
; // hold the iterators which point to map
60 ObjectMap m_aNameMap
; // hold the elements and a name
62 OHardRefMap(bool _bCase
)
67 virtual bool exists(const OUString
& _sName
) override
69 return m_aNameMap
.find(_sName
) != m_aNameMap
.end();
72 virtual bool empty() override
74 return m_aNameMap
.empty();
77 virtual void swapAll() override
79 std::vector
< ObjectIter
>(m_aElements
).swap(m_aElements
);
80 ObjectMap(m_aNameMap
).swap(m_aNameMap
);
83 virtual void swap() override
85 std::vector
< ObjectIter
>().swap(m_aElements
);
87 OSL_ENSURE( m_aNameMap
.empty(), "swap: what did disposeElements do?" );
88 ObjectMap( m_aNameMap
).swap( m_aNameMap
);
89 // Note that it's /important/ to construct the new ObjectMap from m_aNameMap before
90 // swapping. This way, it's ensured that the compare object held by these maps is preserved
91 // during the swap. If we would not do this, the UStringMixLess instance which is used would be
92 // default constructed (instead of being constructed from the same instance in m_aNameMap), and
93 // it's case-sensitive flag would have an unpredictable value.
96 virtual void clear() override
102 virtual void insert(const OUString
& _sName
,const ObjectType
& _xObject
) override
104 m_aElements
.push_back(m_aNameMap
.insert(m_aNameMap
.begin(), ObjectEntry(_sName
,_xObject
)));
107 virtual void reFill(const ::std::vector
< OUString
> &_rVector
) override
109 OSL_ENSURE(m_aNameMap
.empty(),"OCollection::reFill: collection isn't empty");
110 m_aElements
.reserve(_rVector
.size());
112 for (auto const& elem
: _rVector
)
113 m_aElements
.push_back(m_aNameMap
.insert(m_aNameMap
.begin(), ObjectEntry(elem
,ObjectType())));
116 virtual bool rename(const OUString
& _sOldName
, const OUString
& _sNewName
) override
119 ObjectIter aIter
= m_aNameMap
.find(_sOldName
);
120 if ( aIter
!= m_aNameMap
.end() )
122 typename
std::vector
< ObjectIter
>::iterator aFind
= std::find(m_aElements
.begin(),m_aElements
.end(),aIter
);
123 if(m_aElements
.end() != aFind
)
125 (*aFind
) = m_aNameMap
.insert(m_aNameMap
.begin(), ObjectEntry(_sNewName
,(*aFind
)->second
));
126 m_aNameMap
.erase(aIter
);
134 virtual sal_Int32
size() override
136 return static_cast<sal_Int32
>(m_aNameMap
.size());
139 virtual Sequence
< OUString
> getElementNames() override
141 Sequence
< OUString
> aNameList(m_aElements
.size());
143 OUString
* pStringArray
= aNameList
.getArray();
144 for(const auto& rIter
: m_aElements
)
146 *pStringArray
= rIter
->first
;
153 virtual OUString
getName(sal_Int32 _nIndex
) override
155 return m_aElements
[_nIndex
]->first
;
158 virtual void disposeAndErase(sal_Int32 _nIndex
) override
160 OSL_ENSURE(_nIndex
>= 0 && _nIndex
< static_cast<sal_Int32
>(m_aElements
.size()),"Illegal argument!");
161 Reference
<XComponent
> xComp(m_aElements
[_nIndex
]->second
.get(),UNO_QUERY
);
162 ::comphelper::disposeComponent(xComp
);
163 m_aElements
[_nIndex
]->second
= T();
165 OUString sName
= m_aElements
[_nIndex
]->first
;
166 m_aElements
.erase(m_aElements
.begin()+_nIndex
);
167 m_aNameMap
.erase(sName
);
170 virtual void disposeElements() override
172 for (auto & name
: m_aNameMap
)
174 Reference
<XComponent
> xComp(name
.second
.get(),UNO_QUERY
);
177 ::comphelper::disposeComponent(xComp
);
185 virtual sal_Int32
findColumn( const OUString
& columnName
) override
187 ObjectIter aIter
= m_aNameMap
.find(columnName
);
188 OSL_ENSURE(aIter
!= m_aNameMap
.end(),"findColumn:: Illegal name!");
189 return m_aElements
.size() - (m_aElements
.end() - std::find(m_aElements
.begin(),m_aElements
.end(),aIter
));
192 virtual ObjectType
getObject(sal_Int32 _nIndex
) override
194 OSL_ENSURE(_nIndex
>= 0 && _nIndex
< static_cast<sal_Int32
>(m_aElements
.size()),"Illegal argument!");
195 return m_aElements
[_nIndex
]->second
;
198 virtual ObjectType
getObject(const OUString
& columnName
) override
200 return m_aNameMap
.find(columnName
)->second
;
203 virtual void setObject(sal_Int32 _nIndex
,const ObjectType
& _xObject
) override
205 OSL_ENSURE(_nIndex
>= 0 && _nIndex
< static_cast<sal_Int32
>(m_aElements
.size()),"Illegal argument!");
206 m_aElements
[_nIndex
]->second
= _xObject
;
209 bool isCaseSensitive() const override
211 return m_aNameMap
.key_comp().isCaseSensitive();
217 IObjectCollection::~IObjectCollection() {}
219 IMPLEMENT_SERVICE_INFO(OCollection
,"com.sun.star.sdbcx.VContainer" , "com.sun.star.sdbcx.Container")
221 OCollection::OCollection(::cppu::OWeakObject
& _rParent
223 , ::osl::Mutex
& _rMutex
224 , const ::std::vector
< OUString
> &_rVector
225 , bool _bUseIndexOnly
227 :m_aContainerListeners(_rMutex
)
228 ,m_aRefreshListeners(_rMutex
)
231 ,m_bUseIndexOnly(_bUseIndexOnly
)
235 m_pElements
.reset(new OHardRefMap
< ObjectType
>(_bCase
));
239 m_pElements
.reset(new OHardRefMap
< WeakReference
< XPropertySet
> >(_bCase
));
241 m_pElements
->reFill(_rVector
);
244 OCollection::~OCollection()
248 Any SAL_CALL
OCollection::queryInterface( const Type
& rType
)
250 if ( m_bUseIndexOnly
&& rType
== cppu::UnoType
<XNameAccess
>::get() )
254 return OCollectionBase::queryInterface( rType
);
257 Sequence
< Type
> SAL_CALL
OCollection::getTypes()
259 if ( m_bUseIndexOnly
)
261 Sequence
< Type
> aTypes(OCollectionBase::getTypes());
262 Type
* pBegin
= aTypes
.getArray();
263 Type
* pEnd
= pBegin
+ aTypes
.getLength();
265 std::vector
<Type
> aOwnTypes
;
266 aOwnTypes
.reserve(aTypes
.getLength());
267 Type aType
= cppu::UnoType
<XNameAccess
>::get();
268 for(;pBegin
!= pEnd
; ++pBegin
)
270 if ( *pBegin
!= aType
)
271 aOwnTypes
.push_back(*pBegin
);
273 return Sequence
< Type
>(aOwnTypes
.data(), aOwnTypes
.size());
275 return OCollectionBase::getTypes( );
278 void OCollection::clear_NoDispose()
280 ::osl::MutexGuard
aGuard(m_rMutex
);
282 m_pElements
->clear();
283 m_pElements
->swapAll();
287 void OCollection::disposing()
289 m_aContainerListeners
.disposeAndClear(EventObject(static_cast<XTypeProvider
*>(this)));
290 m_aRefreshListeners
.disposeAndClear(EventObject(static_cast<XTypeProvider
*>(this)));
292 ::osl::MutexGuard
aGuard(m_rMutex
);
299 Any SAL_CALL
OCollection::getByIndex( sal_Int32 Index
)
301 ::osl::MutexGuard
aGuard(m_rMutex
);
302 if (Index
< 0 || Index
>= m_pElements
->size() )
303 throw IndexOutOfBoundsException(OUString::number(Index
),static_cast<XTypeProvider
*>(this));
305 return makeAny(getObject(Index
));
308 Any SAL_CALL
OCollection::getByName( const OUString
& aName
)
310 ::osl::MutexGuard
aGuard(m_rMutex
);
312 if ( !m_pElements
->exists(aName
) )
314 ::connectivity::SharedResources aResources
;
315 const OUString
sError( aResources
.getResourceStringWithSubstitution(
319 throw NoSuchElementException( sError
, static_cast< XTypeProvider
* >( this ) );
322 return makeAny(getObject(m_pElements
->findColumn(aName
)));
325 Sequence
< OUString
> SAL_CALL
OCollection::getElementNames( )
327 ::osl::MutexGuard
aGuard(m_rMutex
);
328 return m_pElements
->getElementNames();
331 void SAL_CALL
OCollection::refresh( )
333 ::osl::MutexGuard
aGuard(m_rMutex
);
338 EventObject
aEvt(static_cast<XTypeProvider
*>(this));
339 m_aRefreshListeners
.notifyEach( &XRefreshListener::refreshed
, aEvt
);
342 void OCollection::reFill(const ::std::vector
< OUString
> &_rVector
)
344 m_pElements
->reFill(_rVector
);
347 // XDataDescriptorFactory
348 Reference
< XPropertySet
> SAL_CALL
OCollection::createDataDescriptor( )
350 ::osl::MutexGuard
aGuard(m_rMutex
);
352 return createDescriptor();
355 OUString
OCollection::getNameForObject(const ObjectType
& _xObject
)
357 OSL_ENSURE(_xObject
.is(),"OCollection::getNameForObject: Object is NULL!");
359 _xObject
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME
)) >>= sName
;
364 void SAL_CALL
OCollection::appendByDescriptor( const Reference
< XPropertySet
>& descriptor
)
366 ::osl::ClearableMutexGuard
aGuard(m_rMutex
);
368 OUString sName
= getNameForObject( descriptor
);
370 if ( m_pElements
->exists(sName
) )
371 throw ElementExistException(sName
,static_cast<XTypeProvider
*>(this));
373 ObjectType xNewlyCreated
= appendObject( sName
, descriptor
);
374 if ( !xNewlyCreated
.is() )
375 throw RuntimeException();
377 ODescriptor
* pDescriptor
= comphelper::getUnoTunnelImplementation
<ODescriptor
>( xNewlyCreated
);
379 pDescriptor
->setNew( false );
381 sName
= getNameForObject( xNewlyCreated
);
382 if ( !m_pElements
->exists( sName
) ) // this may happen when the derived class included it itself
383 m_pElements
->insert( sName
, xNewlyCreated
);
385 // notify our container listeners
386 ContainerEvent
aEvent(static_cast<XContainer
*>(this), makeAny(sName
), makeAny(xNewlyCreated
), Any());
388 m_aContainerListeners
.notifyEach( &XContainerListener::elementInserted
, aEvent
);
392 void SAL_CALL
OCollection::dropByName( const OUString
& elementName
)
394 ::osl::MutexGuard
aGuard(m_rMutex
);
396 if ( !m_pElements
->exists(elementName
) )
397 throw NoSuchElementException(elementName
,static_cast<XTypeProvider
*>(this));
399 dropImpl(m_pElements
->findColumn(elementName
));
402 void SAL_CALL
OCollection::dropByIndex( sal_Int32 index
)
404 ::osl::MutexGuard
aGuard(m_rMutex
);
405 if(index
<0 || index
>= getCount())
406 throw IndexOutOfBoundsException(OUString::number(index
),static_cast<XTypeProvider
*>(this));
411 void OCollection::dropImpl(sal_Int32 _nIndex
, bool _bReallyDrop
)
413 OUString elementName
= m_pElements
->getName(_nIndex
);
416 dropObject(_nIndex
,elementName
);
418 m_pElements
->disposeAndErase(_nIndex
);
420 // notify our container listeners
421 notifyElementRemoved(elementName
);
424 void OCollection::notifyElementRemoved(const OUString
& _sName
)
426 ContainerEvent
aEvent(static_cast<XContainer
*>(this), makeAny(_sName
), Any(), Any());
427 // note that xExistent may be empty, in case somebody removed the data source while it is not alive at this moment
428 OInterfaceIteratorHelper2
aListenerLoop(m_aContainerListeners
);
429 while (aListenerLoop
.hasMoreElements())
430 static_cast<XContainerListener
*>(aListenerLoop
.next())->elementRemoved(aEvent
);
433 sal_Int32 SAL_CALL
OCollection::findColumn( const OUString
& columnName
)
435 if ( !m_pElements
->exists(columnName
) )
437 ::dbtools::throwInvalidColumnException( columnName
, static_cast< XIndexAccess
*>(this) );
441 return m_pElements
->findColumn(columnName
) + 1; // because columns start at one
444 Reference
< XEnumeration
> SAL_CALL
OCollection::createEnumeration( )
446 ::osl::MutexGuard
aGuard(m_rMutex
);
447 return new OEnumerationByIndex( static_cast< XIndexAccess
*>(this));
450 void SAL_CALL
OCollection::addContainerListener( const Reference
< XContainerListener
>& _rxListener
)
452 m_aContainerListeners
.addInterface(_rxListener
);
456 void SAL_CALL
OCollection::removeContainerListener( const Reference
< XContainerListener
>& _rxListener
)
458 m_aContainerListeners
.removeInterface(_rxListener
);
461 void SAL_CALL
OCollection::acquire() throw()
466 void SAL_CALL
OCollection::release() throw()
471 Type SAL_CALL
OCollection::getElementType( )
473 return cppu::UnoType
<XPropertySet
>::get();
476 sal_Bool SAL_CALL
OCollection::hasElements( )
478 ::osl::MutexGuard
aGuard(m_rMutex
);
479 return !m_pElements
->empty();
482 sal_Int32 SAL_CALL
OCollection::getCount( )
484 ::osl::MutexGuard
aGuard(m_rMutex
);
485 return m_pElements
->size();
488 sal_Bool SAL_CALL
OCollection::hasByName( const OUString
& aName
)
490 ::osl::MutexGuard
aGuard(m_rMutex
);
491 return m_pElements
->exists(aName
);
494 void SAL_CALL
OCollection::addRefreshListener( const Reference
< XRefreshListener
>& l
)
496 m_aRefreshListeners
.addInterface(l
);
499 void SAL_CALL
OCollection::removeRefreshListener( const Reference
< XRefreshListener
>& l
)
501 m_aRefreshListeners
.removeInterface(l
);
504 void OCollection::insertElement(const OUString
& _sElementName
,const ObjectType
& _xElement
)
506 OSL_ENSURE(!m_pElements
->exists(_sElementName
),"Element already exists");
507 if ( !m_pElements
->exists(_sElementName
) )
508 m_pElements
->insert(_sElementName
,_xElement
);
511 void OCollection::renameObject(const OUString
& _sOldName
, const OUString
& _sNewName
)
513 OSL_ENSURE(m_pElements
->exists(_sOldName
),"Element doesn't exist");
514 OSL_ENSURE(!m_pElements
->exists(_sNewName
),"Element already exists");
515 OSL_ENSURE(!_sNewName
.isEmpty(),"New name must not be empty!");
516 OSL_ENSURE(!_sOldName
.isEmpty(),"Old name must not be empty!");
518 if ( m_pElements
->rename(_sOldName
,_sNewName
) )
520 ContainerEvent
aEvent(static_cast<XContainer
*>(this), makeAny(_sNewName
), makeAny(m_pElements
->getObject(_sNewName
)),makeAny(_sOldName
));
521 // note that xExistent may be empty, in case somebody removed the data source while it is not alive at this moment
522 OInterfaceIteratorHelper2
aListenerLoop(m_aContainerListeners
);
523 while (aListenerLoop
.hasMoreElements())
524 static_cast<XContainerListener
*>(aListenerLoop
.next())->elementReplaced(aEvent
);
528 ObjectType
OCollection::getObject(sal_Int32 _nIndex
)
530 ObjectType xName
= m_pElements
->getObject(_nIndex
);
535 xName
= createObject(m_pElements
->getName(_nIndex
));
537 catch(const SQLException
& e
)
539 css::uno::Any anyEx
= cppu::getCaughtException();
542 dropImpl(_nIndex
,false);
544 catch(const Exception
& )
547 throw WrappedTargetException(e
.Message
,static_cast<XTypeProvider
*>(this),anyEx
);
549 m_pElements
->setObject(_nIndex
,xName
);
554 void OCollection::disposeElements()
556 m_pElements
->disposeElements();
559 Reference
< XPropertySet
> OCollection::createDescriptor()
561 OSL_FAIL("createDescriptor() needs to be overridden when used!");
562 throw SQLException();
565 ObjectType
OCollection::cloneDescriptor( const ObjectType
& _descriptor
)
567 ObjectType
xNewDescriptor( createDescriptor() );
568 ::comphelper::copyProperties( _descriptor
, xNewDescriptor
);
569 return xNewDescriptor
;
572 ObjectType
OCollection::appendObject( const OUString
& /*_rForName*/, const Reference
< XPropertySet
>& descriptor
)
574 return cloneDescriptor( descriptor
);
577 void OCollection::dropObject(sal_Int32
/*_nPos*/, const OUString
& /*_sElementName*/)
582 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */