/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <sal/config.h>
 
#include <string_view>
#include <utility>
#include <unordered_map>
 
#include <properties.h>
#include <helper/mischelper.hxx>
 
#include <com/sun/star/beans/Property.hpp>
#include <com/sun/star/beans/XProperty.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/util/XChangesNotifier.hpp>
#include <com/sun/star/util/PathSubstitution.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/util/XStringSubstitution.hpp>
#include <com/sun/star/util/XChangesListener.hpp>
#include <com/sun/star/util/XPathSettings.hpp>
 
#include <tools/urlobj.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ref.hxx>
#include <sal/log.hxx>
 
#include <cppuhelper/basemutex.hxx>
#include <cppuhelper/propshlp.hxx>
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/configurationhelper.hxx>
#include <unotools/configpaths.hxx>
#include <o3tl/string_view.hxx>
 
using namespace framework;
 
constexpr OUString CFGPROP_USERPATHS = u"UserPaths"_ustr;
constexpr OUString CFGPROP_WRITEPATH = u"WritePath"_ustr;
 
/*
    0 : old style              "Template"              string using ";" as separator
    1 : internal paths         "Template_internal"     string list
    2 : user paths             "Template_user"         string list
    3 : write path             "Template_write"        string
 */
 
constexpr OUString POSTFIX_INTERNAL_PATHS = u"_internal"_ustr;
constexpr OUString POSTFIX_USER_PATHS = u"_user"_ustr;
constexpr OUString POSTFIX_WRITE_PATH = u"_writable"_ustr;
 
