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 "sortdynres.hxx"
21 #include <comphelper/interfacecontainer2.hxx>
22 #include <cppuhelper/supportsservice.hxx>
23 #include <cppuhelper/weak.hxx>
24 #include <com/sun/star/ucb/ContentResultSetCapability.hpp>
25 #include <com/sun/star/ucb/ListActionType.hpp>
26 #include <com/sun/star/ucb/ListenerAlreadySetException.hpp>
27 #include <com/sun/star/ucb/ServiceNotFoundException.hpp>
28 #include <com/sun/star/ucb/WelcomeDynamicResultSetStruct.hpp>
29 #include <com/sun/star/ucb/CachedDynamicResultSetStubFactory.hpp>
30 #include <com/sun/star/ucb/XSourceInitialization.hpp>
31
32 using namespace com::sun::star::beans;
33 using namespace com::sun::star::lang;
34 using namespace com::sun::star::sdbc;
35 using namespace com::sun::star::ucb;
36 using namespace com::sun::star::uno;
37 using namespace comphelper;
38
39
40 // The mutex to synchronize access to containers.
getContainerMutex()41 static osl::Mutex& getContainerMutex()
42 {
43 static osl::Mutex ourMutex;
44
45 return ourMutex;
46 }
47
48
49 // SortedDynamicResultSet
50
SortedDynamicResultSet(const Reference<XDynamicResultSet> & xOriginal,const Sequence<NumberedSortingInfo> & aOptions,const Reference<XAnyCompareFactory> & xCompFac,const Reference<XComponentContext> & rxContext)51 SortedDynamicResultSet::SortedDynamicResultSet(
52 const Reference < XDynamicResultSet > &xOriginal,
53 const Sequence < NumberedSortingInfo > &aOptions,
54 const Reference < XAnyCompareFactory > &xCompFac,
55 const Reference < XComponentContext > &rxContext )
56 {
57 mpDisposeEventListeners = nullptr;
58 mxOwnListener = new SortedDynamicResultSetListener( this );
59
60 mxOriginal = xOriginal;
61 maOptions = aOptions;
62 mxCompFac = xCompFac;
63 m_xContext = rxContext;
64
65 mbGotWelcome = false;
66 mbUseOne = true;
67 mbStatic = false;
68 }
69
70
~SortedDynamicResultSet()71 SortedDynamicResultSet::~SortedDynamicResultSet()
72 {
73 mxOwnListener->impl_OwnerDies();
74 mxOwnListener.clear();
75
76 mpDisposeEventListeners.reset();
77
78 mxOne.clear();
79 mxTwo.clear();
80 mxOriginal.clear();
81 }
82
83 // XServiceInfo methods.
84
getImplementationName()85 OUString SAL_CALL SortedDynamicResultSet::getImplementationName()
86 {
87 return "com.sun.star.comp.ucb.SortedDynamicResultSet";
88 }
89
supportsService(const OUString & ServiceName)90 sal_Bool SAL_CALL SortedDynamicResultSet::supportsService( const OUString& ServiceName )
91 {
92 return cppu::supportsService( this, ServiceName );
93 }
94
getSupportedServiceNames()95 css::uno::Sequence< OUString > SAL_CALL SortedDynamicResultSet::getSupportedServiceNames()
96 {
97 return { "com.sun.star.ucb.SortedDynamicResultSet" };
98 }
99
100 // XComponent methods.
101
dispose()102 void SAL_CALL SortedDynamicResultSet::dispose()
103 {
104 osl::Guard< osl::Mutex > aGuard( maMutex );
105
106 if ( mpDisposeEventListeners && mpDisposeEventListeners->getLength() )
107 {
108 EventObject aEvt;
109 aEvt.Source = static_cast< XComponent * >( this );
110 mpDisposeEventListeners->disposeAndClear( aEvt );
111 }
112
113 mxOne.clear();
114 mxTwo.clear();
115 mxOriginal.clear();
116
117 mbUseOne = true;
118 }
119
addEventListener(const Reference<XEventListener> & Listener)120 void SAL_CALL SortedDynamicResultSet::addEventListener(
121 const Reference< XEventListener >& Listener )
122 {
123 osl::Guard< osl::Mutex > aGuard( maMutex );
124
125 if ( !mpDisposeEventListeners )
126 mpDisposeEventListeners.reset(
127 new OInterfaceContainerHelper2( getContainerMutex() ) );
128
129 mpDisposeEventListeners->addInterface( Listener );
130 }
131
removeEventListener(const Reference<XEventListener> & Listener)132 void SAL_CALL SortedDynamicResultSet::removeEventListener(
133 const Reference< XEventListener >& Listener )
134 {
135 osl::Guard< osl::Mutex > aGuard( maMutex );
136
137 if ( mpDisposeEventListeners )
138 mpDisposeEventListeners->removeInterface( Listener );
139 }
140
141
142 // XDynamicResultSet methods.
143
144 Reference< XResultSet > SAL_CALL
getStaticResultSet()145 SortedDynamicResultSet::getStaticResultSet()
146 {
147 osl::Guard< osl::Mutex > aGuard( maMutex );
148
149 if ( mxListener.is() )
150 throw ListenerAlreadySetException();
151
152 mbStatic = true;
153
154 if ( mxOriginal.is() )
155 {
156 mxOne = new SortedResultSet( mxOriginal->getStaticResultSet() );
157 mxOne->Initialize( maOptions, mxCompFac );
158 }
159
160 return mxOne;
161 }
162
163
164 void SAL_CALL
setListener(const Reference<XDynamicResultSetListener> & Listener)165 SortedDynamicResultSet::setListener( const Reference< XDynamicResultSetListener >& Listener )
166 {
167 osl::Guard< osl::Mutex > aGuard( maMutex );
168
169 if ( mxListener.is() )
170 throw ListenerAlreadySetException();
171
172 addEventListener( Listener );
173
174 mxListener = Listener;
175
176 if ( mxOriginal.is() )
177 mxOriginal->setListener( mxOwnListener );
178 }
179
180
181 void SAL_CALL
connectToCache(const Reference<XDynamicResultSet> & xCache)182 SortedDynamicResultSet::connectToCache( const Reference< XDynamicResultSet > & xCache )
183 {
184 if( mxListener.is() )
185 throw ListenerAlreadySetException();
186
187 if( mbStatic )
188 throw ListenerAlreadySetException();
189
190 Reference< XSourceInitialization > xTarget( xCache, UNO_QUERY );
191 if( xTarget.is() && m_xContext.is() )
192 {
193 Reference< XCachedDynamicResultSetStubFactory > xStubFactory;
194 try
195 {
196 xStubFactory = CachedDynamicResultSetStubFactory::create( m_xContext );
197 }
198 catch ( Exception const & )
199 {
200 }
201
202 if( xStubFactory.is() )
203 {
204 xStubFactory->connectToCache(
205 this, xCache, Sequence< NumberedSortingInfo > (), nullptr );
206 return;
207 }
208 }
209 throw ServiceNotFoundException();
210 }
211
212
getCapabilities()213 sal_Int16 SAL_CALL SortedDynamicResultSet::getCapabilities()
214 {
215 osl::Guard< osl::Mutex > aGuard( maMutex );
216
217 sal_Int16 nCaps = 0;
218
219 if ( mxOriginal.is() )
220 nCaps = mxOriginal->getCapabilities();
221
222 nCaps |= ContentResultSetCapability::SORTED;
223
224 return nCaps;
225 }
226
227
228 // XDynamicResultSetListener methods.
229
230
231 /** In the first notify-call the listener gets the two
232 <type>XResultSet</type>s and has to hold them. The <type>XResultSet</type>s
233 are implementations of the service <type>ContentResultSet</type>.
234
235 <p>The notified new <type>XResultSet</type> will stay valid after returning
236 notification. The old one will become invalid after returning notification.
237
238 <p>While in notify-call the listener is allowed to read old and new version,
239 except in the first call, where only the new Resultset is valid.
240
241 <p>The Listener is allowed to blockade this call, until he really want to go
242 to the new version. The only situation, where the listener has to return the
243 update call at once is, while he disposes his broadcaster or while he is
244 removing himself as listener (otherwise you deadlock)!!!
245 */
impl_notify(const ListEvent & Changes)246 void SortedDynamicResultSet::impl_notify( const ListEvent& Changes )
247 {
248 osl::Guard< osl::Mutex > aGuard( maMutex );
249
250 bool bHasNew = false;
251 bool bHasModified = false;
252
253 SortedResultSet *pCurSet = nullptr;
254
255 // exchange mxNew and mxOld and immediately afterwards copy the tables
256 // from Old to New
257 if ( mbGotWelcome )
258 {
259 if ( mbUseOne )
260 {
261 mbUseOne = false;
262 mxTwo->CopyData( mxOne.get() );
263 pCurSet = mxTwo.get();
264 }
265 else
266 {
267 mbUseOne = true;
268 mxOne->CopyData( mxTwo.get() );
269 pCurSet = mxOne.get();
270 }
271 }
272
273 if (!pCurSet)
274 return;
275
276 Any aRet;
277
278 try {
279 aRet = pCurSet->getPropertyValue("IsRowCountFinal");
280 }
281 catch (const UnknownPropertyException&) {}
282 catch (const WrappedTargetException&) {}
283
284 sal_Int32 nOldCount = pCurSet->GetCount();
285 bool bWasFinal = false;
286
287 aRet >>= bWasFinal;
288
289 // handle the actions in the list
290 for ( const ListAction& aAction : Changes.Changes )
291 {
292 switch ( aAction.ListActionType )
293 {
294 case ListActionType::WELCOME:
295 {
296 WelcomeDynamicResultSetStruct aWelcome;
297 if ( aAction.ActionInfo >>= aWelcome )
298 {
299 mxTwo = new SortedResultSet( aWelcome.Old );
300 mxOne = new SortedResultSet( aWelcome.New );
301 mxOne->Initialize( maOptions, mxCompFac );
302 mbGotWelcome = true;
303 mbUseOne = true;
304 pCurSet = mxOne.get();
305
306 aWelcome.Old = mxTwo.get();
307 aWelcome.New = mxOne.get();
308
309 ListAction aWelcomeAction;
310 aWelcomeAction.ActionInfo <<= aWelcome;
311 aWelcomeAction.Position = 0;
312 aWelcomeAction.Count = 0;
313 aWelcomeAction.ListActionType = ListActionType::WELCOME;
314
315 maActions.Insert( aWelcomeAction );
316 }
317 else
318 {
319 // throw RuntimeException();
320 }
321 break;
322 }
323 case ListActionType::INSERTED:
324 {
325 pCurSet->InsertNew( aAction.Position, aAction.Count );
326 bHasNew = true;
327 break;
328 }
329 case ListActionType::REMOVED:
330 {
331 pCurSet->Remove( aAction.Position,
332 aAction.Count,
333 &maActions );
334 break;
335 }
336 case ListActionType::MOVED:
337 {
338 sal_Int32 nOffset = 0;
339 if ( aAction.ActionInfo >>= nOffset )
340 {
341 pCurSet->Move( aAction.Position,
342 aAction.Count,
343 nOffset );
344 }
345 break;
346 }
347 case ListActionType::PROPERTIES_CHANGED:
348 {
349 pCurSet->SetChanged( aAction.Position, aAction.Count );
350 bHasModified = true;
351 break;
352 }
353 default: break;
354 }
355 }
356
357 if ( bHasModified )
358 pCurSet->ResortModified( &maActions );
359
360 if ( bHasNew )
361 pCurSet->ResortNew( &maActions );
362
363 // send the new actions with a notify to the listeners
364 SendNotify();
365
366 // check for propertyChangeEvents
367 pCurSet->CheckProperties( nOldCount, bWasFinal );
368 }
369
370 // XEventListener
371
impl_disposing()372 void SortedDynamicResultSet::impl_disposing()
373 {
374 mxListener.clear();
375 mxOriginal.clear();
376 }
377
378 // private methods
379
SendNotify()380 void SortedDynamicResultSet::SendNotify()
381 {
382 sal_Int32 nCount = maActions.Count();
383
384 if ( nCount && mxListener.is() )
385 {
386 Sequence< ListAction > aActionList( maActions.Count() );
387 ListAction *pActionList = aActionList.getArray();
388
389 for ( sal_Int32 i=0; i<nCount; i++ )
390 {
391 pActionList[ i ] = maActions.GetAction( i );
392 }
393
394 ListEvent aNewEvent;
395 aNewEvent.Changes = aActionList;
396
397 mxListener->notify( aNewEvent );
398 }
399
400 // clean up
401 maActions.Clear();
402 }
403
404 // SortedDynamicResultSetFactory
405
SortedDynamicResultSetFactory(const Reference<XComponentContext> & rxContext)406 SortedDynamicResultSetFactory::SortedDynamicResultSetFactory(
407 const Reference< XComponentContext > & rxContext )
408 {
409 m_xContext = rxContext;
410 }
411
412
~SortedDynamicResultSetFactory()413 SortedDynamicResultSetFactory::~SortedDynamicResultSetFactory()
414 {
415 }
416
417
418 // XServiceInfo methods.
419
getImplementationName()420 OUString SAL_CALL SortedDynamicResultSetFactory::getImplementationName()
421 {
422 return "com.sun.star.comp.ucb.SortedDynamicResultSetFactory";
423 }
424
supportsService(const OUString & ServiceName)425 sal_Bool SAL_CALL SortedDynamicResultSetFactory::supportsService( const OUString& ServiceName )
426 {
427 return cppu::supportsService( this, ServiceName );
428 }
429
getSupportedServiceNames()430 css::uno::Sequence< OUString > SAL_CALL SortedDynamicResultSetFactory::getSupportedServiceNames()
431 {
432 return { "com.sun.star.ucb.SortedDynamicResultSetFactory" };
433 }
434
435
436 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
ucb_SortedDynamicResultSetFactory_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)437 ucb_SortedDynamicResultSetFactory_get_implementation(
438 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
439 {
440 return cppu::acquire(new SortedDynamicResultSetFactory(context));
441 }
442
443 // SortedDynamicResultSetFactory methods.
444
445 Reference< XDynamicResultSet > SAL_CALL
createSortedDynamicResultSet(const Reference<XDynamicResultSet> & Source,const Sequence<NumberedSortingInfo> & Info,const Reference<XAnyCompareFactory> & CompareFactory)446 SortedDynamicResultSetFactory::createSortedDynamicResultSet(
447 const Reference< XDynamicResultSet > & Source,
448 const Sequence< NumberedSortingInfo > & Info,
449 const Reference< XAnyCompareFactory > & CompareFactory )
450 {
451 Reference< XDynamicResultSet > xRet = new SortedDynamicResultSet( Source, Info, CompareFactory, m_xContext );
452 return xRet;
453 }
454
455 // EventList
456
Clear()457 void EventList::Clear()
458 {
459 maData.clear();
460 }
461
AddEvent(sal_IntPtr nType,sal_Int32 nPos)462 void EventList::AddEvent( sal_IntPtr nType, sal_Int32 nPos )
463 {
464 ListAction aAction;
465 aAction.Position = nPos;
466 aAction.Count = 1;
467 aAction.ListActionType = nType;
468
469 Insert( aAction );
470 }
471
472 // SortedDynamicResultSetListener
473
SortedDynamicResultSetListener(SortedDynamicResultSet * mOwner)474 SortedDynamicResultSetListener::SortedDynamicResultSetListener(
475 SortedDynamicResultSet *mOwner )
476 {
477 mpOwner = mOwner;
478 }
479
480
~SortedDynamicResultSetListener()481 SortedDynamicResultSetListener::~SortedDynamicResultSetListener()
482 {
483 }
484
485 // XEventListener ( base of XDynamicResultSetListener )
486
487 void SAL_CALL
disposing(const EventObject &)488 SortedDynamicResultSetListener::disposing( const EventObject& /*Source*/ )
489 {
490 osl::Guard< osl::Mutex > aGuard( maMutex );
491
492 if ( mpOwner )
493 mpOwner->impl_disposing();
494 }
495
496
497 // XDynamicResultSetListener
498
499 void SAL_CALL
notify(const ListEvent & Changes)500 SortedDynamicResultSetListener::notify( const ListEvent& Changes )
501 {
502 osl::Guard< osl::Mutex > aGuard( maMutex );
503
504 if ( mpOwner )
505 mpOwner->impl_notify( Changes );
506 }
507
508 // own methods:
509
510 void
impl_OwnerDies()511 SortedDynamicResultSetListener::impl_OwnerDies()
512 {
513 osl::Guard< osl::Mutex > aGuard( maMutex );
514 mpOwner = nullptr;
515 }
516
517 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
518