xref: /trunk/main/framework/source/services/pathsettings.cxx (revision 67f7bfb15893aaa2f3b1ee7ec6b966aaaad422fc)
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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_framework.hxx"
24 // ______________________________________________
25 // my own includes
26 
27 /** Attention: stl headers must(!) be included at first. Otherwise it can make trouble
28                with solaris headers ...
29 */
30 #include <vector>
31 #include <services/pathsettings.hxx>
32 #include <threadhelp/readguard.hxx>
33 #include <threadhelp/writeguard.hxx>
34 #include <services.h>
35 
36 // ______________________________________________
37 // interface includes
38 #include <com/sun/star/beans/Property.hpp>
39 #include <com/sun/star/beans/XProperty.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/container/XContainer.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/util/XChangesNotifier.hpp>
44 
45 // ______________________________________________
46 // includes of other projects
47 #include <tools/urlobj.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <rtl/logfile.hxx>
50 
51 #include <comphelper/configurationhelper.hxx>
52 #include <unotools/configpathes.hxx>
53 
54 #include <fwkdllapi.h>
55 
56 // ______________________________________________
57 //  non exported const
58 
59 #define CFG_READONLY_DEFAULT    sal_False
60 
61 const ::rtl::OUString CFGPROP_INTERNALPATHES = ::rtl::OUString::createFromAscii("InternalPaths");
62 const ::rtl::OUString CFGPROP_USERPATHES     = ::rtl::OUString::createFromAscii("UserPaths"    );
63 const ::rtl::OUString CFGPROP_WRITEPATH      = ::rtl::OUString::createFromAscii("WritePath"    );
64 const ::rtl::OUString CFGPROP_ISSINGLEPATH   = ::rtl::OUString::createFromAscii("IsSinglePath" );
65 
66 /*
67     0 : old style              "Template"              string using ";" as seperator
68     1 : internal paths         "Template_internal"     string list
69     2 : user paths             "Template_user"         string list
70     3 : write path             "Template_write"        string
71  */
72 
73 const ::rtl::OUString POSTFIX_INTERNAL_PATHES = ::rtl::OUString::createFromAscii("_internal");
74 const ::rtl::OUString POSTFIX_USER_PATHES     = ::rtl::OUString::createFromAscii("_user"    );
75 const ::rtl::OUString POSTFIX_WRITE_PATH      = ::rtl::OUString::createFromAscii("_writable");
76 
77 const sal_Int32 IDGROUP_OLDSTYLE        = 0;
78 const sal_Int32 IDGROUP_INTERNAL_PATHES = 1;
79 const sal_Int32 IDGROUP_USER_PATHES     = 2;
80 const sal_Int32 IDGROUP_WRITE_PATH      = 3;
81 
82 const sal_Int32 IDGROUP_COUNT           = 4;
83 
84 sal_Int32 impl_getPropGroup(sal_Int32 nID)
85 {
86     return (nID % IDGROUP_COUNT);
87 }
88 
89 // ______________________________________________
90 //  namespace
91 
92 namespace framework
93 {
94 
95 //-----------------------------------------------------------------------------
96 // XInterface, XTypeProvider, XServiceInfo
97 
98 DEFINE_XINTERFACE_7                     (   PathSettings                                             ,
99                                             OWeakObject                                              ,
100                                             DIRECT_INTERFACE ( css::lang::XTypeProvider              ),
101                                             DIRECT_INTERFACE ( css::lang::XServiceInfo               ),
102                                             DERIVED_INTERFACE( css::lang::XEventListener, css::util::XChangesListener),
103                                             DIRECT_INTERFACE ( css::util::XChangesListener           ),
104                                             DIRECT_INTERFACE ( css::beans::XPropertySet              ),
105                                             DIRECT_INTERFACE ( css::beans::XFastPropertySet          ),
106                                             DIRECT_INTERFACE ( css::beans::XMultiPropertySet        )
107                                         )
108 
109 DEFINE_XTYPEPROVIDER_7                  (   PathSettings                                            ,
110                                             css::lang::XTypeProvider                                ,
111                                             css::lang::XServiceInfo                                 ,
112                                             css::lang::XEventListener                               ,
113                                             css::util::XChangesListener                             ,
114                                             css::beans::XPropertySet                                ,
115                                             css::beans::XFastPropertySet                            ,
116                                             css::beans::XMultiPropertySet
117                                         )
118 
119 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE  (   PathSettings                                            ,
120                                             ::cppu::OWeakObject                                     ,
121                                             SERVICENAME_PATHSETTINGS                                ,
122                                             IMPLEMENTATIONNAME_PATHSETTINGS
123                                         )
124 
125 DEFINE_INIT_SERVICE                     (   PathSettings,
126                                             {
127                                                 /*Attention
128                                                     I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
129                                                     to create a new instance of this class by our own supported service factory.
130                                                     see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information!
131                                                 */
132 
133                                                 // fill cache
134                                                 impl_readAll();
135                                             }
136                                         )
137 
138 //-----------------------------------------------------------------------------
139 PathSettings::PathSettings( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR )
140     //  Init baseclasses first
141     //  Attention: Don't change order of initialization!
142     //      ThreadHelpBase is a struct with a lock as member. We can't use a lock as direct member!
143     //      We must garant right initialization and a valid value of this to initialize other baseclasses!
144     :   ThreadHelpBase()
145     ,   ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >(m_aLock.getShareableOslMutex())
146     ,   ::cppu::OPropertySetHelper(*(static_cast< ::cppu::OBroadcastHelper* >(this)))
147     ,   ::cppu::OWeakObject()
148     // Init member
149     ,   m_xSMGR    (xSMGR)
150     ,   m_pPropHelp(0    )
151     ,  m_bIgnoreEvents(sal_False)
152 {
153     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::PathSettings" );
154 }
155 
156 //-----------------------------------------------------------------------------
157 PathSettings::~PathSettings()
158 {
159     if (m_pPropHelp)
160        delete m_pPropHelp;
161 }
162 
163 //-----------------------------------------------------------------------------
164 void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent)
165     throw (css::uno::RuntimeException)
166 {
167     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::changesOccurred" );
168     /*
169     if (m_bIgnoreEvents)
170         return;
171     */
172 
173     sal_Int32 c                 = aEvent.Changes.getLength();
174     sal_Int32 i                 = 0;
175     sal_Bool  bUpdateDescriptor = sal_False;
176 
177     for (i=0; i<c; ++i)
178     {
179         const css::util::ElementChange& aChange = aEvent.Changes[i];
180 
181         ::rtl::OUString sChanged;
182         aChange.Accessor >>= sChanged;
183 
184         ::rtl::OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged);
185         if (sPath.getLength())
186         {
187             PathSettings::EChangeOp eOp = impl_updatePath(sPath, sal_True);
188             if (
189                 (eOp == PathSettings::E_ADDED  ) ||
190                 (eOp == PathSettings::E_REMOVED)
191                )
192                 bUpdateDescriptor = sal_True;
193         }
194     }
195 
196     if (bUpdateDescriptor)
197         impl_rebuildPropertyDescriptor();
198 }
199 
200 //-----------------------------------------------------------------------------
201 void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource)
202     throw(css::uno::RuntimeException)
203 {
204     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::disposing" );
205     // SAFE ->
206     WriteGuard aWriteLock(m_aLock);
207 
208     if (aSource.Source == m_xCfgNew)
209         m_xCfgNew.clear();
210 
211     aWriteLock.unlock();
212     // <- SAFE
213 }
214 
215 //-----------------------------------------------------------------------------
216 void PathSettings::impl_readAll()
217 {
218     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_readAll" );
219     RTL_LOGFILE_CONTEXT(aLog, "framework (as96863) ::PathSettings::load config (all)");
220 
221     // TODO think about me
222     css::uno::Reference< css::container::XNameAccess > xCfg    = fa_getCfgNew();
223     css::uno::Sequence< ::rtl::OUString >              lPaths = xCfg->getElementNames();
224 
225     sal_Int32 c = lPaths.getLength();
226     sal_Int32 i = 0;
227 
228     for (i=0; i<c; ++i)
229     {
230         const ::rtl::OUString& sPath = lPaths[i];
231         impl_updatePath(sPath, sal_False);
232     }
233 
234     impl_rebuildPropertyDescriptor();
235 }
236 
237 //-----------------------------------------------------------------------------
238 // NO substitution here ! It's done outside ...
239 OUStringList PathSettings::impl_readOldFormat(const ::rtl::OUString& sPath)
240 {
241     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_readOldFormat" );
242     css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() );
243     OUStringList aPathVal;
244 
245     if( xCfg->hasByName(sPath) )
246     {
247         css::uno::Any aVal( xCfg->getByName(sPath) );
248 
249         ::rtl::OUString                       sStringVal;
250         css::uno::Sequence< ::rtl::OUString > lStringListVal;
251 
252         if (aVal >>= sStringVal)
253         {
254             aPathVal.push_back(sStringVal);
255         }
256         else if (aVal >>= lStringListVal)
257         {
258             aPathVal << lStringListVal;
259         }
260     }
261 
262     return aPathVal;
263 }
264 
265 //-----------------------------------------------------------------------------
266 // NO substitution here ! It's done outside ...
267 PathSettings::PathInfo PathSettings::impl_readNewFormat(const ::rtl::OUString& sPath)
268 {
269     css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
270 
271     // get access to the "queried" path
272     css::uno::Reference< css::container::XNameAccess > xPath;
273     xCfg->getByName(sPath) >>= xPath;
274 
275     PathSettings::PathInfo aPathVal;
276 
277     // read internal path list
278     css::uno::Reference< css::container::XNameAccess > xIPath;
279     xPath->getByName(CFGPROP_INTERNALPATHES) >>= xIPath;
280     aPathVal.lInternalPaths << xIPath->getElementNames();
281 
282     // read user defined path list
283     aPathVal.lUserPaths << xPath->getByName(CFGPROP_USERPATHES);
284 
285     // read the writeable path
286     xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath;
287 
288     // read state props
289     xPath->getByName(CFGPROP_ISSINGLEPATH) >>= aPathVal.bIsSinglePath;
290 
291     // analyze finalized/mandatory states
292     aPathVal.bIsReadonly = sal_False;
293     css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY);
294     if (xInfo.is())
295     {
296         css::beans::Property aInfo = xInfo->getAsProperty();
297         sal_Bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY );
298         //sal_Bool bMandatory = ((aInfo.Attributes & css::beans::PropertyAttribute::REMOVEABLE) != css::beans::PropertyAttribute::REMOVEABLE);
299 
300         // Note: Till we support finalized / mandatory on our API more in detail we handle
301         // all states simple as READONLY ! But because all really needed paths are "mandatory" by default
302         // we have to handle "finalized" as the real "readonly" indicator .
303         aPathVal.bIsReadonly = bFinalized;
304     }
305 
306     return aPathVal;
307 }
308 
309 //-----------------------------------------------------------------------------
310 void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath)
311 {
312     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_storePath" );
313     m_bIgnoreEvents = sal_True;
314 
315     css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew();
316     css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld();
317 
318     // try to replace path-parts with well known and supported variables.
319     // So an office can be moved easily to another location without losing
320     // its related paths.
321     PathInfo aResubstPath(aPath);
322     impl_subst(aResubstPath, sal_True);
323 
324     // update new configuration
325     if (! aResubstPath.bIsSinglePath)
326     {
327         ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
328                                                             aResubstPath.sPathName,
329                                                             CFGPROP_USERPATHES,
330                                                             css::uno::makeAny(aResubstPath.lUserPaths.getAsConstList()));
331     }
332 
333     ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
334                                                         aResubstPath.sPathName,
335                                                         CFGPROP_WRITEPATH,
336                                                         css::uno::makeAny(aResubstPath.sWritePath));
337 
338     ::comphelper::ConfigurationHelper::flush(xCfgNew);
339 
340     // remove the whole path from the old configuration !
341     // Otherwise we can't make sure that the diff between new and old configuration
342     // on loading time really represent an user setting !!!
343 
344     // Check if the given path exists inside the old configuration.
345     // Because our new configuration knows more then the list of old paths ... !
346     if (xCfgOld->hasByName(aResubstPath.sPathName))
347     {
348         css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW);
349         xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any());
350         ::comphelper::ConfigurationHelper::flush(xCfgOld);
351     }
352 
353     m_bIgnoreEvents = sal_False;
354 }
355 
356 //-----------------------------------------------------------------------------
357 #ifdef MIGRATE_OLD_USER_PATHES
358 void PathSettings::impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
359                                           const OUStringList&           lOld )
360 {
361     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_mergeOldUserPaths" );
362     OUStringList::const_iterator pIt;
363     for (  pIt  = lOld.begin();
364            pIt != lOld.end()  ;
365          ++pIt                )
366     {
367         const ::rtl::OUString& sOld = *pIt;
368 
369         if (rPath.bIsSinglePath)
370         {
371             LOG_ASSERT2(lOld.size()>1, "PathSettings::impl_mergeOldUserPaths()", "Single path has more then one path value inside old configuration (Common.xcu)!")
372             if (! rPath.sWritePath.equals(sOld))
373                rPath.sWritePath = sOld;
374         }
375         else
376         {
377             if (
378                 (  rPath.lInternalPaths.findConst(sOld) == rPath.lInternalPaths.end()) &&
379                 (  rPath.lUserPaths.findConst(sOld)     == rPath.lUserPaths.end()    ) &&
380                 (! rPath.sWritePath.equals(sOld)                                     )
381                )
382                rPath.lUserPaths.push_back(sOld);
383         }
384     }
385 }
386 #endif // MIGRATE_OLD_USER_PATHES
387 
388 //-----------------------------------------------------------------------------
389 PathSettings::EChangeOp PathSettings::impl_updatePath(const ::rtl::OUString& sPath          ,
390                                                             sal_Bool         bNotifyListener)
391 {
392     // SAFE ->
393     WriteGuard aWriteLock(m_aLock);
394 
395     PathSettings::PathInfo* pPathOld = 0;
396     PathSettings::PathInfo* pPathNew = 0;
397     PathSettings::EChangeOp eOp      = PathSettings::E_UNDEFINED;
398     PathSettings::PathInfo  aPath;
399 
400     try
401     {
402         aPath = impl_readNewFormat(sPath);
403         aPath.sPathName = sPath;
404         // replace all might existing variables with real values
405         // Do it before these old paths will be compared against the
406         // new path configuration. Otherwise some strings uses different variables ... but substitution
407         // will produce strings with same content (because some variables are redundant!)
408         impl_subst(aPath, sal_False);
409     }
410     catch(const css::uno::RuntimeException& exRun)
411         { throw exRun; }
412     catch(const css::container::NoSuchElementException&)
413         { eOp = PathSettings::E_REMOVED; }
414     catch(const css::uno::Exception& exAny)
415         { throw exAny; }
416 
417     #ifdef MIGRATE_OLD_USER_PATHES
418     try
419     {
420         // migration of old user defined values on demand
421         // can be disabled for a new major
422         OUStringList lOldVals = impl_readOldFormat(sPath);
423         // replace all might existing variables with real values
424         // Do it before these old paths will be compared against the
425         // new path configuration. Otherwise some strings uses different variables ... but substitution
426         // will produce strings with same content (because some variables are redundant!)
427         impl_subst(lOldVals, fa_getSubstitution(), sal_False);
428         impl_mergeOldUserPaths(aPath, lOldVals);
429     }
430     catch(const css::uno::RuntimeException& exRun)
431         { throw exRun; }
432     // Normal(!) exceptions can be ignored!
433     // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation
434     // we can't find a value for it inside the "old" configuration. So a NoSuchElementException
435     // will be normal .-)
436     catch(const css::uno::Exception&)
437         {}
438     #endif // MIGRATE_OLD_USER_PATHES
439 
440     PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath);
441     if (eOp == PathSettings::E_UNDEFINED)
442     {
443         if (pPath != m_lPaths.end())
444             eOp = PathSettings::E_CHANGED;
445         else
446             eOp = PathSettings::E_ADDED;
447     }
448 
449     switch(eOp)
450     {
451         case PathSettings::E_ADDED :
452              {
453                 if (bNotifyListener)
454                 {
455                     pPathOld = 0;
456                     pPathNew = &aPath;
457                     impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew);
458                 }
459                 m_lPaths[sPath] = aPath;
460              }
461              break;
462 
463         case PathSettings::E_CHANGED :
464              {
465                 if (bNotifyListener)
466                 {
467                     pPathOld = &(pPath->second);
468                     pPathNew = &aPath;
469                     impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew);
470                 }
471                 m_lPaths[sPath] = aPath;
472              }
473              break;
474 
475         case PathSettings::E_REMOVED :
476              {
477                 if (pPath != m_lPaths.end())
478                 {
479                     if (bNotifyListener)
480                     {
481                         pPathOld = &(pPath->second);
482                         pPathNew = 0;
483                         impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew);
484                     }
485                     m_lPaths.erase(pPath);
486                 }
487              }
488              break;
489 
490         default: // to let compiler be happy
491              break;
492     }
493 
494     return eOp;
495 }
496 
497 //-----------------------------------------------------------------------------
498 css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(const ::rtl::OUString& sPath)
499 {
500     ::rtl::OUString sOldStyleProp = sPath;
501     ::rtl::OUString sInternalProp = sPath+POSTFIX_INTERNAL_PATHES;
502     ::rtl::OUString sUserProp     = sPath+POSTFIX_USER_PATHES;
503     ::rtl::OUString sWriteProp    = sPath+POSTFIX_WRITE_PATH;
504 
505     // Attention: The default set of IDs is fix and must follow these schema.
506     // Otherwise the outside code ant work for new added properties.
507     // Why ?
508     // The outside code must fire N events for every changed property.
509     // And the knowing about packaging of variables of the structure PathInfo
510     // follow these group IDs ! But if such ID isn't in the range of [0..IDGROUP_COUNT]
511     // the outside can't determine the right group ... and can't fire the right events .-)
512 
513     css::uno::Sequence< sal_Int32 > lIDs(IDGROUP_COUNT);
514     lIDs[0] = IDGROUP_OLDSTYLE       ;
515     lIDs[1] = IDGROUP_INTERNAL_PATHES;
516     lIDs[2] = IDGROUP_USER_PATHES    ;
517     lIDs[3] = IDGROUP_WRITE_PATH     ;
518 
519     sal_Int32 c = m_lPropDesc.getLength();
520     sal_Int32 i = 0;
521     for (i=0; i<c; ++i)
522     {
523         const css::beans::Property& rProp = m_lPropDesc[i];
524 
525         if (rProp.Name.equals(sOldStyleProp))
526             lIDs[IDGROUP_OLDSTYLE] = rProp.Handle;
527         else
528         if (rProp.Name.equals(sInternalProp))
529             lIDs[IDGROUP_INTERNAL_PATHES] = rProp.Handle;
530         else
531         if (rProp.Name.equals(sUserProp))
532             lIDs[IDGROUP_USER_PATHES] = rProp.Handle;
533         else
534         if (rProp.Name.equals(sWriteProp))
535             lIDs[IDGROUP_WRITE_PATH] = rProp.Handle;
536     }
537 
538     return lIDs;
539 }
540 
541 //-----------------------------------------------------------------------------
542 void PathSettings::impl_notifyPropListener(      PathSettings::EChangeOp /*eOp*/     ,
543                                            const ::rtl::OUString&        sPath   ,
544                                            const PathSettings::PathInfo* pPathOld,
545                                            const PathSettings::PathInfo* pPathNew)
546 {
547     css::uno::Sequence< sal_Int32 >     lHandles(1);
548     css::uno::Sequence< css::uno::Any > lOldVals(1);
549     css::uno::Sequence< css::uno::Any > lNewVals(1);
550 
551     css::uno::Sequence< sal_Int32 > lIDs   = impl_mapPathName2IDList(sPath);
552     sal_Int32                       c      = lIDs.getLength();
553     sal_Int32                       i      = 0;
554     sal_Int32                       nMaxID = m_lPropDesc.getLength()-1;
555     for (i=0; i<c; ++i)
556     {
557         sal_Int32 nID = lIDs[i];
558 
559         if (
560             (nID < 0     ) ||
561             (nID > nMaxID)
562            )
563            continue;
564 
565         lHandles[0] = nID;
566         switch(impl_getPropGroup(nID))
567         {
568             case IDGROUP_OLDSTYLE :
569                  {
570                     if (pPathOld)
571                     {
572                         ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPathOld);
573                         lOldVals[0] <<= sVal;
574                     }
575                     if (pPathNew)
576                     {
577                         ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPathNew);
578                         lNewVals[0] <<= sVal;
579                     }
580                  }
581                  break;
582 
583             case IDGROUP_INTERNAL_PATHES :
584                  {
585                     if (pPathOld)
586                         lOldVals[0] <<= pPathOld->lInternalPaths.getAsConstList();
587                     if (pPathNew)
588                         lNewVals[0] <<= pPathNew->lInternalPaths.getAsConstList();
589                  }
590                  break;
591 
592             case IDGROUP_USER_PATHES :
593                  {
594                     if (pPathOld)
595                         lOldVals[0] <<= pPathOld->lUserPaths.getAsConstList();
596                     if (pPathNew)
597                         lNewVals[0] <<= pPathNew->lUserPaths.getAsConstList();
598                  }
599                  break;
600 
601             case IDGROUP_WRITE_PATH :
602                  {
603                     if (pPathOld)
604                         lOldVals[0] <<= pPathOld->sWritePath;
605                     if (pPathNew)
606                         lNewVals[0] <<= pPathNew->sWritePath;
607                  }
608                  break;
609         }
610 
611         fire(lHandles.getArray(),
612              lNewVals.getArray(),
613              lOldVals.getArray(),
614              1,
615              sal_False);
616     }
617 }
618 
619 //-----------------------------------------------------------------------------
620 void PathSettings::impl_subst(      OUStringList&                                          lVals   ,
621                               const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
622                                     sal_Bool                                               bReSubst)
623 {
624     OUStringList::iterator pIt;
625 
626     for (  pIt  = lVals.begin();
627            pIt != lVals.end()  ;
628          ++pIt                 )
629     {
630         const ::rtl::OUString& sOld = *pIt;
631               ::rtl::OUString  sNew ;
632         if (bReSubst)
633             sNew = xSubst->reSubstituteVariables(sOld);
634         else
635             sNew = xSubst->substituteVariables(sOld, sal_False);
636 
637         *pIt = sNew;
638     }
639 }
640 
641 //-----------------------------------------------------------------------------
642 void PathSettings::impl_subst(PathSettings::PathInfo& aPath   ,
643                               sal_Bool                bReSubst)
644 {
645     css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution();
646 
647     impl_subst(aPath.lInternalPaths, xSubst, bReSubst);
648     impl_subst(aPath.lUserPaths    , xSubst, bReSubst);
649     if (bReSubst)
650         aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath);
651     else
652         aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, sal_False);
653 }
654 
655 //-----------------------------------------------------------------------------
656 ::rtl::OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const
657 {
658     OUStringList::const_iterator pIt;
659     OUStringList                 lTemp;
660     lTemp.reserve(rPath.lInternalPaths.size() + rPath.lUserPaths.size() + 1);
661 
662     for (  pIt  = rPath.lInternalPaths.begin();
663            pIt != rPath.lInternalPaths.end()  ;
664          ++pIt                                 )
665     {
666         lTemp.push_back(*pIt);
667     }
668     for (  pIt  = rPath.lUserPaths.begin();
669            pIt != rPath.lUserPaths.end()  ;
670          ++pIt                             )
671     {
672         lTemp.push_back(*pIt);
673     }
674 
675     if (rPath.sWritePath.getLength() > 0)
676         lTemp.push_back(rPath.sWritePath);
677 
678     ::rtl::OUStringBuffer sPathVal(256);
679     for (  pIt  = lTemp.begin();
680            pIt != lTemp.end()  ;
681                                )
682     {
683         sPathVal.append(*pIt);
684         ++pIt;
685         if (pIt != lTemp.end())
686             sPathVal.appendAscii(";");
687     }
688 
689     return sPathVal.makeStringAndClear();
690 }
691 
692 //-----------------------------------------------------------------------------
693 OUStringList PathSettings::impl_convertOldStyle2Path(const ::rtl::OUString& sOldStylePath) const
694 {
695     OUStringList lList;
696     sal_Int32    nToken = 0;
697     do
698     {
699         ::rtl::OUString sToken = sOldStylePath.getToken(0, ';', nToken);
700         if (sToken.getLength())
701             lList.push_back(sToken);
702     }
703     while(nToken >= 0);
704 
705     return lList;
706 }
707 
708 //-----------------------------------------------------------------------------
709 void PathSettings::impl_purgeKnownPaths(const PathSettings::PathInfo& rPath,
710                                                OUStringList&           lList)
711 {
712     OUStringList::const_iterator pIt;
713     for (  pIt  = rPath.lInternalPaths.begin();
714            pIt != rPath.lInternalPaths.end()  ;
715          ++pIt                                 )
716     {
717         const ::rtl::OUString& rItem = *pIt;
718         OUStringList::iterator pItem = lList.find(rItem);
719         if (pItem != lList.end())
720             lList.erase(pItem);
721     }
722     for (  pIt  = rPath.lUserPaths.begin();
723            pIt != rPath.lUserPaths.end()  ;
724          ++pIt                             )
725     {
726         const ::rtl::OUString& rItem = *pIt;
727         OUStringList::iterator pItem = lList.find(rItem);
728         if (pItem != lList.end())
729             lList.erase(pItem);
730     }
731 
732     OUStringList::iterator pItem = lList.find(rPath.sWritePath);
733     if (pItem != lList.end())
734         lList.erase(pItem);
735 }
736 
737 //-----------------------------------------------------------------------------
738 void PathSettings::impl_rebuildPropertyDescriptor()
739 {
740     // SAFE ->
741     WriteGuard aWriteLock(m_aLock);
742 
743     sal_Int32 c = (sal_Int32)m_lPaths.size();
744     sal_Int32 i = 0;
745     m_lPropDesc.realloc(c*IDGROUP_COUNT);
746 
747     PathHash::const_iterator pIt;
748     for (  pIt  = m_lPaths.begin();
749            pIt != m_lPaths.end()  ;
750          ++pIt                     )
751     {
752         const PathSettings::PathInfo& rPath = pIt->second;
753               css::beans::Property*   pProp = 0;
754 
755         pProp             = &(m_lPropDesc[i]);
756         pProp->Name       = rPath.sPathName;
757         pProp->Handle     = i;
758         pProp->Type       = ::getCppuType((::rtl::OUString*)0);
759         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
760         if (rPath.bIsReadonly)
761             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
762         ++i;
763 
764         pProp             = &(m_lPropDesc[i]);
765         pProp->Name       = rPath.sPathName+POSTFIX_INTERNAL_PATHES;
766         pProp->Handle     = i;
767         pProp->Type       = ::getCppuType((css::uno::Sequence< ::rtl::OUString >*)0);
768         pProp->Attributes = css::beans::PropertyAttribute::BOUND   |
769                             css::beans::PropertyAttribute::READONLY;
770         ++i;
771 
772         pProp             = &(m_lPropDesc[i]);
773         pProp->Name       = rPath.sPathName+POSTFIX_USER_PATHES;
774         pProp->Handle     = i;
775         pProp->Type       = ::getCppuType((css::uno::Sequence< ::rtl::OUString >*)0);
776         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
777         if (rPath.bIsReadonly)
778             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
779         ++i;
780 
781         pProp             = &(m_lPropDesc[i]);
782         pProp->Name       = rPath.sPathName+POSTFIX_WRITE_PATH;
783         pProp->Handle     = i;
784         pProp->Type       = ::getCppuType((::rtl::OUString*)0);
785         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
786         if (rPath.bIsReadonly)
787             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
788         ++i;
789     }
790 
791     if (m_pPropHelp)
792        delete m_pPropHelp;
793     m_pPropHelp = new ::cppu::OPropertyArrayHelper(m_lPropDesc, sal_False); // false => not sorted ... must be done inside helper
794 
795     aWriteLock.unlock();
796     // <- SAFE
797 }
798 
799 //-----------------------------------------------------------------------------
800 css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const
801 {
802     const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID);
803     if (! pPath)
804         throw css::container::NoSuchElementException();
805 
806     css::uno::Any aVal;
807     switch(impl_getPropGroup(nID))
808     {
809         case IDGROUP_OLDSTYLE :
810              {
811                 ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPath);
812                 aVal <<= sVal;
813              }
814              break;
815 
816         case IDGROUP_INTERNAL_PATHES :
817              {
818                 aVal <<= pPath->lInternalPaths.getAsConstList();
819              }
820              break;
821 
822         case IDGROUP_USER_PATHES :
823              {
824                 aVal <<= pPath->lUserPaths.getAsConstList();
825              }
826              break;
827 
828         case IDGROUP_WRITE_PATH :
829              {
830                 aVal <<= pPath->sWritePath;
831              }
832              break;
833     }
834 
835     return aVal;
836 }
837 
838 //-----------------------------------------------------------------------------
839 void PathSettings::impl_setPathValue(      sal_Int32      nID ,
840                                      const css::uno::Any& aVal)
841 {
842     PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID);
843     if (! pOrgPath)
844         throw css::container::NoSuchElementException();
845 
846     // We work on a copied path ... so we can be sure that errors during this operation
847     // does not make our internal cache invalid .-)
848     PathSettings::PathInfo aChangePath(*pOrgPath);
849 
850     switch(impl_getPropGroup(nID))
851     {
852         case IDGROUP_OLDSTYLE :
853              {
854                 ::rtl::OUString sVal;
855                 aVal >>= sVal;
856                 OUStringList lList = impl_convertOldStyle2Path(sVal);
857                 impl_subst(lList, fa_getSubstitution(), sal_False);
858                 impl_purgeKnownPaths(aChangePath, lList);
859                 if (! impl_isValidPath(lList))
860                     throw css::lang::IllegalArgumentException();
861 
862                 if (aChangePath.bIsSinglePath)
863                 {
864                     LOG_ASSERT2(lList.size()>1, "PathSettings::impl_setPathValue()", "You try to set more then path value for a defined SINGLE_PATH!")
865                     if ( !lList.empty() )
866                         aChangePath.sWritePath = *(lList.begin());
867                     else
868                         aChangePath.sWritePath = ::rtl::OUString();
869                 }
870                 else
871                 {
872                     OUStringList::const_iterator pIt;
873                     for (  pIt  = lList.begin();
874                            pIt != lList.end()  ;
875                          ++pIt                 )
876                     {
877                         aChangePath.lUserPaths.push_back(*pIt);
878                     }
879                 }
880              }
881              break;
882 
883         case IDGROUP_INTERNAL_PATHES :
884              {
885                 if (aChangePath.bIsSinglePath)
886                 {
887                     ::rtl::OUStringBuffer sMsg(256);
888                     sMsg.appendAscii("The path '"    );
889                     sMsg.append     (aChangePath.sPathName);
890                     sMsg.appendAscii("' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.");
891                     throw css::uno::Exception(sMsg.makeStringAndClear(),
892                                               static_cast< ::cppu::OWeakObject* >(this));
893                 }
894 
895                 OUStringList lList;
896                 lList << aVal;
897                 if (! impl_isValidPath(lList))
898                     throw css::lang::IllegalArgumentException();
899                 aChangePath.lInternalPaths = lList;
900              }
901              break;
902 
903         case IDGROUP_USER_PATHES :
904              {
905                 if (aChangePath.bIsSinglePath)
906                 {
907                     ::rtl::OUStringBuffer sMsg(256);
908                     sMsg.appendAscii("The path '"    );
909                     sMsg.append     (aChangePath.sPathName);
910                     sMsg.appendAscii("' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.");
911                     throw css::uno::Exception(sMsg.makeStringAndClear(),
912                                               static_cast< ::cppu::OWeakObject* >(this));
913                 }
914 
915                 OUStringList lList;
916                 lList << aVal;
917                 if (! impl_isValidPath(lList))
918                     throw css::lang::IllegalArgumentException();
919                 aChangePath.lUserPaths = lList;
920              }
921              break;
922 
923         case IDGROUP_WRITE_PATH :
924              {
925                 ::rtl::OUString sVal;
926                 aVal >>= sVal;
927                 if (! impl_isValidPath(sVal))
928                     throw css::lang::IllegalArgumentException();
929                 aChangePath.sWritePath = sVal;
930              }
931              break;
932     }
933 
934     // TODO check if path has at least one path value set
935     // At least it depends from the feature using this path, if an empty path list is allowed.
936     /*
937     if (impl_isPathEmpty(aChangePath))
938     {
939         ::rtl::OUStringBuffer sMsg(256);
940         sMsg.appendAscii("The path '"    );
941         sMsg.append     (aChangePath.sPathName);
942         sMsg.appendAscii("' is empty now ... Not a real good idea.");
943         throw css::uno::Exception(sMsg.makeStringAndClear(),
944                                   static_cast< ::cppu::OWeakObject* >(this));
945     }
946     */
947 
948     // first we should try to store the changed (copied!) path ...
949     // In case an error occurs on saving time an exception is thrown ...
950     // If no exception occurs we can update our internal cache (means
951     // we can overwrite pOrgPath !
952     impl_storePath(aChangePath);
953     pOrgPath->takeOver(aChangePath);
954 }
955 
956 //-----------------------------------------------------------------------------
957 sal_Bool PathSettings::impl_isValidPath(const OUStringList& lPath) const
958 {
959     OUStringList::const_iterator pIt;
960     for (  pIt  = lPath.begin();
961            pIt != lPath.end()  ;
962          ++pIt                 )
963     {
964         const ::rtl::OUString& rVal = *pIt;
965         if (! impl_isValidPath(rVal))
966             return sal_False;
967     }
968 
969     return sal_True;
970 }
971 
972 //-----------------------------------------------------------------------------
973 sal_Bool PathSettings::impl_isValidPath(const ::rtl::OUString& sPath) const
974 {
975     // allow empty path to reset a path.
976 // idea by LLA to support empty paths
977 //  if (sPath.getLength() == 0)
978 //  {
979 //      return sal_True;
980 //  }
981 
982     return (! INetURLObject(sPath).HasError());
983 }
984 
985 //-----------------------------------------------------------------------------
986 ::rtl::OUString impl_extractBaseFromPropName(const ::rtl::OUString& sPropName)
987 {
988     sal_Int32 i = -1;
989 
990     i = sPropName.indexOf(POSTFIX_INTERNAL_PATHES);
991     if (i > -1)
992         return sPropName.copy(0, i);
993     i = sPropName.indexOf(POSTFIX_USER_PATHES);
994     if (i > -1)
995         return sPropName.copy(0, i);
996     i = sPropName.indexOf(POSTFIX_WRITE_PATH);
997     if (i > -1)
998         return sPropName.copy(0, i);
999 
1000     return sPropName;
1001 }
1002 
1003 //-----------------------------------------------------------------------------
1004 PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle)
1005 {
1006     // SAFE ->
1007     ReadGuard aReadLock(m_aLock);
1008 
1009     if (nHandle > (m_lPropDesc.getLength()-1))
1010         return 0;
1011 
1012     const css::beans::Property&            rProp = m_lPropDesc[nHandle];
1013           ::rtl::OUString                  sProp = impl_extractBaseFromPropName(rProp.Name);
1014           PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp);
1015 
1016     if (rPath != m_lPaths.end())
1017        return &(rPath->second);
1018 
1019     return 0;
1020     // <- SAFE
1021 }
1022 
1023 //-----------------------------------------------------------------------------
1024 const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const
1025 {
1026     // SAFE ->
1027     ReadGuard aReadLock(m_aLock);
1028 
1029     if (nHandle > (m_lPropDesc.getLength()-1))
1030         return 0;
1031 
1032     const css::beans::Property&                  rProp = m_lPropDesc[nHandle];
1033           ::rtl::OUString                        sProp = impl_extractBaseFromPropName(rProp.Name);
1034           PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp);
1035 
1036     if (rPath != m_lPaths.end())
1037        return &(rPath->second);
1038 
1039     return 0;
1040     // <- SAFE
1041 }
1042 
1043 //-----------------------------------------------------------------------------
1044 sal_Bool SAL_CALL PathSettings::convertFastPropertyValue(      css::uno::Any& aConvertedValue,
1045                                                                css::uno::Any& aOldValue      ,
1046                                                                sal_Int32      nHandle        ,
1047                                                          const css::uno::Any& aValue         )
1048     throw(css::lang::IllegalArgumentException)
1049 {
1050     // throws NoSuchElementException !
1051     css::uno::Any aCurrentVal = impl_getPathValue(nHandle);
1052 
1053     return PropHelper::willPropertyBeChanged(
1054                 aCurrentVal,
1055                 aValue,
1056                 aOldValue,
1057                 aConvertedValue);
1058 }
1059 
1060 //-----------------------------------------------------------------------------
1061 void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast(      sal_Int32      nHandle,
1062                                                              const css::uno::Any& aValue )
1063     throw(css::uno::Exception)
1064 {
1065     // throws NoSuchElement- and IllegalArgumentException !
1066     impl_setPathValue(nHandle, aValue);
1067 }
1068 
1069 //-----------------------------------------------------------------------------
1070 void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue ,
1071                                                  sal_Int32      nHandle) const
1072 {
1073     aValue = impl_getPathValue(nHandle);
1074 }
1075 
1076 //-----------------------------------------------------------------------------
1077 ::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper()
1078 {
1079     return *m_pPropHelp;
1080 }
1081 
1082 //-----------------------------------------------------------------------------
1083 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo()
1084     throw(css::uno::RuntimeException)
1085 {
1086     return css::uno::Reference< css::beans::XPropertySetInfo >(createPropertySetInfo(getInfoHelper()));
1087 }
1088 
1089 //-----------------------------------------------------------------------------
1090 css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution()
1091 {
1092     // SAFE ->
1093     ReadGuard aReadLock(m_aLock);
1094     css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR  = m_xSMGR;
1095     css::uno::Reference< css::util::XStringSubstitution >  xSubst = m_xSubstitution;
1096     aReadLock.unlock();
1097     // <- SAFE
1098 
1099     if (! xSubst.is())
1100     {
1101         // create the needed substitution service.
1102         // We must replace all used variables inside read path values.
1103         // In case we can't do so ... the whole office can't work really.
1104         // That's why it seems to be OK to throw a RuntimeException then.
1105         xSubst = css::uno::Reference< css::util::XStringSubstitution >(
1106                                 xSMGR->createInstance(SERVICENAME_SUBSTITUTEPATHVARIABLES),
1107                                 css::uno::UNO_QUERY_THROW);
1108 
1109         // SAFE ->
1110         WriteGuard aWriteLock(m_aLock);
1111         m_xSubstitution = xSubst;
1112         aWriteLock.unlock();
1113     }
1114 
1115     return xSubst;
1116 }
1117 
1118 //-----------------------------------------------------------------------------
1119 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld()
1120 {
1121     const static ::rtl::OUString CFG_NODE_OLD = ::rtl::OUString::createFromAscii("org.openoffice.Office.Common/Path/Current");
1122 
1123     // SAFE ->
1124     ReadGuard aReadLock(m_aLock);
1125     css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1126     css::uno::Reference< css::container::XNameAccess >     xCfg  = m_xCfgOld;
1127     aReadLock.unlock();
1128     // <- SAFE
1129 
1130     if (! xCfg.is())
1131     {
1132         xCfg = css::uno::Reference< css::container::XNameAccess >(
1133                    ::comphelper::ConfigurationHelper::openConfig(
1134                         xSMGR,
1135                         CFG_NODE_OLD,
1136                         ::comphelper::ConfigurationHelper::E_STANDARD), // not readonly! Sometimes we need write access there !!!
1137                    css::uno::UNO_QUERY_THROW);
1138 
1139         // SAFE ->
1140         WriteGuard aWriteLock(m_aLock);
1141         m_xCfgOld = xCfg;
1142         aWriteLock.unlock();
1143     }
1144 
1145     return xCfg;
1146 }
1147 
1148 //-----------------------------------------------------------------------------
1149 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew()
1150 {
1151     const static ::rtl::OUString CFG_NODE_NEW = ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths/Paths");
1152 
1153     // SAFE ->
1154     ReadGuard aReadLock(m_aLock);
1155     css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1156     css::uno::Reference< css::container::XNameAccess >     xCfg  = m_xCfgNew;
1157     aReadLock.unlock();
1158     // <- SAFE
1159 
1160     if (! xCfg.is())
1161     {
1162         xCfg = css::uno::Reference< css::container::XNameAccess >(
1163                    ::comphelper::ConfigurationHelper::openConfig(
1164                         xSMGR,
1165                         CFG_NODE_NEW,
1166                         ::comphelper::ConfigurationHelper::E_STANDARD),
1167                    css::uno::UNO_QUERY_THROW);
1168 
1169         // SAFE ->
1170         WriteGuard aWriteLock(m_aLock);
1171         m_xCfgNew = xCfg;
1172         aWriteLock.unlock();
1173 
1174         css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW);
1175         xBroadcaster->addChangesListener(static_cast< css::util::XChangesListener* >(this));
1176     }
1177 
1178     return xCfg;
1179 }
1180 
1181 } // namespace framework
1182 
1183 /* vim: set noet sw=4 ts=4: */
1184