namespace {
 
const sal_Int32 IDGROUP_OLDSTYLE        = 0;
const sal_Int32 IDGROUP_INTERNAL_PATHS = 1;
const sal_Int32 IDGROUP_USER_PATHS     = 2;
const sal_Int32 IDGROUP_WRITE_PATH      = 3;
 
const sal_Int32 IDGROUP_COUNT           = 4;
 
sal_Int32 impl_getPropGroup(sal_Int32 nID)
{
    return (nID % IDGROUP_COUNT);
}
 
/* enable it if you wish to migrate old user settings (using the old cfg schema) on demand...
   disable it in case only the new schema must be used.
 */
 
typedef ::cppu::WeakComponentImplHelper<
            css::lang::XServiceInfo,
            css::util::XChangesListener,    // => XEventListener
            css::util::XPathSettings>       // => XPropertySet
                PathSettings_BASE;
 
class PathSettings : private cppu::BaseMutex
                   , public  PathSettings_BASE
                   , public  ::cppu::OPropertySetHelper
{
    struct PathInfo
    {
        public:
 
            PathInfo()
                : bIsSinglePath (false)
                , bIsReadonly   (false)
            {}
 
            /// an internal name describing this path
            OUString sPathName;
 
            /// contains all paths, which are used internally - but are not visible for the user.
            std::vector<OUString> lInternalPaths;
 
            /// contains all paths configured by the user
            std::vector<OUString> lUserPaths;
 
            /// this special path is used to generate feature depending content there
            OUString sWritePath;
 
            /// indicates real single paths, which uses WritePath property only
            bool bIsSinglePath;
 
            /// simple handling of finalized/mandatory states ... => we know one state READONLY only .-)
            bool bIsReadonly;
    };
 
    typedef std::unordered_map<OUString, PathSettings::PathInfo> PathHash;
 
    enum EChangeOp
    {
        E_UNDEFINED,
        E_ADDED,
        E_CHANGED,
        E_REMOVED
    };
 
private:
 
    /** reference to factory, which has create this instance. */
    css::uno::Reference< css::uno::XComponentContext > m_xContext;
 
    /** list of all path variables and her corresponding values. */
    PathSettings::PathHash m_lPaths;
 
    /** describes all properties available on our interface.
        Will be generated on demand based on our path list m_lPaths. */
    css::uno::Sequence< css::beans::Property > m_lPropDesc;
 
    /** helper needed to (re-)substitute all internal save path values. */
    css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;
 
    /** provides access to the old configuration schema (which will be migrated on demand). */
    css::uno::Reference< css::container::XNameAccess > m_xCfgOld;
 
    /** provides access to the new configuration schema. */
    css::uno::Reference< css::container::XNameAccess > m_xCfgNew;
 
    /** helper to listen for configuration changes without ownership cycle problems */
    rtl::Reference< WeakChangesListener > m_xCfgNewListener;
 
    std::unique_ptr<::cppu::OPropertyArrayHelper> m_pPropHelp;
 
public:
 
    /** initialize a new instance of this class.
        Attention: It's necessary for right function of this class, that the order of base
        classes is the right one. Because we transfer information from one base to another
        during this ctor runs! */
    explicit PathSettings(css::uno::Reference< css::uno::XComponentContext >  xContext);
 
    /** free all used resources ... if it was not already done. */
    virtual ~PathSettings() override;
 
    virtual OUString SAL_CALL getImplementationName() override
    {
        return u"com.sun.star.comp.framework.PathSettings"_ustr;
    }
 
    virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
    {
        return cppu::supportsService(this, ServiceName);
    }
 
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
    {
        return {u"com.sun.star.util.PathSettings"_ustr};
    }
 
    // XInterface
    virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
    virtual void SAL_CALL acquire() noexcept override
        { OWeakObject::acquire(); }
    virtual void SAL_CALL release() noexcept override
        { OWeakObject::release(); }
 
    // XTypeProvider
    virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes(  ) override;
 
    // css::util::XChangesListener
    virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
 
    // css::lang::XEventListener
    virtual void SAL_CALL disposing(const css::lang::EventObject& aSource) override;
 
    /**
     * XPathSettings attribute methods
     */
    virtual OUString SAL_CALL getAddin() override
        { return getStringProperty(u"Addin"_ustr); }
    virtual void SAL_CALL setAddin(const OUString& p1) override
        { setStringProperty(u"Addin"_ustr, p1); }
    virtual OUString SAL_CALL getAutoCorrect() override
        { return getStringProperty(u"AutoCorrect"_ustr); }
    virtual void SAL_CALL setAutoCorrect(const OUString& p1) override
        { setStringProperty(u"AutoCorrect"_ustr, p1); }
    virtual OUString SAL_CALL getAutoText() override
        { return getStringProperty(u"AutoText"_ustr); }
    virtual void SAL_CALL setAutoText(const OUString& p1) override
        { setStringProperty(u"AutoText"_ustr, p1); }
    virtual OUString SAL_CALL getBackup() override
        { return getStringProperty(u"Backup"_ustr); }
    virtual void SAL_CALL setBackup(const OUString& p1) override
        { setStringProperty(u"Backup"_ustr, p1); }
    virtual OUString SAL_CALL getBasic() override
        { return getStringProperty(u"Basic"_ustr); }
    virtual void SAL_CALL setBasic(const OUString& p1) override
        { setStringProperty(u"Basic"_ustr, p1); }
    virtual OUString SAL_CALL getBitmap() override
        { return getStringProperty(u"Bitmap"_ustr); }
    virtual void SAL_CALL setBitmap(const OUString& p1) override
        { setStringProperty(u"Bitmap"_ustr, p1); }
    virtual OUString SAL_CALL getConfig() override
        { return getStringProperty(u"Config"_ustr); }
    virtual void SAL_CALL setConfig(const OUString& p1) override
        { setStringProperty(u"Config"_ustr, p1); }
    virtual OUString SAL_CALL getDictionary() override
        { return getStringProperty(u"Dictionary"_ustr); }
    virtual void SAL_CALL setDictionary(const OUString& p1) override
        { setStringProperty(u"Dictionary"_ustr, p1); }
    virtual OUString SAL_CALL getFavorite() override
        { return getStringProperty(u"Favorite"_ustr); }
    virtual void SAL_CALL setFavorite(const OUString& p1) override
        { setStringProperty(u"Favorite"_ustr, p1); }
    virtual OUString SAL_CALL getFilter() override
        { return getStringProperty(u"Filter"_ustr); }
    virtual void SAL_CALL setFilter(const OUString& p1) override
        { setStringProperty(u"Filter"_ustr, p1); }
    virtual OUString SAL_CALL getGallery() override
        { return getStringProperty(u"Gallery"_ustr); }
    virtual void SAL_CALL setGallery(const OUString& p1) override
        { setStringProperty(u"Gallery"_ustr, p1); }
    virtual OUString SAL_CALL getGraphic() override
        { return getStringProperty(u"Graphic"_ustr); }
    virtual void SAL_CALL setGraphic(const OUString& p1) override
        { setStringProperty(u"Graphic"_ustr, p1); }
    virtual OUString SAL_CALL getHelp() override
        { return getStringProperty(u"Help"_ustr); }
    virtual void SAL_CALL setHelp(const OUString& p1) override
        { setStringProperty(u"Help"_ustr, p1); }
    virtual OUString SAL_CALL getLinguistic() override
        { return getStringProperty(u"Linguistic"_ustr); }
    virtual void SAL_CALL setLinguistic(const OUString& p1) override
        { setStringProperty(u"Linguistic"_ustr, p1); }
    virtual OUString SAL_CALL getModule() override
        { return getStringProperty(u"Module"_ustr); }
    virtual void SAL_CALL setModule(const OUString& p1) override
        { setStringProperty(u"Module"_ustr, p1); }
    virtual OUString SAL_CALL getPalette() override
        { return getStringProperty(u"Palette"_ustr); }
    virtual void SAL_CALL setPalette(const OUString& p1) override
        { setStringProperty(u"Palette"_ustr, p1); }
    virtual OUString SAL_CALL getPlugin() override
        { return getStringProperty(u"Plugin"_ustr); }
    virtual void SAL_CALL setPlugin(const OUString& p1) override
        { setStringProperty(u"Plugin"_ustr, p1); }
    virtual OUString SAL_CALL getStorage() override
        { return getStringProperty(u"Storage"_ustr); }
    virtual void SAL_CALL setStorage(const OUString& p1) override
        { setStringProperty(u"Storage"_ustr, p1); }
    virtual OUString SAL_CALL getTemp() override
        { return getStringProperty(u"Temp"_ustr); }
    virtual void SAL_CALL setTemp(const OUString& p1) override
        { setStringProperty(u"Temp"_ustr, p1); }
    virtual OUString SAL_CALL getTemplate() override
        { return getStringProperty(u"Template"_ustr); }
    virtual void SAL_CALL setTemplate(const OUString& p1) override
        { setStringProperty(u"Template"_ustr, p1); }
    virtual OUString SAL_CALL getUIConfig() override
        { return getStringProperty(u"UIConfig"_ustr); }
    virtual void SAL_CALL setUIConfig(const OUString& p1) override
        { setStringProperty(u"UIConfig"_ustr, p1); }
    virtual OUString SAL_CALL getUserConfig() override
        { return getStringProperty(u"UserConfig"_ustr); }
    virtual void SAL_CALL setUserConfig(const OUString& p1) override
        { setStringProperty(u"UserConfig"_ustr, p1); }
    virtual OUString SAL_CALL getUserDictionary() override
        { return getStringProperty(u"UserDictionary"_ustr); }
    virtual void SAL_CALL setUserDictionary(const OUString& p1) override
        { setStringProperty(u"UserDictionary"_ustr, p1); }
    virtual OUString SAL_CALL getWork() override
        { return getStringProperty(u"Work"_ustr); }
    virtual void SAL_CALL setWork(const OUString& p1) override
        { setStringProperty(u"Work"_ustr, p1); }
    virtual OUString SAL_CALL getBasePathShareLayer() override
        { return getStringProperty(u"UIConfig"_ustr); }
    virtual void SAL_CALL setBasePathShareLayer(const OUString& p1) override
        { setStringProperty(u"UIConfig"_ustr, p1); }
    virtual OUString SAL_CALL getBasePathUserLayer() override
        { return getStringProperty(u"UserConfig"_ustr); }
    virtual void SAL_CALL setBasePathUserLayer(const OUString& p1) override
        { setStringProperty(u"UserConfig"_ustr, p1); }
 
    /**
     * overrides to resolve inheritance ambiguity
     */
    virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
        { ::cppu::OPropertySetHelper::setPropertyValue(p1, p2); }
    virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
        { return ::cppu::OPropertySetHelper::getPropertyValue(p1); }
    virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
        { ::cppu::OPropertySetHelper::addPropertyChangeListener(p1, p2); }
    virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
        { ::cppu::OPropertySetHelper::removePropertyChangeListener(p1, p2); }
    virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
        { ::cppu::OPropertySetHelper::addVetoableChangeListener(p1, p2); }
    virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
        { ::cppu::OPropertySetHelper::removeVetoableChangeListener(p1, p2); }
    /** read all configured paths and create all needed internal structures. */
    void impl_readAll();
 
private:
    virtual void SAL_CALL disposing() final override;
 
    /// @throws css::uno::RuntimeException
    OUString getStringProperty(const OUString& p1);
 
    /// @throws css::uno::RuntimeException
    void setStringProperty(const OUString& p1, const OUString& p2);
 
    /** read a path info using the old cfg schema.
        This is needed for "migration on demand" reasons only.
        Can be removed for next major release .-) */
    std::vector<OUString> impl_readOldFormat(const OUString& sPath);
 
    /** read a path info using the new cfg schema. */
    PathSettings::PathInfo impl_readNewFormat(const OUString& sPath);
 
    /** filter "real user defined paths" from the old configuration schema
        and set it as UserPaths on the new schema.
        Can be removed with new major release ... */
    static void impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
                                 const std::vector<OUString>& lOld );
 
