/* -*- 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 <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/configuration/theDefaultProvider.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/util/XChangesBatch.hpp>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <tools/debug.hxx>
#include <unotools/configitem.hxx>
#include <unotools/lingucfg.hxx>
#include <unotools/linguprops.hxx>
#include <comphelper/getexpandeduri.hxx>
#include <comphelper/processfactory.hxx>
#include <o3tl/string_view.hxx>
#include <mutex>
 
#include "itemholder1.hxx"
 
using namespace com::sun::star;
 
constexpr OUStringLiteral FILE_PROTOCOL = u"file:///";
 
namespace
{
    std::mutex& theSvtLinguConfigItemMutex()
    {
        static std::mutex SINGLETON;
        return SINGLETON;
    }
}
 
static bool lcl_SetLocale( LanguageType &rLanguage, const uno::Any &rVal )
{
    bool bSucc = false;
 
    lang::Locale aNew;
    if (rVal >>= aNew)  // conversion successful?
    {
        LanguageType nNew = LanguageTag::convertToLanguageType( aNew, false);
        if (nNew != rLanguage)
        {
            rLanguage = nNew;
            bSucc = true;
        }
    }
    return bSucc;
}
 
static OUString lcl_LanguageToCfgLocaleStr( LanguageType nLanguage )
{
    OUString aRes;
    if (LANGUAGE_SYSTEM != nLanguage)
        aRes = LanguageTag::convertToBcp47( nLanguage );
    return aRes;
}
 
static LanguageType lcl_CfgAnyToLanguage( const uno::Any &rVal )
{
    OUString aTmp;
    rVal >>= aTmp;
    return (aTmp.isEmpty()) ? LANGUAGE_SYSTEM : LanguageTag::convertToLanguageTypeWithFallback( aTmp );
}
 
SvtLinguOptions::SvtLinguOptions()
    : bROActiveDics(false)
    , bROActiveConvDics(false)
    , nHyphMinLeading(2)
    , nHyphMinTrailing(2)
    , nHyphMinWordLength(0)
    , bROHyphMinLeading(false)
    , bROHyphMinTrailing(false)
    , bROHyphMinWordLength(false)
    , nDefaultLanguage(LANGUAGE_NONE)
    , nDefaultLanguage_CJK(LANGUAGE_NONE)
    , nDefaultLanguage_CTL(LANGUAGE_NONE)
    , bRODefaultLanguage(false)
    , bRODefaultLanguage_CJK(false)
    , bRODefaultLanguage_CTL(false)
    , bIsSpellSpecial(true)
    , bIsSpellAuto(false)
    , bIsSpellReverse(false)
    , bROIsSpellSpecial(false)
    , bROIsSpellAuto(false)
    , bROIsSpellReverse(false)
    , bIsHyphSpecial(true)
    , bIsHyphAuto(false)
    , bROIsHyphSpecial(false)
    , bROIsHyphAuto(false)
    , bIsUseDictionaryList(true)
    , bIsIgnoreControlCharacters(true)
    , bROIsUseDictionaryList(false)
    , bROIsIgnoreControlCharacters(false)
    , bIsSpellWithDigits(false)
    , bIsSpellUpperCase(false)
    , bIsSpellClosedCompound(true)
    , bIsSpellHyphenatedCompound(true)
    , bROIsSpellWithDigits(false)
    , bROIsSpellUpperCase(false)
    , bROIsSpellClosedCompound(false)
    , bROIsSpellHyphenatedCompound(false)
    , bIsIgnorePostPositionalWord(true)
    , bIsAutoCloseDialog(false)
    , bIsShowEntriesRecentlyUsedFirst(false)
    , bIsAutoReplaceUniqueEntries(false)
    , bIsDirectionToSimplified(true)
    , bIsUseCharacterVariants(false)
    , bIsTranslateCommonTerms(false)
    , bIsReverseMapping(false)
    , bROIsIgnorePostPositionalWord(false)
    , bROIsAutoCloseDialog(false)
    , bROIsShowEntriesRecentlyUsedFirst(false)
    , bROIsAutoReplaceUniqueEntries(false)
    , bROIsDirectionToSimplified(false)
    , bROIsUseCharacterVariants(false)
    , bROIsTranslateCommonTerms(false)
    , bROIsReverseMapping(false)
    , nDataFilesChangedCheckValue(0)
    , bRODataFilesChangedCheckValue(false)
    , bIsGrammarAuto(false)
    , bIsGrammarInteractive(false)
    , bROIsGrammarAuto(false)
    , bROIsGrammarInteractive(false)
{
}
 
class SvtLinguConfigItem : public utl::ConfigItem
{
    SvtLinguOptions     aOpt;
 
    static bool GetHdlByName( sal_Int32 &rnHdl, std::u16string_view rPropertyName, bool bFullPropName = false );
    static uno::Sequence< OUString > GetPropertyNames();
    void                LoadOptions( const uno::Sequence< OUString > &rProperyNames );
    bool                SaveOptions( const uno::Sequence< OUString > &rProperyNames );
 
    SvtLinguConfigItem(const SvtLinguConfigItem&) = delete;
    SvtLinguConfigItem& operator=(const SvtLinguConfigItem&) = delete;
    virtual void    ImplCommit() override;
 
public:
    SvtLinguConfigItem();
 
    // utl::ConfigItem
    virtual void    Notify( const css::uno::Sequence< OUString > &rPropertyNames ) override;
 
    // make some protected functions of utl::ConfigItem public
    using utl::ConfigItem::GetNodeNames;
    using utl::ConfigItem::GetProperties;
    //using utl::ConfigItem::PutProperties;
    //using utl::ConfigItem::SetSetProperties;
    using utl::ConfigItem::ReplaceSetProperties;
    //using utl::ConfigItem::GetReadOnlyStates;
 
    css::uno::Any
            GetProperty( std::u16string_view rPropertyName ) const;
    css::uno::Any
            GetProperty( sal_Int32 nPropertyHandle ) const;
 
    bool    SetProperty( std::u16string_view rPropertyName,
                         const css::uno::Any &rValue );
    bool    SetProperty( sal_Int32 nPropertyHandle,
                         const css::uno::Any &rValue );
 
    void GetOptions( SvtLinguOptions& ) const;
 
    bool    IsReadOnly( std::u16string_view rPropertyName ) const;
    bool    IsReadOnly( sal_Int32 nPropertyHandle ) const;
};
 
SvtLinguConfigItem::SvtLinguConfigItem() :
    utl::ConfigItem( u"Office.Linguistic"_ustr )
{
    const uno::Sequence< OUString > aPropertyNames = GetPropertyNames();
    LoadOptions( aPropertyNames );
    ClearModified();
 
    // request notify events when properties change
    EnableNotification( aPropertyNames );
}
 
void SvtLinguConfigItem::Notify( const uno::Sequence< OUString > &rPropertyNames )
{
    {
        std::unique_lock aGuard(theSvtLinguConfigItemMutex());
        LoadOptions( rPropertyNames );
    }
    NotifyListeners(ConfigurationHints::NONE);
}
 
void SvtLinguConfigItem::ImplCommit()
{
    SaveOptions( GetPropertyNames() );
}
 
namespace {
 
struct NamesToHdl
{
    OUString     aFullPropName;      // full qualified name as used in configuration
    OUString     aPropName;          // property name only (atom) of above
    sal_Int32    nHdl;               // numeric handle representing the property
};
 
}
 
NamesToHdl constexpr aNamesToHdl[] =
{
{/*  0 */    u"General/DefaultLocale"_ustr,                         UPN_DEFAULT_LOCALE,                    UPH_DEFAULT_LOCALE},
{/*  1 */    u"General/DictionaryList/ActiveDictionaries"_ustr,     UPN_ACTIVE_DICTIONARIES,               UPH_ACTIVE_DICTIONARIES},
{/*  2 */    u"General/DictionaryList/IsUseDictionaryList"_ustr,    UPN_IS_USE_DICTIONARY_LIST,            UPH_IS_USE_DICTIONARY_LIST},
{/*  3 */    u"General/IsIgnoreControlCharacters"_ustr,             UPN_IS_IGNORE_CONTROL_CHARACTERS,      UPH_IS_IGNORE_CONTROL_CHARACTERS},
{/*  5 */    u"General/DefaultLocale_CJK"_ustr,                     UPN_DEFAULT_LOCALE_CJK,                UPH_DEFAULT_LOCALE_CJK},
{/*  6 */    u"General/DefaultLocale_CTL"_ustr,                     UPN_DEFAULT_LOCALE_CTL,                UPH_DEFAULT_LOCALE_CTL},
 
