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