    /** reload one path directly from the new configuration schema (because
        it was updated by any external code) */
    PathSettings::EChangeOp impl_updatePath(const OUString& sPath          ,
                                                  bool         bNotifyListener);
 
    /** replace all might existing placeholder variables inside the given path ...
        or check if the given path value uses paths, which can be replaced with predefined
        placeholder variables ...
     */
    static void impl_subst(std::vector<OUString>& lVals   ,
                    const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
                          bool                                               bReSubst);
 
    void impl_subst(PathSettings::PathInfo& aPath   ,
                    bool                bReSubst);
 
    /** converts our new string list schema to the old ";" separated schema ... */
    static OUString impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath );
    static std::vector<OUString> impl_convertOldStyle2Path(std::u16string_view sOldStylePath);
 
    /** remove still known paths from the given lList argument.
        So real user defined paths can be extracted from the list of
        fix internal paths !
     */
    static void impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
                              std::vector<OUString>& lList);
 
    /** rebuild the member m_lPropDesc using the path list m_lPaths. */
    void impl_rebuildPropertyDescriptor();
 
    /** provides direct access to the list of path values
        using its internal property id.
     */
    css::uno::Any impl_getPathValue(      sal_Int32      nID ) const;
    void          impl_setPathValue(      sal_Int32      nID ,
                                    const css::uno::Any& aVal);
 
    /** check the given handle and return the corresponding PathInfo reference.
        These reference can be used then directly to manipulate these path. */
          PathSettings::PathInfo* impl_getPathAccess     (sal_Int32 nHandle);
    const PathSettings::PathInfo* impl_getPathAccessConst(sal_Int32 nHandle) const;
 
    /** it checks, if the given path value seems to be a valid URL or system path. */
    static bool impl_isValidPath(std::u16string_view sPath);
    static bool impl_isValidPath(const std::vector<OUString>& lPath);
 
    void impl_storePath(const PathSettings::PathInfo& aPath);
 
    css::uno::Sequence< sal_Int32 > impl_mapPathName2IDList(std::u16string_view sPath);
 
    void impl_notifyPropListener( std::u16string_view    sPath   ,
                                  const PathSettings::PathInfo* pPathOld,
                                  const PathSettings::PathInfo* pPathNew);
 
    //  OPropertySetHelper
    virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any&  aConvertedValue,
            css::uno::Any& aOldValue,
            sal_Int32 nHandle,
            const css::uno::Any& aValue ) override;
    virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
            const css::uno::Any&  aValue ) override;
    virtual void SAL_CALL getFastPropertyValue( css::uno::Any&  aValue,
            sal_Int32 nHandle ) const override;
    // Avoid:
    // warning: 'virtual css::uno::Any cppu::OPropertySetHelper::getFastPropertyValue(sal_Int32)' was hidden [-Woverloaded-virtual]
    // warning:   by ‘virtual void {anonymous}::PathSettings::getFastPropertyValue(css::uno::Any&, sal_Int32) const’ [-Woverloaded-virtual]
    using cppu::OPropertySetHelper::getFastPropertyValue;
    virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
    virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
 
    /** factory methods to guarantee right (but on demand) initialized members ... */
    css::uno::Reference< css::util::XStringSubstitution > fa_getSubstitution();
    css::uno::Reference< css::container::XNameAccess >    fa_getCfgOld();
    css::uno::Reference< css::container::XNameAccess >    fa_getCfgNew();
};
 
PathSettings::PathSettings( css::uno::Reference< css::uno::XComponentContext >  xContext )
    : PathSettings_BASE(m_aMutex)
    , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
    ,   m_xContext (std::move(xContext))
{
}
 
PathSettings::~PathSettings()
{
    disposing();
}
 
