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