{/*  7 */    u"SpellChecking/IsSpellUpperCase"_ustr,                UPN_IS_SPELL_UPPER_CASE,               UPH_IS_SPELL_UPPER_CASE},
{/*  8 */    u"SpellChecking/IsSpellWithDigits"_ustr,               UPN_IS_SPELL_WITH_DIGITS,              UPH_IS_SPELL_WITH_DIGITS},
{/*  9 */    u"SpellChecking/IsSpellAuto"_ustr,                     UPN_IS_SPELL_AUTO,                     UPH_IS_SPELL_AUTO},
{/* 10 */    u"SpellChecking/IsSpellSpecial"_ustr,                  UPN_IS_SPELL_SPECIAL,                  UPH_IS_SPELL_SPECIAL},
{/* 11 */    u"SpellChecking/IsSpellClosedCompound"_ustr,           UPN_IS_SPELL_CLOSED_COMPOUND,          UPH_IS_SPELL_CLOSED_COMPOUND},
{/* 12 */    u"SpellChecking/IsSpellHyphenatedCompound"_ustr,       UPN_IS_SPELL_HYPHENATED_COMPOUND,      UPH_IS_SPELL_HYPHENATED_COMPOUND},
{/* 13 */    u"SpellChecking/IsReverseDirection"_ustr,              UPN_IS_WRAP_REVERSE,                   UPH_IS_WRAP_REVERSE},
 
{/* 14 */    u"Hyphenation/MinLeading"_ustr,                        UPN_HYPH_MIN_LEADING,                  UPH_HYPH_MIN_LEADING},
{/* 15 */    u"Hyphenation/MinTrailing"_ustr,                       UPN_HYPH_MIN_TRAILING,                 UPH_HYPH_MIN_TRAILING},
{/* 16 */    u"Hyphenation/MinWordLength"_ustr,                     UPN_HYPH_MIN_WORD_LENGTH,              UPH_HYPH_MIN_WORD_LENGTH},
{/* 17*/     u"Hyphenation/IsHyphSpecial"_ustr,                     UPN_IS_HYPH_SPECIAL,                   UPH_IS_HYPH_SPECIAL},
{/* 18 */    u"Hyphenation/IsHyphAuto"_ustr,                        UPN_IS_HYPH_AUTO,                      UPH_IS_HYPH_AUTO},
 
{/* 19 */    u"TextConversion/ActiveConversionDictionaries"_ustr,   UPN_ACTIVE_CONVERSION_DICTIONARIES,        UPH_ACTIVE_CONVERSION_DICTIONARIES},
{/* 20 */    u"TextConversion/IsIgnorePostPositionalWord"_ustr,     UPN_IS_IGNORE_POST_POSITIONAL_WORD,        UPH_IS_IGNORE_POST_POSITIONAL_WORD},
{/* 21 */    u"TextConversion/IsAutoCloseDialog"_ustr,              UPN_IS_AUTO_CLOSE_DIALOG,                  UPH_IS_AUTO_CLOSE_DIALOG},
{/* 22 */    u"TextConversion/IsShowEntriesRecentlyUsedFirst"_ustr, UPN_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST,   UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST},
{/* 23 */    u"TextConversion/IsAutoReplaceUniqueEntries"_ustr,     UPN_IS_AUTO_REPLACE_UNIQUE_ENTRIES,        UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES},
{/* 24 */    u"TextConversion/IsDirectionToSimplified"_ustr,        UPN_IS_DIRECTION_TO_SIMPLIFIED,            UPH_IS_DIRECTION_TO_SIMPLIFIED},
{/* 25 */    u"TextConversion/IsUseCharacterVariants"_ustr,         UPN_IS_USE_CHARACTER_VARIANTS,             UPH_IS_USE_CHARACTER_VARIANTS},
{/* 26 */    u"TextConversion/IsTranslateCommonTerms"_ustr,         UPN_IS_TRANSLATE_COMMON_TERMS,             UPH_IS_TRANSLATE_COMMON_TERMS},
{/* 27 */    u"TextConversion/IsReverseMapping"_ustr,               UPN_IS_REVERSE_MAPPING,                    UPH_IS_REVERSE_MAPPING},
 