void SAL_CALL PathSettings::disposing()
{
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    css::uno::Reference< css::util::XChangesNotifier >
        xBroadcaster(m_xCfgNew, css::uno::UNO_QUERY);
    if (xBroadcaster.is())
        xBroadcaster->removeChangesListener(m_xCfgNewListener);
 
    m_xSubstitution.clear();
    m_xCfgOld.clear();
    m_xCfgNew.clear();
    m_xCfgNewListener.clear();
 
    m_pPropHelp.reset();
}
 
css::uno::Any SAL_CALL PathSettings::queryInterface( const css::uno::Type& _rType )
{
    css::uno::Any aRet = PathSettings_BASE::queryInterface( _rType );
    if ( !aRet.hasValue() )
        aRet = ::cppu::OPropertySetHelper::queryInterface( _rType );
    return aRet;
}
 
css::uno::Sequence< css::uno::Type > SAL_CALL PathSettings::getTypes(  )
{
    return comphelper::concatSequences(
        PathSettings_BASE::getTypes(),
        ::cppu::OPropertySetHelper::getTypes()
    );
}
 
void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent)
{
    sal_Int32 c                 = aEvent.Changes.getLength();
    sal_Int32 i                 = 0;
    bool  bUpdateDescriptor = false;
 
    for (i=0; i<c; ++i)
    {
        const css::util::ElementChange& aChange = aEvent.Changes[i];
 
        OUString sChanged;
        aChange.Accessor >>= sChanged;
 
        OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged);
        if (!sPath.isEmpty())
        {
            PathSettings::EChangeOp eOp = impl_updatePath(sPath, true);
            if (
                (eOp == PathSettings::E_ADDED  ) ||
                (eOp == PathSettings::E_REMOVED)
               )
                bUpdateDescriptor = true;
        }
    }
 
    if (bUpdateDescriptor)
        impl_rebuildPropertyDescriptor();
}
 
void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource)
{
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    if (aSource.Source == m_xCfgNew)
        m_xCfgNew.clear();
}
 
OUString PathSettings::getStringProperty(const OUString& p1)
{
    css::uno::Any a = ::cppu::OPropertySetHelper::getPropertyValue(p1);
    OUString s;
    a >>= s;
    return s;
}
 
void PathSettings::setStringProperty(const OUString& p1, const OUString& p2)
{
    ::cppu::OPropertySetHelper::setPropertyValue(p1, css::uno::Any(p2));
}
 
void PathSettings::impl_readAll()
{
    try
    {
        // TODO think about me
        css::uno::Reference< css::container::XNameAccess > xCfg    = fa_getCfgNew();
        css::uno::Sequence< OUString >              lPaths = xCfg->getElementNames();
 
        sal_Int32 c = lPaths.getLength();
        for (sal_Int32 i = 0; i < c; ++i)
        {
            const OUString& sPath = lPaths[i];
            impl_updatePath(sPath, false);
        }
    }
    catch(const css::uno::RuntimeException& )
    {
    }
 
    impl_rebuildPropertyDescriptor();
}
 
// NO substitution here ! It's done outside ...
std::vector<OUString> PathSettings::impl_readOldFormat(const OUString& sPath)
{
    css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() );
    std::vector<OUString> aPathVal;
 
    if( xCfg->hasByName(sPath) )
    {
        css::uno::Any aVal( xCfg->getByName(sPath) );
 
        OUString                       sStringVal;
        css::uno::Sequence< OUString > lStringListVal;
 
        if (aVal >>= sStringVal)
        {
            aPathVal.push_back(sStringVal);
        }
        else if (aVal >>= lStringListVal)
        {
            aPathVal = comphelper::sequenceToContainer<std::vector<OUString>>(lStringListVal);
        }
    }
 
    return aPathVal;
}
 
// NO substitution here ! It's done outside ...
PathSettings::PathInfo PathSettings::impl_readNewFormat(const OUString& sPath)
{
    css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
 
    // get access to the "queried" path
    css::uno::Reference< css::container::XNameAccess > xPath;
    xCfg->getByName(sPath) >>= xPath;
 
    PathSettings::PathInfo aPathVal;
 
    // read internal path list
    css::uno::Reference< css::container::XNameAccess > xIPath;
    xPath->getByName(u"InternalPaths"_ustr) >>= xIPath;
    aPathVal.lInternalPaths = comphelper::sequenceToContainer<std::vector<OUString>>(xIPath->getElementNames());
 
    // read user defined path list
    css::uno::Sequence<OUString> vTmpUserPathsSeq;
    xPath->getByName(CFGPROP_USERPATHS) >>= vTmpUserPathsSeq;
    aPathVal.lUserPaths = comphelper::sequenceToContainer<std::vector<OUString>>(vTmpUserPathsSeq);
 
    // read the writeable path
    xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath;
 
    // avoid duplicates, by removing the writeable path from
    // the user defined path list if it happens to be there too
    std::vector<OUString>::iterator aI = std::find(aPathVal.lUserPaths.begin(), aPathVal.lUserPaths.end(), aPathVal.sWritePath);
    if (aI != aPathVal.lUserPaths.end())
        aPathVal.lUserPaths.erase(aI);
 
    // read state props
    xPath->getByName(u"IsSinglePath"_ustr) >>= aPathVal.bIsSinglePath;
 
    // analyze finalized/mandatory states
    aPathVal.bIsReadonly = false;
    css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY);
    if (xInfo.is())
    {
        css::beans::Property aInfo = xInfo->getAsProperty();
        bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY  ) == css::beans::PropertyAttribute::READONLY  );
 
        // Note: 'till we support finalized/mandatory on our API more in detail we handle
        // all states simple as READONLY! But because all really needed paths are "mandatory" by default
        // we have to handle "finalized" as the real "readonly" indicator.
        aPathVal.bIsReadonly = bFinalized;
    }
 
    return aPathVal;
}
 
