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 <framework/ConfigurationController.hxx>
21 #include <framework/Configuration.hxx>
22 #include <framework/FrameworkHelper.hxx>
23 #include "ConfigurationUpdater.hxx"
24 #include "ConfigurationControllerBroadcaster.hxx"
25 #include "ConfigurationTracer.hxx"
26 #include "GenericConfigurationChangeRequest.hxx"
27 #include "ConfigurationControllerResourceManager.hxx"
28 #include "ResourceFactoryManager.hxx"
29 #include "UpdateRequest.hxx"
30 #include "ChangeRequestQueueProcessor.hxx"
31 #include "ConfigurationClassifier.hxx"
32 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
33 #include <com/sun/star/frame/XController.hpp>
34 
35 #include <sal/log.hxx>
36 #include <osl/mutex.hxx>
37 #include <vcl/svapp.hxx>
38 #include <memory>
39 
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::uno;
42 using namespace ::com::sun::star::drawing::framework;
43 using ::sd::framework::FrameworkHelper;
44 
45 namespace sd { namespace framework {
46 
47 //----- ConfigurationController::Implementation -------------------------------
48 
49 class ConfigurationController::Implementation
50 {
51 public:
52     Implementation (
53         ConfigurationController& rController,
54         const Reference<frame::XController>& rxController);
55 
56     Reference<XControllerManager> mxControllerManager;
57 
58     /** The Broadcaster class implements storing and calling of listeners.
59     */
60     std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
61 
62     /** The requested configuration which is modified (asynchronously) by
63         calls to requestResourceActivation() and
64         requestResourceDeactivation().  The mpConfigurationUpdater makes the
65         current configuration reflect the content of this one.
66     */
67     css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration;
68 
69     std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer;
70 
71     std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager;
72 
73     std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater;
74 
75     /** The queue processor owns the queue of configuration change request
76         objects and processes the objects.
77     */
78     std::unique_ptr<ChangeRequestQueueProcessor> mpQueueProcessor;
79 
80     std::shared_ptr<ConfigurationUpdaterLock> mpConfigurationUpdaterLock;
81 
82     sal_Int32 mnLockCount;
83 };
84 
85 //===== ConfigurationController::Lock =========================================
86 
Lock(const Reference<XConfigurationController> & rxController)87 ConfigurationController::Lock::Lock (const Reference<XConfigurationController>& rxController)
88     : mxController(rxController)
89 {
90     OSL_ASSERT(mxController.is());
91 
92     if (mxController.is())
93         mxController->lock();
94 }
95 
~Lock()96 ConfigurationController::Lock::~Lock()
97 {
98     if (mxController.is())
99         mxController->unlock();
100 }
101 
102 //===== ConfigurationController ===============================================
103 
ConfigurationController()104 ConfigurationController::ConfigurationController() throw()
105     : ConfigurationControllerInterfaceBase(MutexOwner::maMutex)
106     , mpImplementation()
107     , mbIsDisposed(false)
108 {
109 }
110 
~ConfigurationController()111 ConfigurationController::~ConfigurationController() throw()
112 {
113 }
114 
disposing()115 void SAL_CALL ConfigurationController::disposing()
116 {
117     if (mpImplementation == nullptr)
118         return;
119 
120     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::disposing");
121     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ":     requesting empty configuration");
122     // To destroy all resources an empty configuration is requested and then,
123     // synchronously, all resulting requests are processed.
124     mpImplementation->mpQueueProcessor->Clear();
125     restoreConfiguration(new Configuration(this,false));
126     mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
127     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ":     all requests processed");
128 
129     // Now that all resources have been deactivated, mark the controller as
130     // disposed.
131     mbIsDisposed = true;
132 
133     // Release the listeners.
134     lang::EventObject aEvent;
135     aEvent.Source = uno::Reference<uno::XInterface>(static_cast<cppu::OWeakObject*>(this));
136 
137     {
138         const SolarMutexGuard aSolarGuard;
139         mpImplementation->mpBroadcaster->DisposeAndClear();
140     }
141 
142     mpImplementation->mpQueueProcessor.reset();
143     mpImplementation->mxRequestedConfiguration = nullptr;
144     mpImplementation.reset();
145 }
146 
ProcessEvent()147 void ConfigurationController::ProcessEvent()
148 {
149     if (mpImplementation != nullptr)
150     {
151         OSL_ASSERT(mpImplementation->mpQueueProcessor != nullptr);
152 
153         mpImplementation->mpQueueProcessor->ProcessOneEvent();
154     }
155 }
156 
RequestSynchronousUpdate()157 void ConfigurationController::RequestSynchronousUpdate()
158 {
159     if (mpImplementation == nullptr)
160         return;
161     if (mpImplementation->mpQueueProcessor == nullptr)
162         return;
163     mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
164 }
165 
166 //----- XConfigurationControllerBroadcaster -----------------------------------
167 
addConfigurationChangeListener(const Reference<XConfigurationChangeListener> & rxListener,const OUString & rsEventType,const Any & rUserData)168 void SAL_CALL ConfigurationController::addConfigurationChangeListener (
169     const Reference<XConfigurationChangeListener>& rxListener,
170     const OUString& rsEventType,
171     const Any& rUserData)
172 {
173     ::osl::MutexGuard aGuard (maMutex);
174 
175     ThrowIfDisposed();
176     OSL_ASSERT(mpImplementation != nullptr);
177     mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData);
178 }
179 
removeConfigurationChangeListener(const Reference<XConfigurationChangeListener> & rxListener)180 void SAL_CALL ConfigurationController::removeConfigurationChangeListener (
181     const Reference<XConfigurationChangeListener>& rxListener)
182 {
183     ::osl::MutexGuard aGuard (maMutex);
184 
185     ThrowIfDisposed();
186     mpImplementation->mpBroadcaster->RemoveListener(rxListener);
187 }
188 
notifyEvent(const ConfigurationChangeEvent & rEvent)189 void SAL_CALL ConfigurationController::notifyEvent (
190     const ConfigurationChangeEvent& rEvent)
191 {
192     ThrowIfDisposed();
193     mpImplementation->mpBroadcaster->NotifyListeners(rEvent);
194 }
195 
196 //----- XConfigurationController ----------------------------------------------
197 
lock()198 void SAL_CALL ConfigurationController::lock()
199 {
200     OSL_ASSERT(mpImplementation != nullptr);
201     OSL_ASSERT(mpImplementation->mpConfigurationUpdater != nullptr);
202 
203     ::osl::MutexGuard aGuard (maMutex);
204     ThrowIfDisposed();
205 
206     ++mpImplementation->mnLockCount;
207     if (mpImplementation->mpConfigurationUpdaterLock == nullptr)
208         mpImplementation->mpConfigurationUpdaterLock
209             = mpImplementation->mpConfigurationUpdater->GetLock();
210 }
211 
unlock()212 void SAL_CALL ConfigurationController::unlock()
213 {
214     ::osl::MutexGuard aGuard (maMutex);
215 
216     // Allow unlocking while the ConfigurationController is being disposed
217     // (but not when that is done and the controller is disposed.)
218     if (rBHelper.bDisposed)
219         ThrowIfDisposed();
220 
221     OSL_ASSERT(mpImplementation->mnLockCount>0);
222     --mpImplementation->mnLockCount;
223     if (mpImplementation->mnLockCount == 0)
224         mpImplementation->mpConfigurationUpdaterLock.reset();
225 }
226 
requestResourceActivation(const Reference<XResourceId> & rxResourceId,ResourceActivationMode eMode)227 void SAL_CALL ConfigurationController::requestResourceActivation (
228     const Reference<XResourceId>& rxResourceId,
229     ResourceActivationMode eMode)
230 {
231     ::osl::MutexGuard aGuard (maMutex);
232     ThrowIfDisposed();
233 
234     // Check whether we are being disposed.  This is handled differently
235     // then being completely disposed because the first thing disposing()
236     // does is to deactivate all remaining resources.  This is done via
237     // regular methods which must not throw DisposedExceptions.  Therefore
238     // we just return silently during that stage.
239     if (rBHelper.bInDispose)
240     {
241         SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceActivation(): ignoring " <<
242                 FrameworkHelper::ResourceIdToString(rxResourceId));
243         return;
244     }
245 
246     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceActivation() " <<
247             FrameworkHelper::ResourceIdToString(rxResourceId));
248 
249     if (!rxResourceId.is())
250         return;
251 
252     if (eMode == ResourceActivationMode_REPLACE)
253     {
254         // Get a list of the matching resources and create deactivation
255         // requests for them.
256         const Sequence<Reference<XResourceId> > aResourceList (
257             mpImplementation->mxRequestedConfiguration->getResources(
258                 rxResourceId->getAnchor(),
259                 rxResourceId->getResourceTypePrefix(),
260                 AnchorBindingMode_DIRECT));
261 
262         for (const auto& rResource : aResourceList)
263         {
264             // Do not request the deactivation of the resource for which
265             // this method was called.  Doing it would not change the
266             // outcome but would result in unnecessary work.
267             if (rxResourceId->compareTo(rResource) == 0)
268                 continue;
269 
270             // Request the deactivation of a resource and all resources
271             // linked to it.
272             requestResourceDeactivation(rResource);
273         }
274     }
275 
276     Reference<XConfigurationChangeRequest> xRequest(
277         new GenericConfigurationChangeRequest(
278             rxResourceId,
279             GenericConfigurationChangeRequest::Activation));
280     postChangeRequest(xRequest);
281 }
282 
requestResourceDeactivation(const Reference<XResourceId> & rxResourceId)283 void SAL_CALL ConfigurationController::requestResourceDeactivation (
284     const Reference<XResourceId>& rxResourceId)
285 {
286     ::osl::MutexGuard aGuard (maMutex);
287     ThrowIfDisposed();
288 
289     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceDeactivation() " <<
290                 FrameworkHelper::ResourceIdToString(rxResourceId));
291 
292     if (!rxResourceId.is())
293         return;
294 
295     // Request deactivation of all resources linked to the specified one
296     // as well.
297     const Sequence<Reference<XResourceId> > aLinkedResources (
298         mpImplementation->mxRequestedConfiguration->getResources(
299             rxResourceId,
300             OUString(),
301             AnchorBindingMode_DIRECT));
302     for (const auto& rLinkedResource : aLinkedResources)
303     {
304         // We do not add deactivation requests directly but call this
305         // method recursively, so that when one time there are resources
306         // linked to linked resources, these are handled correctly, too.
307         requestResourceDeactivation(rLinkedResource);
308     }
309 
310     // Add a deactivation request for the specified resource.
311     Reference<XConfigurationChangeRequest> xRequest(
312         new GenericConfigurationChangeRequest(
313             rxResourceId,
314             GenericConfigurationChangeRequest::Deactivation));
315     postChangeRequest(xRequest);
316 }
317 
getResource(const Reference<XResourceId> & rxResourceId)318 Reference<XResource> SAL_CALL ConfigurationController::getResource (
319     const Reference<XResourceId>& rxResourceId)
320 {
321     ::osl::MutexGuard aGuard (maMutex);
322     ThrowIfDisposed();
323 
324     ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor (
325         mpImplementation->mpResourceManager->GetResource(rxResourceId));
326     return aDescriptor.mxResource;
327 }
328 
update()329 void SAL_CALL ConfigurationController::update()
330 {
331     ::osl::MutexGuard aGuard (maMutex);
332     ThrowIfDisposed();
333 
334     if (mpImplementation->mpQueueProcessor->IsEmpty())
335     {
336         // The queue is empty.  Add another request that does nothing but
337         // asynchronously trigger a request for an update.
338         mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest());
339     }
340     else
341     {
342         // The queue is not empty, so we rely on the queue processor to
343         // request an update automatically when the queue becomes empty.
344     }
345 }
346 
hasPendingRequests()347 sal_Bool SAL_CALL ConfigurationController::hasPendingRequests()
348 {
349     ::osl::MutexGuard aGuard (maMutex);
350     ThrowIfDisposed();
351 
352     return ! mpImplementation->mpQueueProcessor->IsEmpty();
353 }
354 
postChangeRequest(const Reference<XConfigurationChangeRequest> & rxRequest)355 void SAL_CALL ConfigurationController::postChangeRequest (
356     const Reference<XConfigurationChangeRequest>& rxRequest)
357 {
358     ::osl::MutexGuard aGuard (maMutex);
359     ThrowIfDisposed();
360 
361     mpImplementation->mpQueueProcessor->AddRequest(rxRequest);
362 }
363 
getRequestedConfiguration()364 Reference<XConfiguration> SAL_CALL ConfigurationController::getRequestedConfiguration()
365 {
366     ::osl::MutexGuard aGuard (maMutex);
367     ThrowIfDisposed();
368 
369     if (mpImplementation->mxRequestedConfiguration.is())
370         return Reference<XConfiguration>(
371             mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY);
372     else
373         return Reference<XConfiguration>();
374 }
375 
getCurrentConfiguration()376 Reference<XConfiguration> SAL_CALL ConfigurationController::getCurrentConfiguration()
377 {
378     ::osl::MutexGuard aGuard (maMutex);
379     ThrowIfDisposed();
380 
381     Reference<XConfiguration> xCurrentConfiguration(
382         mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration());
383     if (xCurrentConfiguration.is())
384         return Reference<XConfiguration>(xCurrentConfiguration->createClone(), UNO_QUERY);
385     else
386         return Reference<XConfiguration>();
387 }
388 
389 /** The given configuration is restored by generating the appropriate set of
390     activation and deactivation requests.
391 */
restoreConfiguration(const Reference<XConfiguration> & rxNewConfiguration)392 void SAL_CALL ConfigurationController::restoreConfiguration (
393     const Reference<XConfiguration>& rxNewConfiguration)
394 {
395     ::osl::MutexGuard aGuard (maMutex);
396     ThrowIfDisposed();
397 
398     // We will probably be making a couple of activation and deactivation
399     // requests so lock the configuration controller and let it later update
400     // all changes at once.
401     std::shared_ptr<ConfigurationUpdaterLock> pLock (
402         mpImplementation->mpConfigurationUpdater->GetLock());
403 
404     // Get lists of resources that are to be activated or deactivated.
405     Reference<XConfiguration> xCurrentConfiguration (mpImplementation->mxRequestedConfiguration);
406 #if OSL_DEBUG_LEVEL >=1
407     SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::restoreConfiguration(");
408     ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration");
409     ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration");
410 #endif
411     ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration);
412     aClassifier.Partition();
413 #if DEBUG_SD_CONFIGURATION_TRACE
414     aClassifier.TraceResourceIdVector(
415         "requested but not current resources:\n", aClassifier.GetC1minusC2());
416     aClassifier.TraceResourceIdVector(
417         "current but not requested resources:\n", aClassifier.GetC2minusC1());
418     aClassifier.TraceResourceIdVector(
419         "requested and current resources:\n", aClassifier.GetC1andC2());
420 #endif
421 
422     // Request the deactivation of resources that are not requested in the
423     // new configuration.
424     const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate (
425         aClassifier.GetC2minusC1());
426     for (const auto& rxResource : rResourcesToDeactivate)
427     {
428         requestResourceDeactivation(rxResource);
429     }
430 
431     // Request the activation of resources that are requested in the
432     // new configuration but are not part of the current configuration.
433     const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate (
434         aClassifier.GetC1minusC2());
435     for (const auto& rxResource : rResourcesToActivate)
436     {
437         requestResourceActivation(rxResource, ResourceActivationMode_ADD);
438     }
439 
440     pLock.reset();
441 }
442 
443 //----- XResourceFactoryManager -----------------------------------------------
444 
addResourceFactory(const OUString & sResourceURL,const Reference<XResourceFactory> & rxResourceFactory)445 void SAL_CALL ConfigurationController::addResourceFactory(
446     const OUString& sResourceURL,
447     const Reference<XResourceFactory>& rxResourceFactory)
448 {
449     ::osl::MutexGuard aGuard (maMutex);
450     ThrowIfDisposed();
451     mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory);
452 }
453 
removeResourceFactoryForURL(const OUString & sResourceURL)454 void SAL_CALL ConfigurationController::removeResourceFactoryForURL(
455     const OUString& sResourceURL)
456 {
457     ::osl::MutexGuard aGuard (maMutex);
458     ThrowIfDisposed();
459     mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL);
460 }
461 
removeResourceFactoryForReference(const Reference<XResourceFactory> & rxResourceFactory)462 void SAL_CALL ConfigurationController::removeResourceFactoryForReference(
463     const Reference<XResourceFactory>& rxResourceFactory)
464 {
465     ::osl::MutexGuard aGuard (maMutex);
466     ThrowIfDisposed();
467     mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory);
468 }
469 
getResourceFactory(const OUString & sResourceURL)470 Reference<XResourceFactory> SAL_CALL ConfigurationController::getResourceFactory (
471     const OUString& sResourceURL)
472 {
473     ::osl::MutexGuard aGuard (maMutex);
474     ThrowIfDisposed();
475 
476     return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL);
477 }
478 
479 //----- XInitialization -------------------------------------------------------
480 
initialize(const Sequence<Any> & aArguments)481 void SAL_CALL ConfigurationController::initialize (const Sequence<Any>& aArguments)
482 {
483     ::osl::MutexGuard aGuard (maMutex);
484 
485     if (aArguments.getLength() == 1)
486     {
487         const SolarMutexGuard aSolarGuard;
488 
489         mpImplementation.reset(new Implementation(
490             *this,
491             Reference<frame::XController>(aArguments[0], UNO_QUERY_THROW)));
492     }
493 }
494 
ThrowIfDisposed() const495 void ConfigurationController::ThrowIfDisposed () const
496 {
497     if (mbIsDisposed)
498     {
499         throw lang::DisposedException ("ConfigurationController object has already been disposed",
500             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
501     }
502 
503     if (mpImplementation == nullptr)
504     {
505         OSL_ASSERT(mpImplementation != nullptr);
506         throw RuntimeException("ConfigurationController not initialized",
507             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
508     }
509 }
510 
511 //===== ConfigurationController::Implementation ===============================
512 
Implementation(ConfigurationController & rController,const Reference<frame::XController> & rxController)513 ConfigurationController::Implementation::Implementation (
514     ConfigurationController& rController,
515     const Reference<frame::XController>& rxController)
516     : mxControllerManager(rxController, UNO_QUERY_THROW),
517       mpBroadcaster(new ConfigurationControllerBroadcaster(&rController)),
518       mxRequestedConfiguration(new Configuration(&rController, true)),
519       mpResourceFactoryContainer(new ResourceFactoryManager(mxControllerManager)),
520       mpResourceManager(
521           new ConfigurationControllerResourceManager(mpResourceFactoryContainer,mpBroadcaster)),
522       mpConfigurationUpdater(
523           new ConfigurationUpdater(mpBroadcaster, mpResourceManager,mxControllerManager)),
524       mpQueueProcessor(new ChangeRequestQueueProcessor(mpConfigurationUpdater)),
525       mpConfigurationUpdaterLock(),
526       mnLockCount(0)
527 {
528     mpQueueProcessor->SetConfiguration(mxRequestedConfiguration);
529 }
530 
531 } } // end of namespace sd::framework
532 
533 
534 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)535 com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation(
536         css::uno::XComponentContext*,
537         css::uno::Sequence<css::uno::Any> const &)
538 {
539     return cppu::acquire(new sd::framework::ConfigurationController());
540 }
541 
542 
543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
544