{/* 28 */    u"ServiceManager/DataFilesChangedCheckValue"_ustr,     UPN_DATA_FILES_CHANGED_CHECK_VALUE,        UPH_DATA_FILES_CHANGED_CHECK_VALUE},
 
{/* 29 */    u"GrammarChecking/IsAutoCheck"_ustr,                   UPN_IS_GRAMMAR_AUTO,                      UPH_IS_GRAMMAR_AUTO},
{/* 30 */    u"GrammarChecking/IsInteractiveCheck"_ustr,            UPN_IS_GRAMMAR_INTERACTIVE,               UPH_IS_GRAMMAR_INTERACTIVE},
 
            /* similar to entry 0 (thus no own configuration entry) but with different property name and type */
{            u""_ustr,                                         UPN_DEFAULT_LANGUAGE,                      UPH_DEFAULT_LANGUAGE},
 
{            u""_ustr,                                         u""_ustr,                                      -1}
};
 
uno::Sequence< OUString > SvtLinguConfigItem::GetPropertyNames()
{
    uno::Sequence< OUString > aNames;
    aNames.realloc(std::size(aNamesToHdl));
    OUString *pNames = aNames.getArray();
    sal_Int32 nIdx = 0;
    for (auto const & nameToHdl: aNamesToHdl)
    {
        if (!nameToHdl.aFullPropName.isEmpty())
            pNames[ nIdx++ ] = nameToHdl.aFullPropName;
    }
    aNames.realloc( nIdx );
 
    return aNames;
}
 
bool SvtLinguConfigItem::GetHdlByName(
    sal_Int32 &rnHdl,
    std::u16string_view rPropertyName,
    bool bFullPropName )
{
    NamesToHdl const *pEntry = &aNamesToHdl[0];
 
    if (bFullPropName)
    {
        while (pEntry && !pEntry->aFullPropName.isEmpty())
        {
            if (pEntry->aFullPropName == rPropertyName)
            {
                rnHdl = pEntry->nHdl;
                break;
            }
            ++pEntry;
        }
        return pEntry && !pEntry->aFullPropName.isEmpty();
    }
    else
    {
        while (pEntry && !pEntry->aFullPropName.isEmpty())
        {
            if (rPropertyName == pEntry->aPropName )
            {
                rnHdl = pEntry->nHdl;
                break;
            }
            ++pEntry;
        }
        return pEntry && !pEntry->aFullPropName.isEmpty();
    }
}
 
uno::Any SvtLinguConfigItem::GetProperty( std::u16string_view rPropertyName ) const
{
    sal_Int32 nHdl;
    return GetHdlByName( nHdl, rPropertyName ) ? GetProperty( nHdl ) : uno::Any();
}
 
uno::Any SvtLinguConfigItem::GetProperty( sal_Int32 nPropertyHandle ) const
{
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
 
    uno::Any aRes;
 
    const sal_Int16 *pnVal = nullptr;
    const LanguageType *plVal = nullptr;
    const bool  *pbVal = nullptr;
    const sal_Int32 *pnInt32Val = nullptr;
 
    const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
    switch (nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST :   pbVal = &rOpt.bIsUseDictionaryList; break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters;   break;
        case UPH_IS_HYPH_AUTO :             pbVal = &rOpt.bIsHyphAuto;  break;
        case UPH_IS_HYPH_SPECIAL :          pbVal = &rOpt.bIsHyphSpecial;   break;
        case UPH_IS_SPELL_AUTO :            pbVal = &rOpt.bIsSpellAuto; break;
        case UPH_IS_SPELL_SPECIAL :         pbVal = &rOpt.bIsSpellSpecial;  break;
        case UPH_IS_WRAP_REVERSE :          pbVal = &rOpt.bIsSpellReverse;  break;
        case UPH_DEFAULT_LANGUAGE :         plVal = &rOpt.nDefaultLanguage; break;
        case UPH_IS_SPELL_CLOSED_COMPOUND:  pbVal = &rOpt.bIsSpellClosedCompound;       break;
        case UPH_IS_SPELL_HYPHENATED_COMPOUND:  pbVal = &rOpt.bIsSpellHyphenatedCompound;    break;
        case UPH_IS_SPELL_WITH_DIGITS :     pbVal = &rOpt.bIsSpellWithDigits;   break;
        case UPH_IS_SPELL_UPPER_CASE :      pbVal = &rOpt.bIsSpellUpperCase;        break;
        case UPH_HYPH_MIN_LEADING :         pnVal = &rOpt.nHyphMinLeading;      break;
        case UPH_HYPH_MIN_TRAILING :        pnVal = &rOpt.nHyphMinTrailing; break;
        case UPH_HYPH_MIN_WORD_LENGTH :     pnVal = &rOpt.nHyphMinWordLength;   break;
        case UPH_ACTIVE_DICTIONARIES :
        {
            aRes <<= rOpt.aActiveDics;
            break;
        }
        case UPH_ACTIVE_CONVERSION_DICTIONARIES :
        {
            aRes <<= rOpt.aActiveConvDics;
            break;
        }
        case UPH_DEFAULT_LOCALE :
        {
            aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage, false);
            break;
        }
        case UPH_DEFAULT_LOCALE_CJK :
        {
            aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage_CJK, false);
            break;
        }
        case UPH_DEFAULT_LOCALE_CTL :
        {
            aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage_CTL, false);
            break;
        }
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 pbVal = &rOpt.bIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;
 
        case UPH_IS_DIRECTION_TO_SIMPLIFIED:            pbVal = &rOpt.bIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS :            pbVal = &rOpt.bIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS :            pbVal = &rOpt.bIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :                   pbVal = &rOpt.bIsReverseMapping; break;
 
        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       pbVal = &rOpt.bIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                pbVal = &rOpt.bIsGrammarInteractive; break;
        default :
            SAL_WARN( "unotools.config", "unexpected property handle" );
    }
 
    if (pbVal)
        aRes <<= *pbVal;
    else if (pnVal)
        aRes <<= *pnVal;
    else if (plVal)
        aRes <<= static_cast<sal_Int16>(static_cast<sal_uInt16>(*plVal));
    else if (pnInt32Val)
        aRes <<= *pnInt32Val;
 
    return aRes;
}
 