void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath)
{
    css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew();
    css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld();
 
    // try to replace path-parts with well known and supported variables.
    // So an office can be moved easily to another location without losing
    // its related paths.
    PathInfo aResubstPath(aPath);
    impl_subst(aResubstPath, true);
 
    // update new configuration
    if (! aResubstPath.bIsSinglePath)
    {
        ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
                                                            aResubstPath.sPathName,
                                                            CFGPROP_USERPATHS,
                            css::uno::Any(comphelper::containerToSequence(aResubstPath.lUserPaths)));
    }
 
    ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
                                                        aResubstPath.sPathName,
                                                        CFGPROP_WRITEPATH,
                                                        css::uno::Any(aResubstPath.sWritePath));
 
    ::comphelper::ConfigurationHelper::flush(xCfgNew);
 
    // remove the whole path from the old configuration!
    // Otherwise we can't make sure that the diff between new and old configuration
    // on loading time really represents a user setting!!!
 
    // Check if the given path exists inside the old configuration.
    // Because our new configuration knows more than the list of old paths ... !
    if (xCfgOld->hasByName(aResubstPath.sPathName))
    {
        css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW);
        xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any());
        ::comphelper::ConfigurationHelper::flush(xCfgOld);
    }
}
 
// static
void PathSettings::impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
                                          const std::vector<OUString>& lOld )
{
    for (auto const& old : lOld)
    {
        if (rPath.bIsSinglePath)
        {
            SAL_WARN_IF(lOld.size()>1, "fwk", "PathSettings::impl_mergeOldUserPaths(): Single path has more than one path value inside old configuration (Common.xcu)!");
            if ( rPath.sWritePath != old )
               rPath.sWritePath = old;
        }
        else
        {
            if (
                (  std::find(rPath.lInternalPaths.begin(), rPath.lInternalPaths.end(), old) == rPath.lInternalPaths.end()) &&
                (  std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), old)     == rPath.lUserPaths.end()    ) &&
                (  rPath.sWritePath != old                                     )
               )
               rPath.lUserPaths.push_back(old);
        }
    }
}
 
PathSettings::EChangeOp PathSettings::impl_updatePath(const OUString& sPath          ,
                                                            bool         bNotifyListener)
{
    // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    PathSettings::PathInfo* pPathOld = nullptr;
    PathSettings::PathInfo* pPathNew = nullptr;
    PathSettings::EChangeOp eOp      = PathSettings::E_UNDEFINED;
    PathSettings::PathInfo  aPath;
 
    try
    {
        aPath = impl_readNewFormat(sPath);
        aPath.sPathName = sPath;
        // replace all might existing variables with real values
        // Do it before these old paths will be compared against the
        // new path configuration. Otherwise some strings uses different variables ... but substitution
        // will produce strings with same content (because some variables are redundant!)
        impl_subst(aPath, false);
    }
    catch(const css::uno::RuntimeException&)
        { throw; }
    catch(const css::container::NoSuchElementException&)
        { eOp = PathSettings::E_REMOVED; }
    catch(const css::uno::Exception&)
        { throw; }
 
    try
    {
        // migration of old user defined values on demand
        // can be disabled for a new major
        std::vector<OUString> lOldVals = impl_readOldFormat(sPath);
        // replace all might existing variables with real values
        // Do it before these old paths will be compared against the
        // new path configuration. Otherwise some strings uses different variables ... but substitution
        // will produce strings with same content (because some variables are redundant!)
        impl_subst(lOldVals, fa_getSubstitution(), false);
        impl_mergeOldUserPaths(aPath, lOldVals);
    }
    catch(const css::uno::RuntimeException&)
        { throw; }
    // Normal(!) exceptions can be ignored!
    // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation
    // we can't find a value for it inside the "old" configuration. So a NoSuchElementException
    // will be normal .-)
    catch(const css::uno::Exception&)
        {}
 
    PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath);
    if (eOp == PathSettings::E_UNDEFINED)
    {
        if (pPath != m_lPaths.end())
            eOp = PathSettings::E_CHANGED;
        else
            eOp = PathSettings::E_ADDED;
    }
 
    switch(eOp)
    {
        case PathSettings::E_ADDED :
             {
                if (bNotifyListener)
                {
                    pPathOld = nullptr;
                    pPathNew = &aPath;
                    impl_notifyPropListener(sPath, pPathOld, pPathNew);
                }
                m_lPaths[sPath] = aPath;
             }
             break;
 
        case PathSettings::E_CHANGED :
             {
                if (bNotifyListener)
                {
                    pPathOld = &(pPath->second);
                    pPathNew = &aPath;
                    impl_notifyPropListener(sPath, pPathOld, pPathNew);
                }
                m_lPaths[sPath] = std::move(aPath);
             }
             break;
 
        case PathSettings::E_REMOVED :
             {
                if (pPath != m_lPaths.end())
                {
                    if (bNotifyListener)
                    {
                        pPathOld = &(pPath->second);
                        pPathNew = nullptr;
                        impl_notifyPropListener(sPath, pPathOld, pPathNew);
                    }
                    m_lPaths.erase(pPath);
                }
             }
             break;
 
        default: // to let compiler be happy
             break;
    }
 
    return eOp;
}
 
