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 <properties.h>
21 #include <stdtypes.h>
22 #include <helper/mischelper.hxx>
23 
24 #include <com/sun/star/beans/Property.hpp>
25 #include <com/sun/star/beans/XProperty.hpp>
26 #include <com/sun/star/beans/PropertyAttribute.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/util/XChangesNotifier.hpp>
29 #include <com/sun/star/util/PathSubstitution.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/util/XStringSubstitution.hpp>
33 #include <com/sun/star/util/XChangesListener.hpp>
34 #include <com/sun/star/util/XPathSettings.hpp>
35 
36 #include <tools/urlobj.hxx>
37 #include <rtl/ustrbuf.hxx>
38 #include <sal/log.hxx>
39 
40 #include <cppuhelper/basemutex.hxx>
41 #include <cppuhelper/propshlp.hxx>
42 #include <cppuhelper/compbase.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/configurationhelper.hxx>
46 #include <unotools/configitem.hxx>
47 #include <unotools/configpaths.hxx>
48 
49 using namespace framework;
50 
51 #define CFGPROP_USERPATHS "UserPaths"
52 #define CFGPROP_WRITEPATH "WritePath"
53 
54 /*
55     0 : old style              "Template"              string using ";" as separator
56     1 : internal paths         "Template_internal"     string list
57     2 : user paths             "Template_user"         string list
58     3 : write path             "Template_write"        string
59  */
60 
61 #define POSTFIX_INTERNAL_PATHS "_internal"
62 #define POSTFIX_USER_PATHS "_user"
63 #define POSTFIX_WRITE_PATH "_writable"
64 
65 namespace {
66 
67 const sal_Int32 IDGROUP_OLDSTYLE        = 0;
68 const sal_Int32 IDGROUP_INTERNAL_PATHS = 1;
69 const sal_Int32 IDGROUP_USER_PATHS     = 2;
70 const sal_Int32 IDGROUP_WRITE_PATH      = 3;
71 
72 const sal_Int32 IDGROUP_COUNT           = 4;
73 
74 sal_Int32 impl_getPropGroup(sal_Int32 nID)
75 {
76     return (nID % IDGROUP_COUNT);
77 }
78 
79 /* enable it if you wish to migrate old user settings (using the old cfg schema) on demand ....
80    disable it in case only the new schema must be used.
81  */
82 
83 typedef ::cppu::WeakComponentImplHelper<
84             css::lang::XServiceInfo,
85             css::util::XChangesListener,    // => XEventListener
86             css::util::XPathSettings>       // => XPropertySet
87                 PathSettings_BASE;
88 
89 class PathSettings : private cppu::BaseMutex
90                    , public  PathSettings_BASE
91                    , public  ::cppu::OPropertySetHelper
92 {
93     struct PathInfo
94     {
95         public:
96 
97             PathInfo()
98                 : sPathName     ()
99                 , lInternalPaths()
100                 , lUserPaths    ()
101                 , sWritePath    ()
102                 , bIsSinglePath (false)
103                 , bIsReadonly   (false)
104             {}
105 
106             void takeOver(const PathInfo& rCopy)
107             {
108                 sPathName      = rCopy.sPathName;
109                 lInternalPaths = rCopy.lInternalPaths;
110                 lUserPaths     = rCopy.lUserPaths;
111                 sWritePath     = rCopy.sWritePath;
112                 bIsSinglePath  = rCopy.bIsSinglePath;
113                 bIsReadonly    = rCopy.bIsReadonly;
114             }
115 
116             /// an internal name describing this path
117             OUString sPathName;
118 
119             /// contains all paths, which are used internally - but are not visible for the user.
120             std::vector<OUString> lInternalPaths;
121 
122             /// contains all paths configured by the user
123             std::vector<OUString> lUserPaths;
124 
125             /// this special path is used to generate feature depending content there
126             OUString sWritePath;
127 
128             /// indicates real single paths, which uses WritePath property only
129             bool bIsSinglePath;
130 
131             /// simple handling of finalized/mandatory states ... => we know one state READONLY only .-)
132             bool bIsReadonly;
133     };
134 
135     typedef std::unordered_map<OUString, PathSettings::PathInfo> PathHash;
136 
137     enum EChangeOp
138     {
139         E_UNDEFINED,
140         E_ADDED,
141         E_CHANGED,
142         E_REMOVED
143     };
144 
145 private:
146 
147     /** reference to factory, which has create this instance. */
148     css::uno::Reference< css::uno::XComponentContext > m_xContext;
149 
150     /** list of all path variables and her corresponding values. */
151     PathSettings::PathHash m_lPaths;
152 
153     /** describes all properties available on our interface.
154         Will be generated on demand based on our path list m_lPaths. */
155     css::uno::Sequence< css::beans::Property > m_lPropDesc;
156 
157     /** helper needed to (re-)substitute all internal save path values. */
158     css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;
159 
160     /** provides access to the old configuration schema (which will be migrated on demand). */
161     css::uno::Reference< css::container::XNameAccess > m_xCfgOld;
162 
163     /** provides access to the new configuration schema. */
164     css::uno::Reference< css::container::XNameAccess > m_xCfgNew;
165 
166     /** helper to listen for configuration changes without ownership cycle problems */
167     css::uno::Reference< css::util::XChangesListener > m_xCfgNewListener;
168 
169     std::unique_ptr<::cppu::OPropertyArrayHelper> m_pPropHelp;
170 
171 public:
172 
173     /** initialize a new instance of this class.
174         Attention: It's necessary for right function of this class, that the order of base
175         classes is the right one. Because we transfer information from one base to another
176         during this ctor runs! */
177     explicit PathSettings(const css::uno::Reference< css::uno::XComponentContext >& xContext);
178 
179     /** free all used resources ... if it was not already done. */
180     virtual ~PathSettings() override;
181 
182     virtual OUString SAL_CALL getImplementationName() override
183     {
184         return OUString("com.sun.star.comp.framework.PathSettings");
185     }
186 
187     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
188     {
189         return cppu::supportsService(this, ServiceName);
190     }
191 
192     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
193     {
194         css::uno::Sequence< OUString > aSeq { "com.sun.star.util.PathSettings" };
195         return aSeq;
196     }
197 
198     // XInterface
199     virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
200     virtual void SAL_CALL acquire() throw () override
201         { OWeakObject::acquire(); }
202     virtual void SAL_CALL release() throw () override
203         { OWeakObject::release(); }
204 
205     // XTypeProvider
206     virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes(  ) override;
207 
208     // css::util::XChangesListener
209     virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
210 
211     // css::lang::XEventListener
212     virtual void SAL_CALL disposing(const css::lang::EventObject& aSource) override;
213 
214     /**
215      * XPathSettings attribute methods
216      */
217     virtual OUString SAL_CALL getAddin() override
218         { return getStringProperty("Addin"); }
219     virtual void SAL_CALL setAddin(const OUString& p1) override
220         { setStringProperty("Addin", p1); }
221     virtual OUString SAL_CALL getAutoCorrect() override
222         { return getStringProperty("AutoCorrect"); }
223     virtual void SAL_CALL setAutoCorrect(const OUString& p1) override
224         { setStringProperty("AutoCorrect", p1); }
225     virtual OUString SAL_CALL getAutoText() override
226         { return getStringProperty("AutoText"); }
227     virtual void SAL_CALL setAutoText(const OUString& p1) override
228         { setStringProperty("AutoText", p1); }
229     virtual OUString SAL_CALL getBackup() override
230         { return getStringProperty("Backup"); }
231     virtual void SAL_CALL setBackup(const OUString& p1) override
232         { setStringProperty("Backup", p1); }
233     virtual OUString SAL_CALL getBasic() override
234         { return getStringProperty("Basic"); }
235     virtual void SAL_CALL setBasic(const OUString& p1) override
236         { setStringProperty("Basic", p1); }
237     virtual OUString SAL_CALL getBitmap() override
238         { return getStringProperty("Bitmap"); }
239     virtual void SAL_CALL setBitmap(const OUString& p1) override
240         { setStringProperty("Bitmap", p1); }
241     virtual OUString SAL_CALL getConfig() override
242         { return getStringProperty("Config"); }
243     virtual void SAL_CALL setConfig(const OUString& p1) override
244         { setStringProperty("Config", p1); }
245     virtual OUString SAL_CALL getDictionary() override
246         { return getStringProperty("Dictionary"); }
247     virtual void SAL_CALL setDictionary(const OUString& p1) override
248         { setStringProperty("Dictionary", p1); }
249     virtual OUString SAL_CALL getFavorite() override
250         { return getStringProperty("Favorite"); }
251     virtual void SAL_CALL setFavorite(const OUString& p1) override
252         { setStringProperty("Favorite", p1); }
253     virtual OUString SAL_CALL getFilter() override
254         { return getStringProperty("Filter"); }
255     virtual void SAL_CALL setFilter(const OUString& p1) override
256         { setStringProperty("Filter", p1); }
257     virtual OUString SAL_CALL getGallery() override
258         { return getStringProperty("Gallery"); }
259     virtual void SAL_CALL setGallery(const OUString& p1) override
260         { setStringProperty("Gallery", p1); }
261     virtual OUString SAL_CALL getGraphic() override
262         { return getStringProperty("Graphic"); }
263     virtual void SAL_CALL setGraphic(const OUString& p1) override
264         { setStringProperty("Graphic", p1); }
265     virtual OUString SAL_CALL getHelp() override
266         { return getStringProperty("Help"); }
267     virtual void SAL_CALL setHelp(const OUString& p1) override
268         { setStringProperty("Help", p1); }
269     virtual OUString SAL_CALL getLinguistic() override
270         { return getStringProperty("Linguistic"); }
271     virtual void SAL_CALL setLinguistic(const OUString& p1) override
272         { setStringProperty("Linguistic", p1); }
273     virtual OUString SAL_CALL getModule() override
274         { return getStringProperty("Module"); }
275     virtual void SAL_CALL setModule(const OUString& p1) override
276         { setStringProperty("Module", p1); }
277     virtual OUString SAL_CALL getPalette() override
278         { return getStringProperty("Palette"); }
279     virtual void SAL_CALL setPalette(const OUString& p1) override
280         { setStringProperty("Palette", p1); }
281     virtual OUString SAL_CALL getPlugin() override
282         { return getStringProperty("Plugin"); }
283     virtual void SAL_CALL setPlugin(const OUString& p1) override
284         { setStringProperty("Plugin", p1); }
285     virtual OUString SAL_CALL getStorage() override
286         { return getStringProperty("Storage"); }
287     virtual void SAL_CALL setStorage(const OUString& p1) override
288         { setStringProperty("Storage", p1); }
289     virtual OUString SAL_CALL getTemp() override
290         { return getStringProperty("Temp"); }
291     virtual void SAL_CALL setTemp(const OUString& p1) override
292         { setStringProperty("Temp", p1); }
293     virtual OUString SAL_CALL getTemplate() override
294         { return getStringProperty("Template"); }
295     virtual void SAL_CALL setTemplate(const OUString& p1) override
296         { setStringProperty("Template", p1); }
297     virtual OUString SAL_CALL getUIConfig() override
298         { return getStringProperty("UIConfig"); }
299     virtual void SAL_CALL setUIConfig(const OUString& p1) override
300         { setStringProperty("UIConfig", p1); }
301     virtual OUString SAL_CALL getUserConfig() override
302         { return getStringProperty("UserConfig"); }
303     virtual void SAL_CALL setUserConfig(const OUString& p1) override
304         { setStringProperty("UserConfig", p1); }
305     virtual OUString SAL_CALL getUserDictionary() override
306         { return getStringProperty("UserDictionary"); }
307     virtual void SAL_CALL setUserDictionary(const OUString& p1) override
308         { setStringProperty("UserDictionary", p1); }
309     virtual OUString SAL_CALL getWork() override
310         { return getStringProperty("Work"); }
311     virtual void SAL_CALL setWork(const OUString& p1) override
312         { setStringProperty("Work", p1); }
313     virtual OUString SAL_CALL getBasePathShareLayer() override
314         { return getStringProperty("UIConfig"); }
315     virtual void SAL_CALL setBasePathShareLayer(const OUString& p1) override
316         { setStringProperty("UIConfig", p1); }
317     virtual OUString SAL_CALL getBasePathUserLayer() override
318         { return getStringProperty("UserConfig"); }
319     virtual void SAL_CALL setBasePathUserLayer(const OUString& p1) override
320         { setStringProperty("UserConfig", p1); }
321 
322     /**
323      * overrides to resolve inheritance ambiguity
324      */
325     virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
326         { ::cppu::OPropertySetHelper::setPropertyValue(p1, p2); }
327     virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
328         { return ::cppu::OPropertySetHelper::getPropertyValue(p1); }
329     virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
330         { ::cppu::OPropertySetHelper::addPropertyChangeListener(p1, p2); }
331     virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
332         { ::cppu::OPropertySetHelper::removePropertyChangeListener(p1, p2); }
333     virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
334         { ::cppu::OPropertySetHelper::addVetoableChangeListener(p1, p2); }
335     virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
336         { ::cppu::OPropertySetHelper::removeVetoableChangeListener(p1, p2); }
337     /** read all configured paths and create all needed internal structures. */
338     void impl_readAll();
339 
340 private:
341     virtual void SAL_CALL disposing() final override;
342 
343     /// @throws css::uno::RuntimeException
344     OUString getStringProperty(const OUString& p1);
345 
346     /// @throws css::uno::RuntimeException
347     void setStringProperty(const OUString& p1, const OUString& p2);
348 
349     /** read a path info using the old cfg schema.
350         This is needed for "migration on demand" reasons only.
351         Can be removed for next major release .-) */
352     std::vector<OUString> impl_readOldFormat(const OUString& sPath);
353 
354     /** read a path info using the new cfg schema. */
355     PathSettings::PathInfo impl_readNewFormat(const OUString& sPath);
356 
357     /** filter "real user defined paths" from the old configuration schema
358         and set it as UserPaths on the new schema.
359         Can be removed with new major release ... */
360 
361     void impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
362                                  const std::vector<OUString>& lOld );
363 
364     /** reload one path directly from the new configuration schema (because
365         it was updated by any external code) */
366     PathSettings::EChangeOp impl_updatePath(const OUString& sPath          ,
367                                                   bool         bNotifyListener);
368 
369     /** replace all might existing placeholder variables inside the given path ...
370         or check if the given path value uses paths, which can be replaced with predefined
371         placeholder variables ...
372      */
373     void impl_subst(std::vector<OUString>& lVals   ,
374                     const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
375                           bool                                               bReSubst);
376 
377     void impl_subst(PathSettings::PathInfo& aPath   ,
378                     bool                bReSubst);
379 
380     /** converts our new string list schema to the old ";" separated schema ... */
381     OUString impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath        ) const;
382     std::vector<OUString> impl_convertOldStyle2Path(const OUString&        sOldStylePath) const;
383 
384     /** remove still known paths from the given lList argument.
385         So real user defined paths can be extracted from the list of
386         fix internal paths !
387      */
388     void impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
389                               std::vector<OUString>& lList);
390 
391     /** rebuild the member m_lPropDesc using the path list m_lPaths. */
392     void impl_rebuildPropertyDescriptor();
393 
394     /** provides direct access to the list of path values
395         using its internal property id.
396      */
397     css::uno::Any impl_getPathValue(      sal_Int32      nID ) const;
398     void          impl_setPathValue(      sal_Int32      nID ,
399                                     const css::uno::Any& aVal);
400 
401     /** check the given handle and return the corresponding PathInfo reference.
402         These reference can be used then directly to manipulate these path. */
403           PathSettings::PathInfo* impl_getPathAccess     (sal_Int32 nHandle);
404     const PathSettings::PathInfo* impl_getPathAccessConst(sal_Int32 nHandle) const;
405 
406     /** it checks, if the given path value seems to be a valid URL or system path. */
407     bool impl_isValidPath(const OUString& sPath) const;
408     bool impl_isValidPath(const std::vector<OUString>& lPath) const;
409 
410     void impl_storePath(const PathSettings::PathInfo& aPath);
411 
412     css::uno::Sequence< sal_Int32 > impl_mapPathName2IDList(const OUString& sPath);
413 
414     void impl_notifyPropListener( const OUString&        sPath   ,
415                                   const PathSettings::PathInfo* pPathOld,
416                                   const PathSettings::PathInfo* pPathNew);
417 
418     //  OPropertySetHelper
419     virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any&  aConvertedValue,
420             css::uno::Any& aOldValue,
421             sal_Int32 nHandle,
422             const css::uno::Any& aValue ) override;
423     virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
424             const css::uno::Any&  aValue ) override;
425     virtual void SAL_CALL getFastPropertyValue( css::uno::Any&  aValue,
426             sal_Int32 nHandle ) const override;
427     // Avoid:
428     // warning: ‘virtual css::uno::Any cppu::OPropertySetHelper::getFastPropertyValue(sal_Int32)’ was hidden [-Woverloaded-virtual]
429     // warning:   by ‘virtual void {anonymous}::PathSettings::getFastPropertyValue(css::uno::Any&, sal_Int32) const’ [-Woverloaded-virtual]
430     using cppu::OPropertySetHelper::getFastPropertyValue;
431     virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
432     virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
433 
434     /** factory methods to guarantee right (but on demand) initialized members ... */
435     css::uno::Reference< css::util::XStringSubstitution > fa_getSubstitution();
436     css::uno::Reference< css::container::XNameAccess >    fa_getCfgOld();
437     css::uno::Reference< css::container::XNameAccess >    fa_getCfgNew();
438 };
439 
440 PathSettings::PathSettings( const css::uno::Reference< css::uno::XComponentContext >& xContext )
441     : PathSettings_BASE(m_aMutex)
442     , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
443     ,   m_xContext (xContext)
444 {
445 }
446 
447 PathSettings::~PathSettings()
448 {
449     disposing();
450 }
451 
452 void SAL_CALL PathSettings::disposing()
453 {
454     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
455 
456     css::uno::Reference< css::util::XChangesNotifier >
457         xBroadcaster(m_xCfgNew, css::uno::UNO_QUERY);
458     if (xBroadcaster.is())
459         xBroadcaster->removeChangesListener(m_xCfgNewListener);
460 
461     m_xSubstitution.clear();
462     m_xCfgOld.clear();
463     m_xCfgNew.clear();
464     m_xCfgNewListener.clear();
465 
466     m_pPropHelp.reset();
467 }
468 
469 css::uno::Any SAL_CALL PathSettings::queryInterface( const css::uno::Type& _rType )
470 {
471     css::uno::Any aRet = PathSettings_BASE::queryInterface( _rType );
472     if ( !aRet.hasValue() )
473         aRet = ::cppu::OPropertySetHelper::queryInterface( _rType );
474     return aRet;
475 }
476 
477 css::uno::Sequence< css::uno::Type > SAL_CALL PathSettings::getTypes(  )
478 {
479     return comphelper::concatSequences(
480         PathSettings_BASE::getTypes(),
481         ::cppu::OPropertySetHelper::getTypes()
482     );
483 }
484 
485 void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent)
486 {
487     sal_Int32 c                 = aEvent.Changes.getLength();
488     sal_Int32 i                 = 0;
489     bool  bUpdateDescriptor = false;
490 
491     for (i=0; i<c; ++i)
492     {
493         const css::util::ElementChange& aChange = aEvent.Changes[i];
494 
495         OUString sChanged;
496         aChange.Accessor >>= sChanged;
497 
498         OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged);
499         if (!sPath.isEmpty())
500         {
501             PathSettings::EChangeOp eOp = impl_updatePath(sPath, true);
502             if (
503                 (eOp == PathSettings::E_ADDED  ) ||
504                 (eOp == PathSettings::E_REMOVED)
505                )
506                 bUpdateDescriptor = true;
507         }
508     }
509 
510     if (bUpdateDescriptor)
511         impl_rebuildPropertyDescriptor();
512 }
513 
514 void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource)
515 {
516     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
517 
518     if (aSource.Source == m_xCfgNew)
519         m_xCfgNew.clear();
520 }
521 
522 OUString PathSettings::getStringProperty(const OUString& p1)
523 {
524     css::uno::Any a = ::cppu::OPropertySetHelper::getPropertyValue(p1);
525     OUString s;
526     a >>= s;
527     return s;
528 }
529 
530 void PathSettings::setStringProperty(const OUString& p1, const OUString& p2)
531 {
532     ::cppu::OPropertySetHelper::setPropertyValue(p1, css::uno::Any(p2));
533 }
534 
535 void PathSettings::impl_readAll()
536 {
537     try
538     {
539         // TODO think about me
540         css::uno::Reference< css::container::XNameAccess > xCfg    = fa_getCfgNew();
541         css::uno::Sequence< OUString >              lPaths = xCfg->getElementNames();
542 
543         sal_Int32 c = lPaths.getLength();
544         for (sal_Int32 i = 0; i < c; ++i)
545         {
546             const OUString& sPath = lPaths[i];
547             impl_updatePath(sPath, false);
548         }
549     }
550     catch(const css::uno::RuntimeException& )
551     {
552     }
553 
554     impl_rebuildPropertyDescriptor();
555 }
556 
557 // NO substitution here ! It's done outside ...
558 std::vector<OUString> PathSettings::impl_readOldFormat(const OUString& sPath)
559 {
560     css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() );
561     std::vector<OUString> aPathVal;
562 
563     if( xCfg->hasByName(sPath) )
564     {
565         css::uno::Any aVal( xCfg->getByName(sPath) );
566 
567         OUString                       sStringVal;
568         css::uno::Sequence< OUString > lStringListVal;
569 
570         if (aVal >>= sStringVal)
571         {
572             aPathVal.push_back(sStringVal);
573         }
574         else if (aVal >>= lStringListVal)
575         {
576             aPathVal = comphelper::sequenceToContainer<std::vector<OUString>>(lStringListVal);
577         }
578     }
579 
580     return aPathVal;
581 }
582 
583 // NO substitution here ! It's done outside ...
584 PathSettings::PathInfo PathSettings::impl_readNewFormat(const OUString& sPath)
585 {
586     const OUString CFGPROP_INTERNALPATHS("InternalPaths");
587     const OUString CFGPROP_ISSINGLEPATH("IsSinglePath");
588 
589     css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
590 
591     // get access to the "queried" path
592     css::uno::Reference< css::container::XNameAccess > xPath;
593     xCfg->getByName(sPath) >>= xPath;
594 
595     PathSettings::PathInfo aPathVal;
596 
597     // read internal path list
598     css::uno::Reference< css::container::XNameAccess > xIPath;
599     xPath->getByName(CFGPROP_INTERNALPATHS) >>= xIPath;
600     aPathVal.lInternalPaths = comphelper::sequenceToContainer<std::vector<OUString>>(xIPath->getElementNames());
601 
602     // read user defined path list
603     css::uno::Sequence<OUString> vTmpUserPathsSeq;
604     xPath->getByName(CFGPROP_USERPATHS) >>= vTmpUserPathsSeq;
605     aPathVal.lUserPaths = comphelper::sequenceToContainer<std::vector<OUString>>(vTmpUserPathsSeq);
606 
607     // read the writeable path
608     xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath;
609 
610     // avoid duplicates, by removing the writeable path from
611     // the user defined path list if it happens to be there too
612     std::vector<OUString>::iterator aI = std::find(aPathVal.lUserPaths.begin(), aPathVal.lUserPaths.end(), aPathVal.sWritePath);
613     if (aI != aPathVal.lUserPaths.end())
614         aPathVal.lUserPaths.erase(aI);
615 
616     // read state props
617     xPath->getByName(CFGPROP_ISSINGLEPATH) >>= aPathVal.bIsSinglePath;
618 
619     // analyze finalized/mandatory states
620     aPathVal.bIsReadonly = false;
621     css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY);
622     if (xInfo.is())
623     {
624         css::beans::Property aInfo = xInfo->getAsProperty();
625         bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY  ) == css::beans::PropertyAttribute::READONLY  );
626 
627         // Note: 'till we support finalized/mandatory on our API more in detail we handle
628         // all states simple as READONLY! But because all really needed paths are "mandatory" by default
629         // we have to handle "finalized" as the real "readonly" indicator.
630         aPathVal.bIsReadonly = bFinalized;
631     }
632 
633     return aPathVal;
634 }
635 
636 void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath)
637 {
638     css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew();
639     css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld();
640 
641     // try to replace path-parts with well known and supported variables.
642     // So an office can be moved easily to another location without losing
643     // its related paths.
644     PathInfo aResubstPath(aPath);
645     impl_subst(aResubstPath, true);
646 
647     // update new configuration
648     if (! aResubstPath.bIsSinglePath)
649     {
650         ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
651                                                             aResubstPath.sPathName,
652                                                             CFGPROP_USERPATHS,
653                             css::uno::makeAny(comphelper::containerToSequence(aResubstPath.lUserPaths)));
654     }
655 
656     ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
657                                                         aResubstPath.sPathName,
658                                                         CFGPROP_WRITEPATH,
659                                                         css::uno::makeAny(aResubstPath.sWritePath));
660 
661     ::comphelper::ConfigurationHelper::flush(xCfgNew);
662 
663     // remove the whole path from the old configuration!
664     // Otherwise we can't make sure that the diff between new and old configuration
665     // on loading time really represents a user setting!!!
666 
667     // Check if the given path exists inside the old configuration.
668     // Because our new configuration knows more than the list of old paths ... !
669     if (xCfgOld->hasByName(aResubstPath.sPathName))
670     {
671         css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW);
672         xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any());
673         ::comphelper::ConfigurationHelper::flush(xCfgOld);
674     }
675 }
676 
677 void PathSettings::impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
678                                           const std::vector<OUString>& lOld )
679 {
680     for (auto const& old : lOld)
681     {
682         if (rPath.bIsSinglePath)
683         {
684             SAL_WARN_IF(lOld.size()>1, "fwk", "PathSettings::impl_mergeOldUserPaths(): Single path has more than one path value inside old configuration (Common.xcu)!");
685             if ( rPath.sWritePath != old )
686                rPath.sWritePath = old;
687         }
688         else
689         {
690             if (
691                 (  std::find(rPath.lInternalPaths.begin(), rPath.lInternalPaths.end(), old) == rPath.lInternalPaths.end()) &&
692                 (  std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), old)     == rPath.lUserPaths.end()    ) &&
693                 (  rPath.sWritePath != old                                     )
694                )
695                rPath.lUserPaths.push_back(old);
696         }
697     }
698 }
699 
700 PathSettings::EChangeOp PathSettings::impl_updatePath(const OUString& sPath          ,
701                                                             bool         bNotifyListener)
702 {
703     // SAFE ->
704     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
705 
706     PathSettings::PathInfo* pPathOld = nullptr;
707     PathSettings::PathInfo* pPathNew = nullptr;
708     PathSettings::EChangeOp eOp      = PathSettings::E_UNDEFINED;
709     PathSettings::PathInfo  aPath;
710 
711     try
712     {
713         aPath = impl_readNewFormat(sPath);
714         aPath.sPathName = sPath;
715         // replace all might existing variables with real values
716         // Do it before these old paths will be compared against the
717         // new path configuration. Otherwise some strings uses different variables ... but substitution
718         // will produce strings with same content (because some variables are redundant!)
719         impl_subst(aPath, false);
720     }
721     catch(const css::uno::RuntimeException&)
722         { throw; }
723     catch(const css::container::NoSuchElementException&)
724         { eOp = PathSettings::E_REMOVED; }
725     catch(const css::uno::Exception&)
726         { throw; }
727 
728     try
729     {
730         // migration of old user defined values on demand
731         // can be disabled for a new major
732         std::vector<OUString> lOldVals = impl_readOldFormat(sPath);
733         // replace all might existing variables with real values
734         // Do it before these old paths will be compared against the
735         // new path configuration. Otherwise some strings uses different variables ... but substitution
736         // will produce strings with same content (because some variables are redundant!)
737         impl_subst(lOldVals, fa_getSubstitution(), false);
738         impl_mergeOldUserPaths(aPath, lOldVals);
739     }
740     catch(const css::uno::RuntimeException&)
741         { throw; }
742     // Normal(!) exceptions can be ignored!
743     // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation
744     // we can't find a value for it inside the "old" configuration. So a NoSuchElementException
745     // will be normal .-)
746     catch(const css::uno::Exception&)
747         {}
748 
749     PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath);
750     if (eOp == PathSettings::E_UNDEFINED)
751     {
752         if (pPath != m_lPaths.end())
753             eOp = PathSettings::E_CHANGED;
754         else
755             eOp = PathSettings::E_ADDED;
756     }
757 
758     switch(eOp)
759     {
760         case PathSettings::E_ADDED :
761              {
762                 if (bNotifyListener)
763                 {
764                     pPathOld = nullptr;
765                     pPathNew = &aPath;
766                     impl_notifyPropListener(sPath, pPathOld, pPathNew);
767                 }
768                 m_lPaths[sPath] = aPath;
769              }
770              break;
771 
772         case PathSettings::E_CHANGED :
773              {
774                 if (bNotifyListener)
775                 {
776                     pPathOld = &(pPath->second);
777                     pPathNew = &aPath;
778                     impl_notifyPropListener(sPath, pPathOld, pPathNew);
779                 }
780                 m_lPaths[sPath] = aPath;
781              }
782              break;
783 
784         case PathSettings::E_REMOVED :
785              {
786                 if (pPath != m_lPaths.end())
787                 {
788                     if (bNotifyListener)
789                     {
790                         pPathOld = &(pPath->second);
791                         pPathNew = nullptr;
792                         impl_notifyPropListener(sPath, pPathOld, pPathNew);
793                     }
794                     m_lPaths.erase(pPath);
795                 }
796              }
797              break;
798 
799         default: // to let compiler be happy
800              break;
801     }
802 
803     return eOp;
804 }
805 
806 css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(const OUString& sPath)
807 {
808     OUString sInternalProp = sPath+POSTFIX_INTERNAL_PATHS;
809     OUString sUserProp     = sPath+POSTFIX_USER_PATHS;
810     OUString sWriteProp    = sPath+POSTFIX_WRITE_PATH;
811 
812     // Attention: The default set of IDs is fix and must follow these schema.
813     // Otherwise the outside code ant work for new added properties.
814     // Why?
815     // The outside code must fire N events for every changed property.
816     // And the knowing about packaging of variables of the structure PathInfo
817     // follow these group IDs! But if such ID is not in the range of [0..IDGROUP_COUNT]
818     // the outside can't determine the right group ... and can not fire the right events .-)
819 
820     css::uno::Sequence< sal_Int32 > lIDs(IDGROUP_COUNT);
821     lIDs[0] = IDGROUP_OLDSTYLE;
822     lIDs[1] = IDGROUP_INTERNAL_PATHS;
823     lIDs[2] = IDGROUP_USER_PATHS;
824     lIDs[3] = IDGROUP_WRITE_PATH;
825 
826     sal_Int32 c = m_lPropDesc.getLength();
827     sal_Int32 i = 0;
828     for (i=0; i<c; ++i)
829     {
830         const css::beans::Property& rProp = m_lPropDesc[i];
831 
832         if (rProp.Name == sPath)
833             lIDs[IDGROUP_OLDSTYLE] = rProp.Handle;
834         else
835         if (rProp.Name == sInternalProp)
836             lIDs[IDGROUP_INTERNAL_PATHS] = rProp.Handle;
837         else
838         if (rProp.Name == sUserProp)
839             lIDs[IDGROUP_USER_PATHS] = rProp.Handle;
840         else
841         if (rProp.Name == sWriteProp)
842             lIDs[IDGROUP_WRITE_PATH] = rProp.Handle;
843     }
844 
845     return lIDs;
846 }
847 
848 void PathSettings::impl_notifyPropListener( const OUString&               sPath,
849                                             const PathSettings::PathInfo* pPathOld,
850                                             const PathSettings::PathInfo* pPathNew)
851 {
852     css::uno::Sequence< sal_Int32 >     lHandles(1);
853     css::uno::Sequence< css::uno::Any > lOldVals(1);
854     css::uno::Sequence< css::uno::Any > lNewVals(1);
855 
856     css::uno::Sequence< sal_Int32 > lIDs   = impl_mapPathName2IDList(sPath);
857     sal_Int32                       c      = lIDs.getLength();
858     sal_Int32                       i      = 0;
859     sal_Int32                       nMaxID = m_lPropDesc.getLength()-1;
860     for (i=0; i<c; ++i)
861     {
862         sal_Int32 nID = lIDs[i];
863 
864         if (
865             (nID < 0     ) ||
866             (nID > nMaxID)
867            )
868            continue;
869 
870         lHandles[0] = nID;
871         switch(impl_getPropGroup(nID))
872         {
873             case IDGROUP_OLDSTYLE :
874                  {
875                     if (pPathOld)
876                     {
877                         OUString sVal = impl_convertPath2OldStyle(*pPathOld);
878                         lOldVals[0] <<= sVal;
879                     }
880                     if (pPathNew)
881                     {
882                         OUString sVal = impl_convertPath2OldStyle(*pPathNew);
883                         lNewVals[0] <<= sVal;
884                     }
885                  }
886                  break;
887 
888             case IDGROUP_INTERNAL_PATHS :
889                  {
890                     if (pPathOld)
891                         lOldVals[0] <<= comphelper::containerToSequence(pPathOld->lInternalPaths);
892                     if (pPathNew)
893                         lNewVals[0] <<= comphelper::containerToSequence(pPathNew->lInternalPaths);
894                  }
895                  break;
896 
897             case IDGROUP_USER_PATHS :
898                  {
899                     if (pPathOld)
900                         lOldVals[0] <<= comphelper::containerToSequence(pPathOld->lUserPaths);
901                     if (pPathNew)
902                         lNewVals[0] <<= comphelper::containerToSequence(pPathNew->lUserPaths);
903                  }
904                  break;
905 
906             case IDGROUP_WRITE_PATH :
907                  {
908                     if (pPathOld)
909                         lOldVals[0] <<= pPathOld->sWritePath;
910                     if (pPathNew)
911                         lNewVals[0] <<= pPathNew->sWritePath;
912                  }
913                  break;
914         }
915 
916         fire(lHandles.getArray(),
917              lNewVals.getArray(),
918              lOldVals.getArray(),
919              1,
920              false);
921     }
922 }
923 
924 void PathSettings::impl_subst(std::vector<OUString>& lVals   ,
925                               const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
926                                     bool                                               bReSubst)
927 {
928     for (auto & old : lVals)
929     {
930         OUString  sNew;
931         if (bReSubst)
932             sNew = xSubst->reSubstituteVariables(old);
933         else
934             sNew = xSubst->substituteVariables(old, false);
935 
936         old = sNew;
937     }
938 }
939 
940 void PathSettings::impl_subst(PathSettings::PathInfo& aPath   ,
941                               bool                bReSubst)
942 {
943     css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution();
944 
945     impl_subst(aPath.lInternalPaths, xSubst, bReSubst);
946     impl_subst(aPath.lUserPaths    , xSubst, bReSubst);
947     if (bReSubst)
948         aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath);
949     else
950         aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, false);
951 }
952 
953 OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const
954 {
955     std::vector<OUString> lTemp;
956     lTemp.reserve(rPath.lInternalPaths.size() + rPath.lUserPaths.size() + 1);
957 
958     for (auto const& internalPath : rPath.lInternalPaths)
959     {
960         lTemp.push_back(internalPath);
961     }
962     for (auto const& userPath : rPath.lUserPaths)
963     {
964         lTemp.push_back(userPath);
965     }
966 
967     if (!rPath.sWritePath.isEmpty())
968         lTemp.push_back(rPath.sWritePath);
969 
970     OUStringBuffer sPathVal(256);
971     for (  auto pIt  = lTemp.begin();
972            pIt != lTemp.end();
973                                )
974     {
975         sPathVal.append(*pIt);
976         ++pIt;
977         if (pIt != lTemp.end())
978             sPathVal.append(";");
979     }
980 
981     return sPathVal.makeStringAndClear();
982 }
983 
984 std::vector<OUString> PathSettings::impl_convertOldStyle2Path(const OUString& sOldStylePath) const
985 {
986     std::vector<OUString> lList;
987     sal_Int32    nToken = 0;
988     do
989     {
990         OUString sToken = sOldStylePath.getToken(0, ';', nToken);
991         if (!sToken.isEmpty())
992             lList.push_back(sToken);
993     }
994     while(nToken >= 0);
995 
996     return lList;
997 }
998 
999 void PathSettings::impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
1000                                         std::vector<OUString>& lList)
1001 {
1002     std::vector<OUString>::iterator pIt;
1003 
1004     // Erase items in the internal path list from lList.
1005     // Also erase items in the internal path list from the user path list.
1006     for (auto const& internalPath : rPath.lInternalPaths)
1007     {
1008         std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), internalPath);
1009         if (pItem != lList.end())
1010             lList.erase(pItem);
1011         pItem = std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), internalPath);
1012         if (pItem != rPath.lUserPaths.end())
1013             rPath.lUserPaths.erase(pItem);
1014     }
1015 
1016     // Erase items not in lList from the user path list.
1017     pIt = rPath.lUserPaths.begin();
1018     while ( pIt != rPath.lUserPaths.end() )
1019     {
1020         const OUString& rItem = *pIt;
1021         std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), rItem);
1022         if ( pItem == lList.end() )
1023         {
1024             pIt = rPath.lUserPaths.erase(pIt);
1025         }
1026         else
1027         {
1028             ++pIt;
1029         }
1030     }
1031 
1032     // Erase items in the user path list from lList.
1033     for (auto const& userPath : rPath.lUserPaths)
1034     {
1035         std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), userPath);
1036         if (pItem != lList.end())
1037             lList.erase(pItem);
1038     }
1039 
1040     // Erase the write path from lList
1041     std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), rPath.sWritePath);
1042     if (pItem != lList.end())
1043         lList.erase(pItem);
1044 }
1045 
1046 void PathSettings::impl_rebuildPropertyDescriptor()
1047 {
1048     // SAFE ->
1049     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1050 
1051     sal_Int32 c = static_cast<sal_Int32>(m_lPaths.size());
1052     sal_Int32 i = 0;
1053     m_lPropDesc.realloc(c*IDGROUP_COUNT);
1054 
1055     for (auto const& path : m_lPaths)
1056     {
1057         const PathSettings::PathInfo& rPath = path.second;
1058               css::beans::Property*   pProp = nullptr;
1059 
1060         pProp             = &(m_lPropDesc[i]);
1061         pProp->Name       = rPath.sPathName;
1062         pProp->Handle     = i;
1063         pProp->Type       = cppu::UnoType<OUString>::get();
1064         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1065         if (rPath.bIsReadonly)
1066             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1067         ++i;
1068 
1069         pProp             = &(m_lPropDesc[i]);
1070         pProp->Name       = rPath.sPathName+POSTFIX_INTERNAL_PATHS;
1071         pProp->Handle     = i;
1072         pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
1073         pProp->Attributes = css::beans::PropertyAttribute::BOUND   |
1074                             css::beans::PropertyAttribute::READONLY;
1075         ++i;
1076 
1077         pProp             = &(m_lPropDesc[i]);
1078         pProp->Name       = rPath.sPathName+POSTFIX_USER_PATHS;
1079         pProp->Handle     = i;
1080         pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
1081         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1082         if (rPath.bIsReadonly)
1083             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1084         ++i;
1085 
1086         pProp             = &(m_lPropDesc[i]);
1087         pProp->Name       = rPath.sPathName+POSTFIX_WRITE_PATH;
1088         pProp->Handle     = i;
1089         pProp->Type       = cppu::UnoType<OUString>::get();
1090         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1091         if (rPath.bIsReadonly)
1092             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1093         ++i;
1094     }
1095 
1096     m_pPropHelp.reset(new ::cppu::OPropertyArrayHelper(m_lPropDesc, false)); // false => not sorted ... must be done inside helper
1097 
1098     // <- SAFE
1099 }
1100 
1101 css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const
1102 {
1103     const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID);
1104     if (! pPath)
1105         throw css::lang::IllegalArgumentException();
1106 
1107     css::uno::Any aVal;
1108     switch(impl_getPropGroup(nID))
1109     {
1110         case IDGROUP_OLDSTYLE :
1111              {
1112                 OUString sVal = impl_convertPath2OldStyle(*pPath);
1113                 aVal <<= sVal;
1114              }
1115              break;
1116 
1117         case IDGROUP_INTERNAL_PATHS :
1118              {
1119                 aVal <<= comphelper::containerToSequence(pPath->lInternalPaths);
1120              }
1121              break;
1122 
1123         case IDGROUP_USER_PATHS :
1124              {
1125                 aVal <<= comphelper::containerToSequence(pPath->lUserPaths);
1126              }
1127              break;
1128 
1129         case IDGROUP_WRITE_PATH :
1130              {
1131                 aVal <<= pPath->sWritePath;
1132              }
1133              break;
1134     }
1135 
1136     return aVal;
1137 }
1138 
1139 void PathSettings::impl_setPathValue(      sal_Int32      nID ,
1140                                      const css::uno::Any& aVal)
1141 {
1142     PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID);
1143     if (! pOrgPath)
1144         throw css::container::NoSuchElementException();
1145 
1146     // We work on a copied path ... so we can be sure that errors during this operation
1147     // does not make our internal cache invalid  .-)
1148     PathSettings::PathInfo aChangePath(*pOrgPath);
1149 
1150     switch(impl_getPropGroup(nID))
1151     {
1152         case IDGROUP_OLDSTYLE :
1153              {
1154                 OUString sVal;
1155                 aVal >>= sVal;
1156                 std::vector<OUString> lList = impl_convertOldStyle2Path(sVal);
1157                 impl_subst(lList, fa_getSubstitution(), false);
1158                 impl_purgeKnownPaths(aChangePath, lList);
1159                 if (! impl_isValidPath(lList))
1160                     throw css::lang::IllegalArgumentException();
1161 
1162                 if (aChangePath.bIsSinglePath)
1163                 {
1164                     SAL_WARN_IF(lList.size()>1, "fwk", "PathSettings::impl_setPathValue(): You try to set more than path value for a defined SINGLE_PATH!");
1165                     if ( !lList.empty() )
1166                         aChangePath.sWritePath = *(lList.begin());
1167                     else
1168                         aChangePath.sWritePath.clear();
1169                 }
1170                 else
1171                 {
1172                     for (auto const& elem : lList)
1173                     {
1174                         aChangePath.lUserPaths.push_back(elem);
1175                     }
1176                 }
1177              }
1178              break;
1179 
1180         case IDGROUP_INTERNAL_PATHS :
1181              {
1182                 if (aChangePath.bIsSinglePath)
1183                 {
1184                     throw css::uno::Exception(
1185                         "The path '" + aChangePath.sPathName
1186                         + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
1187                         static_cast< ::cppu::OWeakObject* >(this));
1188                 }
1189 
1190                 css::uno::Sequence<OUString> lTmpList;
1191                 aVal >>= lTmpList;
1192                 std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
1193                 if (! impl_isValidPath(lList))
1194                     throw css::lang::IllegalArgumentException();
1195                 aChangePath.lInternalPaths = lList;
1196              }
1197              break;
1198 
1199         case IDGROUP_USER_PATHS :
1200              {
1201                 if (aChangePath.bIsSinglePath)
1202                 {
1203                     throw css::uno::Exception(
1204                         "The path '" + aChangePath.sPathName
1205                         + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
1206                         static_cast< ::cppu::OWeakObject* >(this));
1207                 }
1208 
1209                 css::uno::Sequence<OUString> lTmpList;
1210                 aVal >>= lTmpList;
1211                 std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
1212                 if (! impl_isValidPath(lList))
1213                     throw css::lang::IllegalArgumentException();
1214                 aChangePath.lUserPaths = lList;
1215              }
1216              break;
1217 
1218         case IDGROUP_WRITE_PATH :
1219              {
1220                 OUString sVal;
1221                 aVal >>= sVal;
1222                 if (! impl_isValidPath(sVal))
1223                     throw css::lang::IllegalArgumentException();
1224                 aChangePath.sWritePath = sVal;
1225              }
1226              break;
1227     }
1228 
1229     // TODO check if path has at least one path value set
1230     // At least it depends from the feature using this path, if an empty path list is allowed.
1231 
1232     // first we should try to store the changed (copied!) path ...
1233     // In case an error occurs on saving time an exception is thrown ...
1234     // If no exception occurs we can update our internal cache (means
1235     // we can overwrite pOrgPath !
1236     impl_storePath(aChangePath);
1237     pOrgPath->takeOver(aChangePath);
1238 }
1239 
1240 bool PathSettings::impl_isValidPath(const std::vector<OUString>& lPath) const
1241 {
1242     for (auto const& path : lPath)
1243     {
1244         if (! impl_isValidPath(path))
1245             return false;
1246     }
1247 
1248     return true;
1249 }
1250 
1251 bool PathSettings::impl_isValidPath(const OUString& sPath) const
1252 {
1253     // allow empty path to reset a path.
1254 // idea by LLA to support empty paths
1255 //    if (sPath.getLength() == 0)
1256 //    {
1257 //        return sal_True;
1258 //    }
1259 
1260     return (! INetURLObject(sPath).HasError());
1261 }
1262 
1263 OUString impl_extractBaseFromPropName(const OUString& sPropName)
1264 {
1265     sal_Int32 i = sPropName.indexOf(POSTFIX_INTERNAL_PATHS);
1266     if (i > -1)
1267         return sPropName.copy(0, i);
1268     i = sPropName.indexOf(POSTFIX_USER_PATHS);
1269     if (i > -1)
1270         return sPropName.copy(0, i);
1271     i = sPropName.indexOf(POSTFIX_WRITE_PATH);
1272     if (i > -1)
1273         return sPropName.copy(0, i);
1274 
1275     return sPropName;
1276 }
1277 
1278 PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle)
1279 {
1280     // SAFE ->
1281     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1282 
1283     if (nHandle > (m_lPropDesc.getLength()-1))
1284         return nullptr;
1285 
1286     const css::beans::Property&            rProp = m_lPropDesc[nHandle];
1287           OUString                  sProp = impl_extractBaseFromPropName(rProp.Name);
1288           PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp);
1289 
1290     if (rPath != m_lPaths.end())
1291        return &(rPath->second);
1292 
1293     return nullptr;
1294     // <- SAFE
1295 }
1296 
1297 const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const
1298 {
1299     // SAFE ->
1300     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1301 
1302     if (nHandle > (m_lPropDesc.getLength()-1))
1303         return nullptr;
1304 
1305     const css::beans::Property&                  rProp = m_lPropDesc[nHandle];
1306           OUString                        sProp = impl_extractBaseFromPropName(rProp.Name);
1307           PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp);
1308 
1309     if (rPath != m_lPaths.end())
1310        return &(rPath->second);
1311 
1312     return nullptr;
1313     // <- SAFE
1314 }
1315 
1316 sal_Bool SAL_CALL PathSettings::convertFastPropertyValue(      css::uno::Any& aConvertedValue,
1317                                                                css::uno::Any& aOldValue      ,
1318                                                                sal_Int32      nHandle        ,
1319                                                          const css::uno::Any& aValue         )
1320 {
1321     // throws NoSuchElementException !
1322     css::uno::Any aCurrentVal = impl_getPathValue(nHandle);
1323 
1324     return PropHelper::willPropertyBeChanged(
1325                 aCurrentVal,
1326                 aValue,
1327                 aOldValue,
1328                 aConvertedValue);
1329 }
1330 
1331 void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast(      sal_Int32      nHandle,
1332                                                              const css::uno::Any& aValue )
1333 {
1334     // throws NoSuchElement- and IllegalArgumentException !
1335     impl_setPathValue(nHandle, aValue);
1336 }
1337 
1338 void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue ,
1339                                                  sal_Int32      nHandle) const
1340 {
1341     aValue = impl_getPathValue(nHandle);
1342 }
1343 
1344 ::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper()
1345 {
1346     return *m_pPropHelp;
1347 }
1348 
1349 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo()
1350 {
1351     return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
1352 }
1353 
1354 css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution()
1355 {
1356     css::uno::Reference< css::util::XStringSubstitution > xSubst;
1357     { // SAFE ->
1358     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1359     xSubst = m_xSubstitution;
1360     }
1361 
1362     if (! xSubst.is())
1363     {
1364         // create the needed substitution service.
1365         // We must replace all used variables inside read path values.
1366         // In case we can't do so... the whole office can't work really.
1367         // That's why it seems to be OK to throw a RuntimeException then.
1368         xSubst = css::util::PathSubstitution::create(m_xContext);
1369 
1370         { // SAFE ->
1371         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1372         m_xSubstitution = xSubst;
1373         }
1374     }
1375 
1376     return xSubst;
1377 }
1378 
1379 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld()
1380 {
1381     const OUString CFG_NODE_OLD("org.openoffice.Office.Common/Path/Current");
1382 
1383     css::uno::Reference< css::container::XNameAccess > xCfg;
1384     { // SAFE ->
1385     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1386     xCfg = m_xCfgOld;
1387     } // <- SAFE
1388 
1389     if (! xCfg.is())
1390     {
1391         xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
1392                         m_xContext,
1393                         CFG_NODE_OLD,
1394                         ::comphelper::EConfigurationModes::Standard), // not readonly! Sometimes we need write access there !!!
1395                    css::uno::UNO_QUERY_THROW);
1396 
1397         { // SAFE ->
1398         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1399         m_xCfgOld = xCfg;
1400         }
1401     }
1402 
1403     return xCfg;
1404 }
1405 
1406 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew()
1407 {
1408     const OUString CFG_NODE_NEW("org.openoffice.Office.Paths/Paths");
1409 
1410     css::uno::Reference< css::container::XNameAccess > xCfg;
1411     { // SAFE ->
1412     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1413     xCfg = m_xCfgNew;
1414     } // <- SAFE
1415 
1416     if (! xCfg.is())
1417     {
1418         xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
1419                         m_xContext,
1420                         CFG_NODE_NEW,
1421                         ::comphelper::EConfigurationModes::Standard),
1422                    css::uno::UNO_QUERY_THROW);
1423 
1424         { // SAFE ->
1425         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1426         m_xCfgNew = xCfg;
1427         m_xCfgNewListener = new WeakChangesListener(this);
1428         }
1429 
1430         css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW);
1431         xBroadcaster->addChangesListener(m_xCfgNewListener);
1432     }
1433 
1434     return xCfg;
1435 }
1436 
1437 struct Instance {
1438     explicit Instance(
1439         css::uno::Reference<css::uno::XComponentContext> const & context):
1440         instance(
1441             static_cast<cppu::OWeakObject *>(new PathSettings(context)))
1442     {
1443         // fill cache
1444         static_cast<PathSettings *>(static_cast<cppu::OWeakObject *>
1445                 (instance.get()))->impl_readAll();
1446     }
1447 
1448     css::uno::Reference<css::uno::XInterface> instance;
1449 };
1450 
1451 struct Singleton:
1452     public rtl::StaticWithArg<
1453         Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton>
1454 {};
1455 
1456 }
1457 
1458 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
1459 com_sun_star_comp_framework_PathSettings_get_implementation(
1460     css::uno::XComponentContext *context,
1461     css::uno::Sequence<css::uno::Any> const &)
1462 {
1463     return cppu::acquire(static_cast<cppu::OWeakObject *>(
1464                 Singleton::get(context).instance.get()));
1465 }
1466 
1467 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1468