bool SvtLinguConfigItem::SetProperty( std::u16string_view rPropertyName, const uno::Any &rValue )
{
    bool bSucc = false;
    sal_Int32 nHdl;
    if (GetHdlByName( nHdl, rPropertyName ))
        bSucc = SetProperty( nHdl, rValue );
    return bSucc;
}
 
bool SvtLinguConfigItem::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
{
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
 
    bool bSucc = false;
    if (!rValue.hasValue())
        return bSucc;
 
    bool bMod = false;
 
    sal_Int16 *pnVal = nullptr;
    LanguageType *plVal = nullptr;
    bool  *pbVal = nullptr;
    sal_Int32 *pnInt32Val = nullptr;
 
    SvtLinguOptions &rOpt = aOpt;
    switch (nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST :   pbVal = &rOpt.bIsUseDictionaryList;    break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters;  break;
        case UPH_IS_HYPH_AUTO :             pbVal = &rOpt.bIsHyphAuto; break;
        case UPH_IS_HYPH_SPECIAL :          pbVal = &rOpt.bIsHyphSpecial;  break;
        case UPH_IS_SPELL_AUTO :            pbVal = &rOpt.bIsSpellAuto;    break;
        case UPH_IS_SPELL_SPECIAL :         pbVal = &rOpt.bIsSpellSpecial; break;
        case UPH_IS_WRAP_REVERSE :          pbVal = &rOpt.bIsSpellReverse; break;
        case UPH_DEFAULT_LANGUAGE :         plVal = &rOpt.nDefaultLanguage;    break;
        case UPH_IS_SPELL_CLOSED_COMPOUND:  pbVal = &rOpt.bIsSpellClosedCompound;      break;
        case UPH_IS_SPELL_HYPHENATED_COMPOUND:  pbVal = &rOpt.bIsSpellHyphenatedCompound;    break;
        case UPH_IS_SPELL_WITH_DIGITS :     pbVal = &rOpt.bIsSpellWithDigits;  break;
        case UPH_IS_SPELL_UPPER_CASE :      pbVal = &rOpt.bIsSpellUpperCase;       break;
        case UPH_HYPH_MIN_LEADING :         pnVal = &rOpt.nHyphMinLeading;     break;
        case UPH_HYPH_MIN_TRAILING :        pnVal = &rOpt.nHyphMinTrailing;    break;
        case UPH_HYPH_MIN_WORD_LENGTH :     pnVal = &rOpt.nHyphMinWordLength;  break;
        case UPH_ACTIVE_DICTIONARIES :
        {
            rValue >>= rOpt.aActiveDics;
            bMod = true;
            break;
        }
        case UPH_ACTIVE_CONVERSION_DICTIONARIES :
        {
            rValue >>= rOpt.aActiveConvDics;
            bMod = true;
            break;
        }
        case UPH_DEFAULT_LOCALE :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_DEFAULT_LOCALE_CJK :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CJK, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_DEFAULT_LOCALE_CTL :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CTL, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 pbVal = &rOpt.bIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;
 
        case UPH_IS_DIRECTION_TO_SIMPLIFIED :           pbVal = &rOpt.bIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS :            pbVal = &rOpt.bIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS :            pbVal = &rOpt.bIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :                   pbVal = &rOpt.bIsReverseMapping; break;
 
        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       pbVal = &rOpt.bIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                pbVal = &rOpt.bIsGrammarInteractive; break;
        default :
            SAL_WARN( "unotools.config", "unexpected property handle" );
    }
 
    if (pbVal)
    {
        bool bNew = bool();
        if (rValue >>= bNew)
        {
            if (bNew != *pbVal)
            {
                *pbVal = bNew;
                bMod = true;
            }
            bSucc = true;
        }
    }
    else if (pnVal)
    {
        sal_Int16 nNew = sal_Int16();
        if (rValue >>= nNew)
        {
            if (nNew != *pnVal)
            {
                *pnVal = nNew;
                bMod = true;
            }
            bSucc = true;
        }
    }
    else if (plVal)
    {
        sal_Int16 nNew = sal_Int16();
        if (rValue >>= nNew)
        {
            if (nNew != static_cast<sal_uInt16>(*plVal))
            {
                *plVal = LanguageType(static_cast<sal_uInt16>(nNew));
                bMod = true;
            }
            bSucc = true;
        }
    }
    else if (pnInt32Val)
    {
        sal_Int32 nNew = sal_Int32();
        if (rValue >>= nNew)
        {
            if (nNew != *pnInt32Val)
            {
                *pnInt32Val = nNew;
                bMod = true;
            }
            bSucc = true;
        }
    }
 
    if (bMod)
        SetModified();
 
    NotifyListeners(ConfigurationHints::NONE);
    return bSucc;
}
 
void SvtLinguConfigItem::GetOptions(SvtLinguOptions &rOptions) const
{
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
    rOptions = aOpt;
}
 