css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(std::u16string_view sPath)
{
    OUString sInternalProp = OUString::Concat(sPath)+POSTFIX_INTERNAL_PATHS;
    OUString sUserProp     = OUString::Concat(sPath)+POSTFIX_USER_PATHS;
    OUString sWriteProp    = OUString::Concat(sPath)+POSTFIX_WRITE_PATH;
 
    // Attention: The default set of IDs is fix and must follow these schema.
    // Otherwise the outside code ant work for new added properties.
    // Why?
    // The outside code must fire N events for every changed property.
    // And the knowing about packaging of variables of the structure PathInfo
    // follow these group IDs! But if such ID is not in the range of [0..IDGROUP_COUNT]
    // the outside can't determine the right group ... and can not fire the right events .-)
 
    css::uno::Sequence<sal_Int32> lIDs{ IDGROUP_OLDSTYLE, IDGROUP_INTERNAL_PATHS,
                                        IDGROUP_USER_PATHS, IDGROUP_WRITE_PATH };
    assert(lIDs.getLength() == IDGROUP_COUNT);
    auto plIDs = lIDs.getArray();
 
    sal_Int32 c = m_lPropDesc.getLength();
    sal_Int32 i = 0;
    for (i=0; i<c; ++i)
    {
        const css::beans::Property& rProp = m_lPropDesc[i];
 
        if (rProp.Name == sPath)
            plIDs[IDGROUP_OLDSTYLE] = rProp.Handle;
        else
        if (rProp.Name == sInternalProp)
            plIDs[IDGROUP_INTERNAL_PATHS] = rProp.Handle;
        else
        if (rProp.Name == sUserProp)
            plIDs[IDGROUP_USER_PATHS] = rProp.Handle;
        else
        if (rProp.Name == sWriteProp)
            plIDs[IDGROUP_WRITE_PATH] = rProp.Handle;
    }
 
    return lIDs;
}
 
void PathSettings::impl_notifyPropListener( std::u16string_view           sPath,
                                            const PathSettings::PathInfo* pPathOld,
                                            const PathSettings::PathInfo* pPathNew)
{
    css::uno::Sequence< sal_Int32 >     lHandles(1);
    auto plHandles = lHandles.getArray();
    css::uno::Sequence< css::uno::Any > lOldVals(1);
    auto plOldVals = lOldVals.getArray();
    css::uno::Sequence< css::uno::Any > lNewVals(1);
    auto plNewVals = lNewVals.getArray();
 
    css::uno::Sequence< sal_Int32 > lIDs   = impl_mapPathName2IDList(sPath);
    sal_Int32                       c      = lIDs.getLength();
    sal_Int32                       i      = 0;
    sal_Int32                       nMaxID = m_lPropDesc.getLength()-1;
    for (i=0; i<c; ++i)
    {
        sal_Int32 nID = lIDs[i];
 
        if (
            (nID < 0     ) ||
            (nID > nMaxID)
           )
           continue;
 
        plHandles[0] = nID;
        switch(impl_getPropGroup(nID))
        {
            case IDGROUP_OLDSTYLE :
                 {
                    if (pPathOld)
                    {
                        OUString sVal = impl_convertPath2OldStyle(*pPathOld);
                        plOldVals[0] <<= sVal;
                    }
                    if (pPathNew)
                    {
                        OUString sVal = impl_convertPath2OldStyle(*pPathNew);
                        plNewVals[0] <<= sVal;
                    }
                 }
                 break;
 
            case IDGROUP_INTERNAL_PATHS :
                 {
                    if (pPathOld)
                        plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lInternalPaths);
                    if (pPathNew)
                        plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lInternalPaths);
                 }
                 break;
 
            case IDGROUP_USER_PATHS :
                 {
                    if (pPathOld)
                        plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lUserPaths);
                    if (pPathNew)
                        plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lUserPaths);
                 }
                 break;
 
            case IDGROUP_WRITE_PATH :
                 {
                    if (pPathOld)
                        plOldVals[0] <<= pPathOld->sWritePath;
                    if (pPathNew)
                        plNewVals[0] <<= pPathNew->sWritePath;
                 }
                 break;
        }
 
        fire(plHandles,
             plNewVals,
             plOldVals,
             1,
             false);
    }
}
 
// static
void PathSettings::impl_subst(std::vector<OUString>& lVals   ,
                              const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
                                    bool                                               bReSubst)
{
    for (auto & old : lVals)
    {
        OUString  sNew;
        if (bReSubst)
            sNew = xSubst->reSubstituteVariables(old);
        else
            sNew = xSubst->substituteVariables(old, false);
 
        old = sNew;
    }
}
 
void PathSettings::impl_subst(PathSettings::PathInfo& aPath   ,
                              bool                bReSubst)
{
    css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution();
 
    impl_subst(aPath.lInternalPaths, xSubst, bReSubst);
    impl_subst(aPath.lUserPaths    , xSubst, bReSubst);
    if (bReSubst)
        aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath);
    else
        aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, false);
}
 
// static
OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath)
{
    OUStringBuffer sPathVal(256);
 
    for (auto const& internalPath : rPath.lInternalPaths)
    {
        if (sPathVal.getLength())
            sPathVal.append(";");
        sPathVal.append(internalPath);
    }
    for (auto const& userPath : rPath.lUserPaths)
    {
        if (sPathVal.getLength())
            sPathVal.append(";");
        sPathVal.append(userPath);
    }
    if (!rPath.sWritePath.isEmpty())
    {
        if (sPathVal.getLength())
            sPathVal.append(";");
        sPathVal.append(rPath.sWritePath);
    }
 
    return sPathVal.makeStringAndClear();
}
 
// static
std::vector<OUString> PathSettings::impl_convertOldStyle2Path(std::u16string_view sOldStylePath)
{
    std::vector<OUString> lList;
    sal_Int32    nToken = 0;
    do
    {
        OUString sToken( o3tl::getToken(sOldStylePath, 0, ';', nToken) );
        if (!sToken.isEmpty())
            lList.push_back(sToken);
    }
    while(nToken >= 0);
 
    return lList;
}
 
// static
void PathSettings::impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
                                        std::vector<OUString>& lList)
{
    // Erase items in the internal path list from lList.
    // Also erase items in the internal path list from the user path list.
    for (auto const& internalPath : rPath.lInternalPaths)
    {
        std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), internalPath);
        if (pItem != lList.end())
            lList.erase(pItem);
        pItem = std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), internalPath);
        if (pItem != rPath.lUserPaths.end())
            rPath.lUserPaths.erase(pItem);
    }
 
    // Erase items not in lList from the user path list.
    std::erase_if(rPath.lUserPaths,
        [&lList](const OUString& rItem) {
            return std::find(lList.begin(), lList.end(), rItem) == lList.end();
        });
 
    // Erase items in the user path list from lList.
    for (auto const& userPath : rPath.lUserPaths)
    {
        std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), userPath);
        if (pItem != lList.end())
            lList.erase(pItem);
    }
 
    // Erase the write path from lList
    std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), rPath.sWritePath);
    if (pItem != lList.end())
        lList.erase(pItem);
}
 
