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