void SvtLinguConfigItem::LoadOptions( const uno::Sequence< OUString > &rProperyNames )
{
    bool bRes = false;
 
    const OUString *pProperyNames = rProperyNames.getConstArray();
    sal_Int32 nProps = rProperyNames.getLength();
 
    const uno::Sequence< uno::Any > aValues = GetProperties( rProperyNames );
    const uno::Sequence< sal_Bool > aROStates = GetReadOnlyStates( rProperyNames );
 
    if (nProps  &&  aValues.getLength() == nProps &&  aROStates.getLength() == nProps)
    {
        SvtLinguOptions &rOpt = aOpt;
 
        const uno::Any *pValue = aValues.getConstArray();
        const sal_Bool *pROStates = aROStates.getConstArray();
        for (sal_Int32 i = 0;  i < nProps;  ++i)
        {
            const uno::Any &rVal = pValue[i];
            sal_Int32 nPropertyHandle(0);
            GetHdlByName( nPropertyHandle, pProperyNames[i], true );
            switch ( nPropertyHandle )
            {
                case UPH_DEFAULT_LOCALE :
                    { rOpt.bRODefaultLanguage = pROStates[i]; rOpt.nDefaultLanguage = lcl_CfgAnyToLanguage( rVal ); } break;
                case UPH_ACTIVE_DICTIONARIES :
                    { rOpt.bROActiveDics = pROStates[i]; rVal >>= rOpt.aActiveDics;   } break;
                case UPH_IS_USE_DICTIONARY_LIST :
                    { rOpt.bROIsUseDictionaryList = pROStates[i]; rVal >>= rOpt.bIsUseDictionaryList;  } break;
                case UPH_IS_IGNORE_CONTROL_CHARACTERS :
                    { rOpt.bROIsIgnoreControlCharacters = pROStates[i]; rVal >>= rOpt.bIsIgnoreControlCharacters;    } break;
                case UPH_DEFAULT_LOCALE_CJK :
                    { rOpt.bRODefaultLanguage_CJK = pROStates[i]; rOpt.nDefaultLanguage_CJK = lcl_CfgAnyToLanguage( rVal );    } break;
                case UPH_DEFAULT_LOCALE_CTL :
                    { rOpt.bRODefaultLanguage_CTL = pROStates[i]; rOpt.nDefaultLanguage_CTL = lcl_CfgAnyToLanguage( rVal );    } break;
 
                case UPH_IS_SPELL_UPPER_CASE :
                    { rOpt.bROIsSpellUpperCase = pROStates[i]; rVal >>= rOpt.bIsSpellUpperCase; } break;
                case UPH_IS_SPELL_WITH_DIGITS :
                    { rOpt.bROIsSpellWithDigits = pROStates[i]; rVal >>= rOpt.bIsSpellWithDigits;    } break;
                case UPH_IS_SPELL_CLOSED_COMPOUND :
                    { rOpt.bROIsSpellClosedCompound = pROStates[i]; rVal >>= rOpt.bIsSpellClosedCompound;    } break;
                case UPH_IS_SPELL_HYPHENATED_COMPOUND :
                    { rOpt.bROIsSpellHyphenatedCompound = pROStates[i]; rVal >>= rOpt.bIsSpellHyphenatedCompound;    } break;
 
                case UPH_IS_SPELL_AUTO :
                    { rOpt.bROIsSpellAuto = pROStates[i]; rVal >>= rOpt.bIsSpellAuto;  } break;
                case UPH_IS_SPELL_SPECIAL :
                    { rOpt.bROIsSpellSpecial = pROStates[i]; rVal >>= rOpt.bIsSpellSpecial;   } break;
                case UPH_IS_WRAP_REVERSE :
                    { rOpt.bROIsSpellReverse = pROStates[i]; rVal >>= rOpt.bIsSpellReverse;   } break;
 
                case UPH_HYPH_MIN_LEADING :
                    { rOpt.bROHyphMinLeading = pROStates[i]; rVal >>= rOpt.nHyphMinLeading;   } break;
                case UPH_HYPH_MIN_TRAILING :
                    { rOpt.bROHyphMinTrailing = pROStates[i]; rVal >>= rOpt.nHyphMinTrailing;  } break;
                case UPH_HYPH_MIN_WORD_LENGTH :
                    { rOpt.bROHyphMinWordLength = pROStates[i]; rVal >>= rOpt.nHyphMinWordLength;    } break;
                case UPH_IS_HYPH_SPECIAL :
                    { rOpt.bROIsHyphSpecial = pROStates[i]; rVal >>= rOpt.bIsHyphSpecial;    } break;
                case UPH_IS_HYPH_AUTO :
                    { rOpt.bROIsHyphAuto = pROStates[i]; rVal >>= rOpt.bIsHyphAuto;   } break;
 
                case UPH_ACTIVE_CONVERSION_DICTIONARIES : { rOpt.bROActiveConvDics = pROStates[i]; rVal >>= rOpt.aActiveConvDics;   } break;
 
                case UPH_IS_IGNORE_POST_POSITIONAL_WORD :
                    { rOpt.bROIsIgnorePostPositionalWord = pROStates[i]; rVal >>= rOpt.bIsIgnorePostPositionalWord;  } break;
                case UPH_IS_AUTO_CLOSE_DIALOG :
                    { rOpt.bROIsAutoCloseDialog = pROStates[i]; rVal >>= rOpt.bIsAutoCloseDialog;  } break;
                case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :
                    { rOpt.bROIsShowEntriesRecentlyUsedFirst = pROStates[i]; rVal >>= rOpt.bIsShowEntriesRecentlyUsedFirst;  } break;
                case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :
                    { rOpt.bROIsAutoReplaceUniqueEntries = pROStates[i]; rVal >>= rOpt.bIsAutoReplaceUniqueEntries;  } break;
 
                case UPH_IS_DIRECTION_TO_SIMPLIFIED :
                    {
                        rOpt.bROIsDirectionToSimplified = pROStates[i];
                        if( ! (rVal >>= rOpt.bIsDirectionToSimplified) )
                        {
                            //default is locale dependent:
                            if (MsLangId::isTraditionalChinese(rOpt.nDefaultLanguage_CJK))
                            {
                                rOpt.bIsDirectionToSimplified = false;
                            }
                            else
                            {
                                rOpt.bIsDirectionToSimplified = true;
                            }
                        }
                    } break;
                case UPH_IS_USE_CHARACTER_VARIANTS :
                    { rOpt.bROIsUseCharacterVariants = pROStates[i]; rVal >>= rOpt.bIsUseCharacterVariants;  } break;
                case UPH_IS_TRANSLATE_COMMON_TERMS :
                    { rOpt.bROIsTranslateCommonTerms = pROStates[i]; rVal >>= rOpt.bIsTranslateCommonTerms;  } break;
                case UPH_IS_REVERSE_MAPPING :
                    { rOpt.bROIsReverseMapping = pROStates[i]; rVal >>= rOpt.bIsReverseMapping;  } break;
 
                case UPH_DATA_FILES_CHANGED_CHECK_VALUE :
                    { rOpt.bRODataFilesChangedCheckValue = pROStates[i]; rVal >>= rOpt.nDataFilesChangedCheckValue;  } break;
 
                case UPH_IS_GRAMMAR_AUTO:
                    { rOpt.bROIsGrammarAuto = pROStates[i]; rVal >>= rOpt.bIsGrammarAuto; }
                break;
                case UPH_IS_GRAMMAR_INTERACTIVE:
                    { rOpt.bROIsGrammarInteractive = pROStates[i]; rVal >>= rOpt.bIsGrammarInteractive; }
                break;
 
                default:
                    SAL_WARN( "unotools.config", "unexpected case" );
            }
        }
 
        bRes = true;
    }
    DBG_ASSERT( bRes, "LoadOptions failed" );
}
 