void PathSettings::impl_rebuildPropertyDescriptor()
{
    // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    sal_Int32 c = static_cast<sal_Int32>(m_lPaths.size());
    sal_Int32 i = 0;
    m_lPropDesc.realloc(c*IDGROUP_COUNT);
    auto plPropDesc = m_lPropDesc.getArray();
 
    for (auto const& path : m_lPaths)
    {
        const PathSettings::PathInfo& rPath = path.second;
        css::beans::Property*   pProp = nullptr;
 
        pProp             = &(plPropDesc[i]);
        pProp->Name       = rPath.sPathName;
        pProp->Handle     = i;
        pProp->Type       = cppu::UnoType<OUString>::get();
        pProp->Attributes = css::beans::PropertyAttribute::BOUND;
        if (rPath.bIsReadonly)
            pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
        ++i;
 
        pProp             = &(plPropDesc[i]);
        pProp->Name       = rPath.sPathName+POSTFIX_INTERNAL_PATHS;
        pProp->Handle     = i;
        pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
        pProp->Attributes = css::beans::PropertyAttribute::BOUND   |
                            css::beans::PropertyAttribute::READONLY;
        ++i;
 
        pProp             = &(plPropDesc[i]);
        pProp->Name       = rPath.sPathName+POSTFIX_USER_PATHS;
        pProp->Handle     = i;
        pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
        pProp->Attributes = css::beans::PropertyAttribute::BOUND;
        if (rPath.bIsReadonly)
            pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
        ++i;
 
        pProp             = &(plPropDesc[i]);
        pProp->Name       = rPath.sPathName+POSTFIX_WRITE_PATH;
        pProp->Handle     = i;
        pProp->Type       = cppu::UnoType<OUString>::get();
        pProp->Attributes = css::beans::PropertyAttribute::BOUND;
        if (rPath.bIsReadonly)
            pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
        ++i;
    }
 
    m_pPropHelp.reset(new ::cppu::OPropertyArrayHelper(m_lPropDesc, false)); // false => not sorted ... must be done inside helper
 
    // <- SAFE
}
 
css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const
{
    const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID);
    if (! pPath)
        throw css::lang::IllegalArgumentException();
 
    css::uno::Any aVal;
    switch(impl_getPropGroup(nID))
    {
        case IDGROUP_OLDSTYLE :
             {
                OUString sVal = impl_convertPath2OldStyle(*pPath);
                aVal <<= sVal;
             }
             break;
 
        case IDGROUP_INTERNAL_PATHS :
             {
                aVal <<= comphelper::containerToSequence(pPath->lInternalPaths);
             }
             break;
 
        case IDGROUP_USER_PATHS :
             {
                aVal <<= comphelper::containerToSequence(pPath->lUserPaths);
             }
             break;
 
        case IDGROUP_WRITE_PATH :
             {
                aVal <<= pPath->sWritePath;
             }
             break;
    }
 
    return aVal;
}
 
void PathSettings::impl_setPathValue(      sal_Int32      nID ,
                                     const css::uno::Any& aVal)
{
    PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID);
    if (! pOrgPath)
        throw css::container::NoSuchElementException();
 
    // We work on a copied path ... so we can be sure that errors during this operation
    // does not make our internal cache invalid  .-)
    PathSettings::PathInfo aChangePath(*pOrgPath);
 
    switch(impl_getPropGroup(nID))
    {
        case IDGROUP_OLDSTYLE :
             {
                OUString sVal;
                aVal >>= sVal;
                std::vector<OUString> lList = impl_convertOldStyle2Path(sVal);
                impl_subst(lList, fa_getSubstitution(), false);
                impl_purgeKnownPaths(aChangePath, lList);
                if (! impl_isValidPath(lList))
                    throw css::lang::IllegalArgumentException();
 
                if (aChangePath.bIsSinglePath)
                {
                    SAL_WARN_IF(lList.size()>1, "fwk", "PathSettings::impl_setPathValue(): You try to set more than path value for a defined SINGLE_PATH!");
                    if ( !lList.empty() )
                        aChangePath.sWritePath = *(lList.begin());
                    else
                        aChangePath.sWritePath.clear();
                }
                else
                {
                    for (auto const& elem : lList)
                    {
                        aChangePath.lUserPaths.push_back(elem);
                    }
                }
             }
             break;
 
        case IDGROUP_INTERNAL_PATHS :
             {
                if (aChangePath.bIsSinglePath)
                {
                    throw css::uno::Exception(
                        "The path '" + aChangePath.sPathName
                        + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
                        static_cast< ::cppu::OWeakObject* >(this));
                }
 
                css::uno::Sequence<OUString> lTmpList;
                aVal >>= lTmpList;
                std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
                if (! impl_isValidPath(lList))
                    throw css::lang::IllegalArgumentException();
                aChangePath.lInternalPaths = std::move(lList);
             }
             break;
 
        case IDGROUP_USER_PATHS :
             {
                if (aChangePath.bIsSinglePath)
                {
                    throw css::uno::Exception(
                        "The path '" + aChangePath.sPathName
                        + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
                        static_cast< ::cppu::OWeakObject* >(this));
                }
 
                css::uno::Sequence<OUString> lTmpList;
                aVal >>= lTmpList;
                std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
                if (! impl_isValidPath(lList))
                    throw css::lang::IllegalArgumentException();
                aChangePath.lUserPaths = std::move(lList);
             }
             break;
 
        case IDGROUP_WRITE_PATH :
             {
                OUString sVal;
                aVal >>= sVal;
                if (! impl_isValidPath(sVal))
                    throw css::lang::IllegalArgumentException();
                aChangePath.sWritePath = sVal;
             }
             break;
    }
 
    // TODO check if path has at least one path value set
    // At least it depends from the feature using this path, if an empty path list is allowed.
 
    // first we should try to store the changed (copied!) path ...
    // In case an error occurs on saving time an exception is thrown ...
    // If no exception occurs we can update our internal cache (means
    // we can overwrite pOrgPath !
    impl_storePath(aChangePath);
    *pOrgPath = std::move(aChangePath);
}
 
