1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_sd.hxx"
25 
26 #include "framework/ConfigurationController.hxx"
27 
28 #include "framework/Configuration.hxx"
29 #include "framework/FrameworkHelper.hxx"
30 #include "ConfigurationUpdater.hxx"
31 #include "ConfigurationControllerBroadcaster.hxx"
32 #include "ConfigurationTracer.hxx"
33 #include "GenericConfigurationChangeRequest.hxx"
34 #include "ResourceFactoryManager.hxx"
35 #include "UpdateRequest.hxx"
36 #include "ChangeRequestQueueProcessor.hxx"
37 #include "ConfigurationClassifier.hxx"
38 #include "ViewShellBase.hxx"
39 #include "UpdateLockManager.hxx"
40 #include "DrawController.hxx"
41 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
42 #include <com/sun/star/util/XURLTransformer.hpp>
43 
44 #include <comphelper/stl_types.hxx>
45 #include <vos/mutex.hxx>
46 #include <vcl/svapp.hxx>
47 
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::drawing::framework;
51 using rtl::OUString;
52 using ::sd::framework::FrameworkHelper;
53 
54 #undef VERBOSE
55 //#define VERBOSE 3
56 
57 
58 namespace sd { namespace framework {
59 
60 Reference<XInterface> SAL_CALL ConfigurationController_createInstance (
61     const Reference<XComponentContext>& rxContext)
62 {
63     (void)rxContext;
64     return static_cast<XWeak*>(new ConfigurationController());
65 }
66 
67 
68 
69 
70 OUString ConfigurationController_getImplementationName (void) throw(RuntimeException)
71 {
72     return OUString(RTL_CONSTASCII_USTRINGPARAM(
73         "com.sun.star.comp.Draw.framework.configuration.ConfigurationController"));
74 }
75 
76 
77 
78 
79 Sequence<rtl::OUString> SAL_CALL ConfigurationController_getSupportedServiceNames (void)
80     throw (RuntimeException)
81 {
82 	static const OUString sServiceName(OUString::createFromAscii(
83         "com.sun.star.drawing.framework.ConfigurationController"));
84 	return Sequence<rtl::OUString>(&sServiceName, 1);
85 }
86 
87 
88 
89 
90 //----- ConfigurationController::Implementation -------------------------------
91 
92 class ConfigurationController::Implementation
93 {
94 public:
95     Implementation (
96         ConfigurationController& rController,
97         const Reference<frame::XController>& rxController);
98     ~Implementation (void);
99 
100     Reference<XControllerManager> mxControllerManager;
101 
102     /** The Broadcaster class implements storing and calling of listeners.
103     */
104     ::boost::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
105 
106     /** The requested configuration which is modifed (asynchronously) by
107         calls to requestResourceActivation() and
108         requestResourceDeactivation().  The mpConfigurationUpdater makes the
109         current configuration reflect the content of this one.
110     */
111     ::com::sun::star::uno::Reference<
112         ::com::sun::star::drawing::framework::XConfiguration> mxRequestedConfiguration;
113 
114     ViewShellBase* mpBase;
115 
116     ::boost::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer;
117 
118     ::boost::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager;
119 
120     ::boost::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater;
121 
122     /** The queue processor ownes the queue of configuration change request
123         objects and processes the objects.
124     */
125     ::boost::scoped_ptr<ChangeRequestQueueProcessor> mpQueueProcessor;
126 
127     ::boost::shared_ptr<ConfigurationUpdaterLock> mpConfigurationUpdaterLock;
128 
129     sal_Int32 mnLockCount;
130 };
131 
132 
133 
134 
135 //===== ConfigurationController::Lock =========================================
136 
137 ConfigurationController::Lock::Lock (const Reference<XConfigurationController>& rxController)
138     : mxController(rxController)
139 {
140     OSL_ASSERT(mxController.is());
141 
142     if (mxController.is())
143         mxController->lock();
144 }
145 
146 
147 
148 
149 ConfigurationController::Lock::~Lock (void)
150 {
151     if (mxController.is())
152         mxController->unlock();
153 }
154 
155 
156 
157 
158 //===== ConfigurationController ===============================================
159 
160 ConfigurationController::ConfigurationController (void) throw()
161     : ConfigurationControllerInterfaceBase(MutexOwner::maMutex),
162       mpImplementation(),
163       mbIsDisposed(false)
164 {
165 }
166 
167 
168 
169 
170 ConfigurationController::~ConfigurationController (void) throw()
171 {
172 }
173 
174 
175 
176 
177 void SAL_CALL ConfigurationController::disposing (void)
178 {
179     if (mpImplementation.get() == NULL)
180         return;
181 
182 #if defined VERBOSE && VERBOSE>=1
183     OSL_TRACE("ConfigurationController::disposing\n");
184     OSL_TRACE("    requesting empty configuration\n");
185 #endif
186     // To destroy all resources an empty configuration is requested and then,
187     // synchronously, all resulting requests are processed.
188     mpImplementation->mpQueueProcessor->Clear();
189     restoreConfiguration(new Configuration(this,false));
190     mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
191 #if defined VERBOSE && VERBOSE>=1
192     OSL_TRACE("    all requests processed\n");
193 #endif
194 
195     // Now that all resources have been deactivated, mark the controller as
196     // disposed.
197     mbIsDisposed = true;
198 
199     // Release the listeners.
200 	lang::EventObject aEvent;
201 	aEvent.Source = uno::Reference<uno::XInterface>((cppu::OWeakObject*)this);
202 
203     {
204         const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
205         mpImplementation->mpBroadcaster->DisposeAndClear();
206     }
207 
208     mpImplementation->mpQueueProcessor.reset();
209     mpImplementation->mxRequestedConfiguration = NULL;
210     mpImplementation.reset();
211 }
212 
213 
214 
215 
216 void ConfigurationController::ProcessEvent (void)
217 {
218     if (mpImplementation.get() != NULL)
219     {
220         OSL_ASSERT(mpImplementation->mpQueueProcessor.get()!=NULL);
221 
222         mpImplementation->mpQueueProcessor->ProcessOneEvent();
223     }
224 }
225 
226 
227 
228 
229 void ConfigurationController::RequestSynchronousUpdate (void)
230 {
231     if (mpImplementation.get() == NULL)
232         return;
233     if (mpImplementation->mpQueueProcessor.get() == 0)
234         return;
235     mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
236 }
237 
238 
239 
240 
241 //----- XConfigurationControllerBroadcaster -----------------------------------
242 
243 void SAL_CALL ConfigurationController::addConfigurationChangeListener (
244     const Reference<XConfigurationChangeListener>& rxListener,
245     const ::rtl::OUString& rsEventType,
246     const Any& rUserData)
247     throw (RuntimeException)
248 {
249     ::osl::MutexGuard aGuard (maMutex);
250 
251 	ThrowIfDisposed();
252     OSL_ASSERT(mpImplementation.get()!=NULL);
253     mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData);
254 }
255 
256 
257 
258 
259 void SAL_CALL ConfigurationController::removeConfigurationChangeListener (
260     const Reference<XConfigurationChangeListener>& rxListener)
261     throw (RuntimeException)
262 {
263     ::osl::MutexGuard aGuard (maMutex);
264 
265 	ThrowIfDisposed();
266     mpImplementation->mpBroadcaster->RemoveListener(rxListener);
267 }
268 
269 
270 
271 
272 void SAL_CALL ConfigurationController::notifyEvent (
273     const ConfigurationChangeEvent& rEvent)
274     throw (RuntimeException)
275 {
276 	ThrowIfDisposed();
277     mpImplementation->mpBroadcaster->NotifyListeners(rEvent);
278 }
279 
280 
281 
282 
283 
284 //----- XConfigurationController ----------------------------------------------
285 
286 void SAL_CALL ConfigurationController::lock (void)
287     throw (RuntimeException)
288 {
289     OSL_ASSERT(mpImplementation.get()!=NULL);
290     OSL_ASSERT(mpImplementation->mpConfigurationUpdater.get()!=NULL);
291 
292     ::osl::MutexGuard aGuard (maMutex);
293     ThrowIfDisposed();
294 
295 
296     ++mpImplementation->mnLockCount;
297     if (mpImplementation->mpConfigurationUpdaterLock.get()==NULL)
298         mpImplementation->mpConfigurationUpdaterLock
299             = mpImplementation->mpConfigurationUpdater->GetLock();
300 }
301 
302 
303 
304 
305 void SAL_CALL ConfigurationController::unlock (void)
306     throw (RuntimeException)
307 {
308     ::osl::MutexGuard aGuard (maMutex);
309 
310     // Allow unlocking while the ConfigurationController is being disposed
311     // (but not when that is done and the controller is disposed.)
312     if (rBHelper.bDisposed)
313         ThrowIfDisposed();
314 
315     OSL_ASSERT(mpImplementation->mnLockCount>0);
316     --mpImplementation->mnLockCount;
317     if (mpImplementation->mnLockCount == 0)
318         mpImplementation->mpConfigurationUpdaterLock.reset();
319 }
320 
321 
322 
323 
324 void SAL_CALL ConfigurationController::requestResourceActivation (
325     const Reference<XResourceId>& rxResourceId,
326     ResourceActivationMode eMode)
327     throw (RuntimeException)
328 {
329     ::osl::MutexGuard aGuard (maMutex);
330    	ThrowIfDisposed();
331 
332     // Check whether we are being disposed.  This is handled differently
333     // then being completely disposed because the first thing disposing()
334     // does is to deactivate all remaining resources.  This is done via
335     // regular methods which must not throw DisposedExceptions.  Therefore
336     // we just return silently during that stage.
337     if (rBHelper.bInDispose)
338     {
339 #if defined VERBOSE && VERBOSE>=1
340         OSL_TRACE("ConfigurationController::requestResourceActivation(): ignoring %s\n",
341             OUStringToOString(
342                 FrameworkHelper::ResourceIdToString(rxResourceId), RTL_TEXTENCODING_UTF8).getStr());
343 #endif
344         return;
345     }
346 
347 #if defined VERBOSE && VERBOSE>=2
348     OSL_TRACE("ConfigurationController::requestResourceActivation() %s\n",
349         OUStringToOString(
350             FrameworkHelper::ResourceIdToString(rxResourceId), RTL_TEXTENCODING_UTF8).getStr());
351 #endif
352 
353     if (rxResourceId.is())
354     {
355         if (eMode == ResourceActivationMode_REPLACE)
356         {
357             // Get a list of the matching resources and create deactivation
358             // requests for them.
359             Sequence<Reference<XResourceId> > aResourceList (
360                 mpImplementation->mxRequestedConfiguration->getResources(
361                     rxResourceId->getAnchor(),
362                     rxResourceId->getResourceTypePrefix(),
363                     AnchorBindingMode_DIRECT));
364 
365             for (sal_Int32 nIndex=0; nIndex<aResourceList.getLength(); ++nIndex)
366             {
367                 // Do not request the deactivation of the resource for which
368                 // this method was called.  Doing it would not change the
369                 // outcome but would result in unnecessary work.
370                 if (rxResourceId->compareTo(aResourceList[nIndex]) == 0)
371                     continue;
372 
373                 // Request the deactivation of a resource and all resources
374                 // linked to it.
375                 requestResourceDeactivation(aResourceList[nIndex]);
376             }
377         }
378 
379         Reference<XConfigurationChangeRequest> xRequest(
380             new GenericConfigurationChangeRequest(
381                 rxResourceId,
382                 GenericConfigurationChangeRequest::Activation));
383         postChangeRequest(xRequest);
384     }
385 }
386 
387 
388 
389 
390 void SAL_CALL ConfigurationController::requestResourceDeactivation (
391     const Reference<XResourceId>& rxResourceId)
392     throw (RuntimeException)
393 {
394     ::osl::MutexGuard aGuard (maMutex);
395     ThrowIfDisposed();
396 
397 #if defined VERBOSE && VERBOSE>=2
398     OSL_TRACE("ConfigurationController::requestResourceDeactivation() %s\n",
399             OUStringToOString(
400                 FrameworkHelper::ResourceIdToString(rxResourceId), RTL_TEXTENCODING_UTF8).getStr());
401 #endif
402 
403     if (rxResourceId.is())
404     {
405         // Request deactivation of all resources linked to the specified one
406         // as well.
407         const Sequence<Reference<XResourceId> > aLinkedResources (
408             mpImplementation->mxRequestedConfiguration->getResources(
409                 rxResourceId,
410                 OUString(),
411                 AnchorBindingMode_DIRECT));
412         const sal_Int32 nCount (aLinkedResources.getLength());
413         for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
414         {
415             // We do not add deactivation requests directly but call this
416             // method recursively, so that when one time there are resources
417             // linked to linked resources, these are handled correctly, too.
418             requestResourceDeactivation(aLinkedResources[nIndex]);
419         }
420 
421         // Add a deactivation request for the specified resource.
422         Reference<XConfigurationChangeRequest> xRequest(
423             new GenericConfigurationChangeRequest(
424                 rxResourceId,
425                 GenericConfigurationChangeRequest::Deactivation));
426         postChangeRequest(xRequest);
427     }
428 }
429 
430 
431 
432 
433 Reference<XResource> SAL_CALL ConfigurationController::getResource (
434     const Reference<XResourceId>& rxResourceId)
435     throw (RuntimeException)
436 {
437     ::osl::MutexGuard aGuard (maMutex);
438     ThrowIfDisposed();
439 
440     ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor (
441         mpImplementation->mpResourceManager->GetResource(rxResourceId));
442     return aDescriptor.mxResource;
443 }
444 
445 
446 
447 
448 void SAL_CALL ConfigurationController::update (void)
449     throw (RuntimeException)
450 {
451     ::osl::MutexGuard aGuard (maMutex);
452     ThrowIfDisposed();
453 
454     if (mpImplementation->mpQueueProcessor->IsEmpty())
455     {
456         // The queue is empty.  Add another request that does nothing but
457         // asynchronously trigger a request for an update.
458         mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest());
459     }
460     else
461     {
462         // The queue is not empty, so we rely on the queue processor to
463         // request an update automatically when the queue becomes empty.
464     }
465 }
466 
467 
468 
469 
470 sal_Bool SAL_CALL ConfigurationController::hasPendingRequests (void)
471     throw (RuntimeException)
472 {
473     ::osl::MutexGuard aGuard (maMutex);
474     ThrowIfDisposed();
475 
476     return ! mpImplementation->mpQueueProcessor->IsEmpty();
477 }
478 
479 
480 
481 
482 
483 void SAL_CALL ConfigurationController::postChangeRequest (
484     const Reference<XConfigurationChangeRequest>& rxRequest)
485     throw (RuntimeException)
486 {
487     ::osl::MutexGuard aGuard (maMutex);
488     ThrowIfDisposed();
489 
490     mpImplementation->mpQueueProcessor->AddRequest(rxRequest);
491 }
492 
493 
494 
495 
496 Reference<XConfiguration> SAL_CALL ConfigurationController::getRequestedConfiguration (void)
497     throw (RuntimeException)
498 {
499     ::osl::MutexGuard aGuard (maMutex);
500     ThrowIfDisposed();
501 
502     if (mpImplementation->mxRequestedConfiguration.is())
503         return Reference<XConfiguration>(
504             mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY);
505     else
506         return Reference<XConfiguration>();
507 }
508 
509 
510 
511 
512 Reference<XConfiguration> SAL_CALL ConfigurationController::getCurrentConfiguration (void)
513     throw (RuntimeException)
514 {
515     ::osl::MutexGuard aGuard (maMutex);
516     ThrowIfDisposed();
517 
518     Reference<XConfiguration> xCurrentConfiguration(
519         mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration());
520     if (xCurrentConfiguration.is())
521         return Reference<XConfiguration>(xCurrentConfiguration->createClone(), UNO_QUERY);
522     else
523         return Reference<XConfiguration>();
524 }
525 
526 
527 
528 
529 /** The given configuration is restored by generating the appropriate set of
530     activation and deactivation requests.
531 */
532 void SAL_CALL ConfigurationController::restoreConfiguration (
533     const Reference<XConfiguration>& rxNewConfiguration)
534     throw (RuntimeException)
535 {
536     ::osl::MutexGuard aGuard (maMutex);
537     ThrowIfDisposed();
538 
539     // We will probably be making a couple of activation and deactivation
540     // requests so lock the configuration controller and let it later update
541     // all changes at once.
542     ::boost::shared_ptr<ConfigurationUpdaterLock> pLock (
543         mpImplementation->mpConfigurationUpdater->GetLock());
544 
545     // Get lists of resources that are to be activated or deactivated.
546     Reference<XConfiguration> xCurrentConfiguration (mpImplementation->mxRequestedConfiguration);
547 #if defined VERBOSE && VERBOSE>=1
548     OSL_TRACE("ConfigurationController::restoreConfiguration(\n");
549     ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration");
550     ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration");
551 #endif
552     ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration);
553     aClassifier.Partition();
554 #if defined VERBOSE && VERBOSE>=3
555     aClassifier.TraceResourceIdVector(
556         "requested but not current resources:\n", aClassifier.GetC1minusC2());
557     aClassifier.TraceResourceIdVector(
558         "current but not requested resources:\n", aClassifier.GetC2minusC1());
559     aClassifier.TraceResourceIdVector(
560         "requested and current resources:\n", aClassifier.GetC1andC2());
561 #endif
562 
563     ConfigurationClassifier::ResourceIdVector::const_iterator iResource;
564 
565     // Request the deactivation of resources that are not requested in the
566     // new configuration.
567     const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate (
568         aClassifier.GetC2minusC1());
569     for (iResource=rResourcesToDeactivate.begin();
570          iResource!=rResourcesToDeactivate.end();
571          ++iResource)
572     {
573         requestResourceDeactivation(*iResource);
574     }
575 
576     // Request the activation of resources that are requested in the
577     // new configuration but are not part of the current configuration.
578     const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate (
579         aClassifier.GetC1minusC2());
580     for (iResource=rResourcesToActivate.begin();
581          iResource!=rResourcesToActivate.end();
582          ++iResource)
583     {
584         requestResourceActivation(*iResource, ResourceActivationMode_ADD);
585     }
586 
587     pLock.reset();
588 }
589 
590 
591 
592 
593 //----- XResourceFactoryManager -----------------------------------------------
594 
595 void SAL_CALL ConfigurationController::addResourceFactory(
596     const OUString& sResourceURL,
597     const Reference<XResourceFactory>& rxResourceFactory)
598     throw (RuntimeException)
599 {
600     ::osl::MutexGuard aGuard (maMutex);
601     ThrowIfDisposed();
602     mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory);
603 }
604 
605 
606 
607 
608 void SAL_CALL ConfigurationController::removeResourceFactoryForURL(
609     const OUString& sResourceURL)
610     throw (RuntimeException)
611 {
612     ::osl::MutexGuard aGuard (maMutex);
613     ThrowIfDisposed();
614     mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL);
615 }
616 
617 
618 
619 
620 void SAL_CALL ConfigurationController::removeResourceFactoryForReference(
621     const Reference<XResourceFactory>& rxResourceFactory)
622     throw (RuntimeException)
623 {
624     ::osl::MutexGuard aGuard (maMutex);
625     ThrowIfDisposed();
626     mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory);
627 }
628 
629 
630 
631 
632 Reference<XResourceFactory> SAL_CALL ConfigurationController::getResourceFactory (
633     const OUString& sResourceURL)
634     throw (RuntimeException)
635 {
636     ::osl::MutexGuard aGuard (maMutex);
637     ThrowIfDisposed();
638 
639     return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL);
640 }
641 
642 
643 
644 
645 //----- XInitialization -------------------------------------------------------
646 
647 void SAL_CALL ConfigurationController::initialize (const Sequence<Any>& aArguments)
648     throw (Exception, RuntimeException)
649 {
650     ::osl::MutexGuard aGuard (maMutex);
651 
652     if (aArguments.getLength() == 1)
653     {
654         const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
655 
656         mpImplementation.reset(new Implementation(
657             *this,
658             Reference<frame::XController>(aArguments[0], UNO_QUERY_THROW)));
659     }
660 }
661 
662 
663 
664 
665 //-----------------------------------------------------------------------------
666 
667 void ConfigurationController::ThrowIfDisposed (void) const
668     throw (::com::sun::star::lang::DisposedException)
669 {
670 	if (mbIsDisposed)
671 	{
672         throw lang::DisposedException (
673             OUString(RTL_CONSTASCII_USTRINGPARAM(
674                 "ConfigurationController object has already been disposed")),
675             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
676     }
677 
678     if (mpImplementation.get() == NULL)
679     {
680         OSL_ASSERT(mpImplementation.get() != NULL);
681         throw RuntimeException(
682             OUString(RTL_CONSTASCII_USTRINGPARAM(
683                 "ConfigurationController not initialized")),
684             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
685     }
686 }
687 
688 
689 
690 
691 //===== ConfigurationController::Implementation ===============================
692 
693 ConfigurationController::Implementation::Implementation (
694     ConfigurationController& rController,
695     const Reference<frame::XController>& rxController)
696     : mxControllerManager(rxController, UNO_QUERY_THROW),
697       mpBroadcaster(new ConfigurationControllerBroadcaster(&rController)),
698       mxRequestedConfiguration(new Configuration(&rController, true)),
699       mpBase(NULL),
700       mpResourceFactoryContainer(new ResourceFactoryManager(mxControllerManager)),
701       mpResourceManager(
702           new ConfigurationControllerResourceManager(mpResourceFactoryContainer,mpBroadcaster)),
703       mpConfigurationUpdater(
704           new ConfigurationUpdater(mpBroadcaster, mpResourceManager,mxControllerManager)),
705       mpQueueProcessor(new ChangeRequestQueueProcessor(&rController,mpConfigurationUpdater)),
706       mpConfigurationUpdaterLock(),
707       mnLockCount(0)
708 {
709     mpQueueProcessor->SetConfiguration(mxRequestedConfiguration);
710 }
711 
712 
713 
714 
715 ConfigurationController::Implementation::~Implementation (void)
716 {
717 }
718 
719 
720 
721 
722 } } // end of namespace sd::framework
723