bool SvtLinguConfigItem::SaveOptions( const uno::Sequence< OUString > &rProperyNames )
{
    if (!IsModified())
        return true;
 
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
 
    bool bRet = false;
 
    sal_Int32 nProps = rProperyNames.getLength();
    uno::Sequence< uno::Any > aValues( nProps );
    uno::Any *pValue = aValues.getArray();
 
    if (nProps  &&  aValues.getLength() == nProps)
    {
        const SvtLinguOptions &rOpt = aOpt;
 
        OUString aTmp( lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage ) );
        *pValue++ <<= aTmp;                               //   0
        *pValue++ <<= rOpt.aActiveDics;                   //   1
        *pValue++ <<= rOpt.bIsUseDictionaryList;        //   2
        *pValue++ <<= rOpt.bIsIgnoreControlCharacters;  //   3
        aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CJK );
        *pValue++ <<= aTmp;                               //   5
        aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CTL );
        *pValue++ <<= aTmp;                               //   6
 
        *pValue++ <<= rOpt.bIsSpellUpperCase;          //   7
        *pValue++ <<= rOpt.bIsSpellWithDigits;         //   8
        *pValue++ <<= rOpt.bIsSpellAuto;               //   9
        *pValue++ <<= rOpt.bIsSpellSpecial;            //  10
        *pValue++ <<= rOpt.bIsSpellClosedCompound;     //  11
        *pValue++ <<= rOpt.bIsSpellHyphenatedCompound; //  12
        *pValue++ <<= rOpt.bIsSpellReverse;            //  13
 
        *pValue++ <<= rOpt.nHyphMinLeading;            //  14
        *pValue++ <<= rOpt.nHyphMinTrailing;           //  15
        *pValue++ <<= rOpt.nHyphMinWordLength;         //  16
        *pValue++ <<= rOpt.bIsHyphSpecial;             //  17
        *pValue++ <<= rOpt.bIsHyphAuto;                //  18
 
        *pValue++ <<= rOpt.aActiveConvDics;               //   19
 
        *pValue++ <<= rOpt.bIsIgnorePostPositionalWord; //  20
        *pValue++ <<= rOpt.bIsAutoCloseDialog;          //  21
        *pValue++ <<= rOpt.bIsShowEntriesRecentlyUsedFirst; //  22
        *pValue++ <<= rOpt.bIsAutoReplaceUniqueEntries; //  23
 
        *pValue++ <<= rOpt.bIsDirectionToSimplified; //  24
        *pValue++ <<= rOpt.bIsUseCharacterVariants; //  25
        *pValue++ <<= rOpt.bIsTranslateCommonTerms; //  26
        *pValue++ <<= rOpt.bIsReverseMapping; //  27
 
        *pValue++ <<= rOpt.nDataFilesChangedCheckValue; //  28
        *pValue++ <<= rOpt.bIsGrammarAuto; //  29
        *pValue++ <<= rOpt.bIsGrammarInteractive; // 30
 
        bRet |= PutProperties( rProperyNames, aValues );
    }
 
    if (bRet)
        ClearModified();
 
    return bRet;
}
 
bool SvtLinguConfigItem::IsReadOnly( std::u16string_view rPropertyName ) const
{
    bool bReadOnly = false;
    sal_Int32 nHdl;
    if (GetHdlByName( nHdl, rPropertyName ))
        bReadOnly = IsReadOnly( nHdl );
    return bReadOnly;
}
 
bool SvtLinguConfigItem::IsReadOnly( sal_Int32 nPropertyHandle ) const
{
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
 
    bool bReadOnly = false;
 
    const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
    switch(nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST         : bReadOnly = rOpt.bROIsUseDictionaryList; break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS   : bReadOnly = rOpt.bROIsIgnoreControlCharacters; break;
        case UPH_IS_HYPH_AUTO                   : bReadOnly = rOpt.bROIsHyphAuto; break;
        case UPH_IS_HYPH_SPECIAL                : bReadOnly = rOpt.bROIsHyphSpecial; break;
        case UPH_IS_SPELL_AUTO                  : bReadOnly = rOpt.bROIsSpellAuto; break;
        case UPH_IS_SPELL_SPECIAL               : bReadOnly = rOpt.bROIsSpellSpecial; break;
        case UPH_IS_WRAP_REVERSE                : bReadOnly = rOpt.bROIsSpellReverse; break;
        case UPH_DEFAULT_LANGUAGE               : bReadOnly = rOpt.bRODefaultLanguage; break;
        case UPH_IS_SPELL_CLOSED_COMPOUND       : bReadOnly = rOpt.bROIsSpellClosedCompound; break;
        case UPH_IS_SPELL_HYPHENATED_COMPOUND   : bReadOnly = rOpt.bROIsSpellHyphenatedCompound; break;
        case UPH_IS_SPELL_WITH_DIGITS           : bReadOnly = rOpt.bROIsSpellWithDigits; break;
        case UPH_IS_SPELL_UPPER_CASE            : bReadOnly = rOpt.bROIsSpellUpperCase; break;
        case UPH_HYPH_MIN_LEADING               : bReadOnly = rOpt.bROHyphMinLeading; break;
        case UPH_HYPH_MIN_TRAILING              : bReadOnly = rOpt.bROHyphMinTrailing; break;
        case UPH_HYPH_MIN_WORD_LENGTH           : bReadOnly = rOpt.bROHyphMinWordLength; break;
        case UPH_ACTIVE_DICTIONARIES            : bReadOnly = rOpt.bROActiveDics; break;
        case UPH_ACTIVE_CONVERSION_DICTIONARIES : bReadOnly = rOpt.bROActiveConvDics; break;
        case UPH_DEFAULT_LOCALE                 : bReadOnly = rOpt.bRODefaultLanguage; break;
        case UPH_DEFAULT_LOCALE_CJK             : bReadOnly = rOpt.bRODefaultLanguage_CJK; break;
        case UPH_DEFAULT_LOCALE_CTL             : bReadOnly = rOpt.bRODefaultLanguage_CTL; break;
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       bReadOnly = rOpt.bROIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 bReadOnly = rOpt.bROIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  bReadOnly = rOpt.bROIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       bReadOnly = rOpt.bROIsAutoReplaceUniqueEntries; break;
        case UPH_IS_DIRECTION_TO_SIMPLIFIED : bReadOnly = rOpt.bROIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS : bReadOnly = rOpt.bROIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS : bReadOnly = rOpt.bROIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :        bReadOnly = rOpt.bROIsReverseMapping; break;
        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       bReadOnly = rOpt.bRODataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       bReadOnly = rOpt.bROIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                bReadOnly = rOpt.bROIsGrammarInteractive; break;
        default :
            SAL_WARN( "unotools.config", "unexpected property handle" );
    }
    return bReadOnly;
}
 