// static
bool PathSettings::impl_isValidPath(const std::vector<OUString>& lPath)
{
    for (auto const& path : lPath)
    {
        if (! impl_isValidPath(path))
            return false;
    }
 
    return true;
}
 
// static
bool PathSettings::impl_isValidPath(std::u16string_view sPath)
{
    // allow empty path to reset a path.
// idea by LLA to support empty paths
//    if (sPath.getLength() == 0)
//    {
//        return sal_True;
//    }
 
    return (! INetURLObject(sPath).HasError());
}
 
OUString impl_extractBaseFromPropName(const OUString& sPropName)
{
    sal_Int32 i = sPropName.indexOf(POSTFIX_INTERNAL_PATHS);
    if (i > -1)
        return sPropName.copy(0, i);
    i = sPropName.indexOf(POSTFIX_USER_PATHS);
    if (i > -1)
        return sPropName.copy(0, i);
    i = sPropName.indexOf(POSTFIX_WRITE_PATH);
    if (i > -1)
        return sPropName.copy(0, i);
 
    return sPropName;
}
 
PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle)
{
    // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    if (nHandle > (m_lPropDesc.getLength()-1))
        return nullptr;
 
    const css::beans::Property&            rProp = m_lPropDesc[nHandle];
    OUString                  sProp = impl_extractBaseFromPropName(rProp.Name);
    PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp);
 
    if (rPath != m_lPaths.end())
       return &(rPath->second);
 
    return nullptr;
    // <- SAFE
}
 
const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const
{
    // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
 
    if (nHandle > (m_lPropDesc.getLength()-1))
        return nullptr;
 
    const css::beans::Property&     rProp = m_lPropDesc[nHandle];
    OUString                        sProp = impl_extractBaseFromPropName(rProp.Name);
    PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp);
 
    if (rPath != m_lPaths.end())
       return &(rPath->second);
 
    return nullptr;
    // <- SAFE
}
 
sal_Bool SAL_CALL PathSettings::convertFastPropertyValue(      css::uno::Any& aConvertedValue,
                                                               css::uno::Any& aOldValue      ,
                                                               sal_Int32      nHandle        ,
                                                         const css::uno::Any& aValue         )
{
    // throws NoSuchElementException !
    css::uno::Any aCurrentVal = impl_getPathValue(nHandle);
 
    return PropHelper::willPropertyBeChanged(
                aCurrentVal,
                aValue,
                aOldValue,
                aConvertedValue);
}
 
void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast(      sal_Int32      nHandle,
                                                             const css::uno::Any& aValue )
{
    // throws NoSuchElement- and IllegalArgumentException !
    impl_setPathValue(nHandle, aValue);
}
 
void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue ,
                                                 sal_Int32      nHandle) const
{
    aValue = impl_getPathValue(nHandle);
}
 
::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper()
{
    return *m_pPropHelp;
}
 
css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo()
{
    return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
}
 
css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution()
{
    css::uno::Reference< css::util::XStringSubstitution > xSubst;
    { // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
    xSubst = m_xSubstitution;
    }
 
    if (! xSubst.is())
    {
        // create the needed substitution service.
        // We must replace all used variables inside read path values.
        // In case we can't do so... the whole office can't work really.
        // That's why it seems to be OK to throw a RuntimeException then.
        xSubst = css::util::PathSubstitution::create(m_xContext);
 
        { // SAFE ->
        osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
        m_xSubstitution = xSubst;
        }
    }
 
    return xSubst;
}
 
css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld()
{
    css::uno::Reference< css::container::XNameAccess > xCfg;
    { // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
    xCfg = m_xCfgOld;
    } // <- SAFE
 
    if (! xCfg.is())
    {
        xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
                        m_xContext,
                        u"org.openoffice.Office.Common/Path/Current"_ustr,
                        ::comphelper::EConfigurationModes::Standard), // not readonly! Sometimes we need write access there !!!
                   css::uno::UNO_QUERY_THROW);
 
        { // SAFE ->
        osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
        m_xCfgOld = xCfg;
        }
    }
 
    return xCfg;
}
 
css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew()
{
    css::uno::Reference< css::container::XNameAccess > xCfg;
    { // SAFE ->
    osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
    xCfg = m_xCfgNew;
    } // <- SAFE
 
    if (! xCfg.is())
    {
        xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
                        m_xContext,
                        u"org.openoffice.Office.Paths/Paths"_ustr,
                        ::comphelper::EConfigurationModes::Standard),
                   css::uno::UNO_QUERY_THROW);
 
        { // SAFE ->
        osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
        m_xCfgNew = xCfg;
        m_xCfgNewListener = new WeakChangesListener(this);
        }
 
        css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW);
        xBroadcaster->addChangesListener(m_xCfgNewListener);
    }
 
    return xCfg;
}
 
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_framework_PathSettings_get_implementation(
    css::uno::XComponentContext *context,
    css::uno::Sequence<css::uno::Any> const &)
{
    rtl::Reference<PathSettings> xPathSettings = new PathSettings(context);
    // fill cache
    xPathSettings->impl_readAll();
 
    return cppu::acquire(xPathSettings.get());
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'disposing' virtual function in the destructor may lead to unexpected result at runtime.

V1001 The 'a' variable is assigned but is not used by the end of the function.