static SvtLinguConfigItem *pCfgItem = nullptr;
static sal_Int32           nCfgItemRefCount = 0;
 
constexpr OUString aG_Dictionaries = u"Dictionaries"_ustr;
 
SvtLinguConfig::SvtLinguConfig()
{
    // Global access, must be guarded (multithreading)
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
    ++nCfgItemRefCount;
}
 
SvtLinguConfig::~SvtLinguConfig()
{
    if (pCfgItem && pCfgItem->IsModified())
        pCfgItem->Commit();
 
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
 
    if (--nCfgItemRefCount <= 0)
    {
        delete pCfgItem;
        pCfgItem = nullptr;
    }
}
 
SvtLinguConfigItem & SvtLinguConfig::GetConfigItem()
{
    // Global access, must be guarded (multithreading)
    std::unique_lock aGuard(theSvtLinguConfigItemMutex());
    if (!pCfgItem)
    {
        pCfgItem = new SvtLinguConfigItem;
        aGuard.unlock();
        ItemHolder1::holdConfigItem(EItem::LinguConfig);
    }
    return *pCfgItem;
}
 
uno::Sequence< OUString > SvtLinguConfig::GetNodeNames( const OUString &rNode ) const
{
    return GetConfigItem().GetNodeNames( rNode );
}
 
uno::Sequence< uno::Any > SvtLinguConfig::GetProperties( const uno::Sequence< OUString > &rNames ) const
{
    return GetConfigItem().GetProperties(rNames);
}
 
bool SvtLinguConfig::ReplaceSetProperties(
        const OUString &rNode, const uno::Sequence< beans::PropertyValue >& rValues )
{
    return GetConfigItem().ReplaceSetProperties( rNode, rValues );
}
 
uno::Any SvtLinguConfig::GetProperty( std::u16string_view rPropertyName ) const
{
    return GetConfigItem().GetProperty( rPropertyName );
}
 
uno::Any SvtLinguConfig::GetProperty( sal_Int32 nPropertyHandle ) const
{
    return GetConfigItem().GetProperty( nPropertyHandle );
}
 
bool SvtLinguConfig::SetProperty( std::u16string_view rPropertyName, const uno::Any &rValue )
{
    return GetConfigItem().SetProperty( rPropertyName, rValue );
}
 
bool SvtLinguConfig::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
{
    return GetConfigItem().SetProperty( nPropertyHandle, rValue );
}
 
void SvtLinguConfig::GetOptions( SvtLinguOptions &rOptions ) const
{
    GetConfigItem().GetOptions(rOptions);
}
 
bool SvtLinguConfig::IsReadOnly( std::u16string_view rPropertyName ) const
{
    return GetConfigItem().IsReadOnly( rPropertyName );
}
 
bool SvtLinguConfig::GetElementNamesFor(
     const OUString &rNodeName,
     uno::Sequence< OUString > &rElementNames ) const
{
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY );
        if (!xNA)
            return false;
        rElementNames = xNA->getElementNames();
        bSuccess = true;
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}
 
bool SvtLinguConfig::GetSupportedDictionaryFormatsFor(
    const OUString &rSetName,
    const OUString &rSetEntry,
    uno::Sequence< OUString > &rFormatList ) const
{
    if (rSetName.isEmpty() || rSetEntry.isEmpty())
        return false;
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY );
        if (!xNA)
            return false;
        if (xNA->getByName( u"SupportedDictionaryFormats"_ustr ) >>= rFormatList)
            bSuccess = true;
        DBG_ASSERT( rFormatList.hasElements(), "supported dictionary format list is empty" );
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}
 
bool SvtLinguConfig::GetLocaleListFor( const OUString &rSetName, const OUString &rSetEntry, css::uno::Sequence< OUString > &rLocaleList ) const
{
    if (rSetName.isEmpty() || rSetEntry.isEmpty())
        return false;
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY );
        if (!xNA)
            return false;
        if (xNA->getByName( u"Locales"_ustr ) >>= rLocaleList)
            bSuccess = true;
        DBG_ASSERT( rLocaleList.hasElements(), "Locale list is empty" );
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}
 
static bool lcl_GetFileUrlFromOrigin(
    OUString /*out*/ &rFileUrl,
    const OUString &rOrigin )
{
    OUString aURL(
        comphelper::getExpandedUri(
            comphelper::getProcessComponentContext(), rOrigin));
    if (aURL.startsWith( FILE_PROTOCOL ))
    {
        rFileUrl = aURL;
        return true;
    }
    else
    {
        SAL_WARN(
            "unotools.config", "not a file URL, <" << aURL << ">" );
        return false;
    }
}
 
bool SvtLinguConfig::GetDictionaryEntry(
    const OUString &rNodeName,
    SvtLinguConfigDictionaryEntry &rDicEntry ) const
{
    if (rNodeName.isEmpty())
        return false;
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( aG_Dictionaries ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY_THROW );
 
        // read group data...
        uno::Sequence< OUString >  aLocations;
        OUString                   aFormatName;
        uno::Sequence< OUString >  aLocaleNames;
        bSuccess =  (xNA->getByName( u"Locations"_ustr ) >>= aLocations)  &&
                    (xNA->getByName( u"Format"_ustr )    >>= aFormatName) &&
                    (xNA->getByName( u"Locales"_ustr )   >>= aLocaleNames);
        DBG_ASSERT( aLocations.hasElements(), "Dictionary locations not set" );
        DBG_ASSERT( !aFormatName.isEmpty(), "Dictionary format name not set" );
        DBG_ASSERT( aLocaleNames.hasElements(), "No locales set for the dictionary" );
 
        // if successful continue
        if (bSuccess)
        {
            // get file URL's for the locations
            for (OUString& rLocation : asNonConstRange(aLocations))
            {
                if (!lcl_GetFileUrlFromOrigin( rLocation, rLocation ))
                    bSuccess = false;
            }
 
            // if everything was fine return the result
            if (bSuccess)
            {
                rDicEntry.aLocations    = std::move(aLocations);
                rDicEntry.aFormatName   = aFormatName;
                rDicEntry.aLocaleNames  = std::move(aLocaleNames);
            }
        }
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}
 
uno::Sequence< OUString > SvtLinguConfig::GetDisabledDictionaries() const
{
    uno::Sequence< OUString > aResult;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY );
        if (!xNA)
            return aResult;
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return aResult;
        xNA->getByName( u"DisabledDictionaries"_ustr ) >>= aResult;
    }
    catch (uno::Exception &)
    {
    }
    return aResult;
}
 
std::vector< SvtLinguConfigDictionaryEntry > SvtLinguConfig::GetActiveDictionariesByFormat(
    std::u16string_view rFormatName ) const
{
    std::vector< SvtLinguConfigDictionaryEntry > aRes;
    if (rFormatName.empty())
        return aRes;
 
    try
    {
        uno::Sequence< OUString > aElementNames;
        GetElementNamesFor( aG_Dictionaries, aElementNames );
 
        const uno::Sequence< OUString > aDisabledDics( GetDisabledDictionaries() );
 
        SvtLinguConfigDictionaryEntry aDicEntry;
        for (const OUString& rElementName : aElementNames)
        {
            // does dictionary match the format we are looking for?
            if (GetDictionaryEntry( rElementName, aDicEntry ) &&
                aDicEntry.aFormatName == rFormatName)
            {
                // check if it is active or not
                bool bDicIsActive = std::none_of(aDisabledDics.begin(), aDisabledDics.end(),
                    [&rElementName](const OUString& rDic) { return rDic == rElementName; });
 
                if (bDicIsActive)
                {
                    DBG_ASSERT( !aDicEntry.aFormatName.isEmpty(),
                            "FormatName not set" );
                    DBG_ASSERT( aDicEntry.aLocations.hasElements(),
                            "Locations not set" );
                    DBG_ASSERT( aDicEntry.aLocaleNames.hasElements(),
                            "Locales not set" );
                    aRes.push_back( aDicEntry );
                }
            }
        }
    }
    catch (uno::Exception &)
    {
    }
 
    return aRes;
}
 
uno::Reference< util::XChangesBatch > const & SvtLinguConfig::GetMainUpdateAccess() const
{
    if (m_xMainUpdateAccess)
        return m_xMainUpdateAccess;
    try
    {
        // get configuration provider
        const uno::Reference< uno::XComponentContext >& xContext = comphelper::getProcessComponentContext();
        uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider =
                configuration::theDefaultProvider::get( xContext );
 
        // get configuration update access
        beans::PropertyValue aValue;
        aValue.Name  = "nodepath";
        aValue.Value <<= u"org.openoffice.Office.Linguistic"_ustr;
        uno::Sequence< uno::Any > aProps{ uno::Any(aValue) };
        m_xMainUpdateAccess.set(
                xConfigurationProvider->createInstanceWithArguments(
                    u"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr, aProps),
                    uno::UNO_QUERY );
    }
    catch (uno::Exception &)
    {
    }
 
    return m_xMainUpdateAccess;
}
 
OUString SvtLinguConfig::GetVendorImageUrl_Impl(
    const OUString &rServiceImplName,
    const OUString &rImageName ) const
{
    OUString aRes;
    try
    {
        uno::Reference< container::XNameAccess > xImagesNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xImagesNA.set( xImagesNA->getByName(u"Images"_ustr), uno::UNO_QUERY_THROW );
 
        uno::Reference< container::XNameAccess > xNA( xImagesNA->getByName(u"ServiceNameEntries"_ustr), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rServiceImplName ), uno::UNO_QUERY_THROW );
        uno::Any aAny(xNA->getByName(u"VendorImagesNode"_ustr));
        OUString aVendorImagesNode;
        if (aAny >>= aVendorImagesNode)
        {
            xNA = std::move(xImagesNA);
            xNA.set( xNA->getByName(u"VendorImages"_ustr), uno::UNO_QUERY_THROW );
            xNA.set( xNA->getByName( aVendorImagesNode ), uno::UNO_QUERY_THROW );
            aAny = xNA->getByName( rImageName );
            OUString aTmp;
            if (aAny >>= aTmp)
            {
                if (lcl_GetFileUrlFromOrigin( aTmp, aTmp ))
                    aRes = aTmp;
            }
        }
    }
    catch (uno::Exception &)
    {
        DBG_UNHANDLED_EXCEPTION("unotools");
    }
    return aRes;
}
 
OUString SvtLinguConfig::GetSpellAndGrammarContextSuggestionImage(
    const OUString &rServiceImplName
) const
{
    OUString   aRes;
    if (!rServiceImplName.isEmpty())
    {
        aRes = GetVendorImageUrl_Impl( rServiceImplName, u"SpellAndGrammarContextMenuSuggestionImage"_ustr );
    }
    return aRes;
}
 
OUString SvtLinguConfig::GetSpellAndGrammarContextDictionaryImage(
    const OUString &rServiceImplName
) const
{
    OUString   aRes;
    if (!rServiceImplName.isEmpty())
    {
        aRes = GetVendorImageUrl_Impl( rServiceImplName, u"SpellAndGrammarContextMenuDictionaryImage"_ustr );
    }
    return aRes;
}
 
OUString SvtLinguConfig::GetSynonymsContextImage(
    const OUString &rServiceImplName
) const
{
    OUString   aRes;
    if (!rServiceImplName.isEmpty())
        aRes = GetVendorImageUrl_Impl(rServiceImplName, u"SynonymsContextMenuImage"_ustr);
    return aRes;
}
 
bool SvtLinguConfig::HasGrammarChecker() const
{
    bool bRes = false;
 
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName(u"ServiceManager"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return false;
        xNA.set( xNA->getByName(u"GrammarCheckerList"_ustr), uno::UNO_QUERY );
        if (!xNA)
            return false;
 
        uno::Sequence< OUString > aElementNames( xNA->getElementNames() );
        bRes = aElementNames.hasElements();
    }
    catch (const uno::Exception&)
    {
    }
 
    return bRes;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1019 Compound assignment expression is used inside condition.

V1019 Compound assignment expression is used inside condition.