/* -*- 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 <com/sun/star/uno/Exception.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/document/XTypeDetection.hpp>
#include <com/sun/star/container/XContainerQuery.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
 
#include <comphelper/sequenceashashmap.hxx>
 
#include <sot/exchange.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <svl/stritem.hxx>
 
#include <comphelper/processfactory.hxx>
 
#include <sal/types.h>
#include <com/sun/star/uno/Reference.hxx>
#include <unotools/moduleoptions.hxx>
#include <unotools/mediadescriptor.hxx>
#include <tools/urlobj.hxx>
 
#include <unotools/syslocale.hxx>
#include <unotools/charclass.hxx>
 
#include <sfx2/docfilt.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfxtypes.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/strings.hrc>
#include <sfx2/sfxresid.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/sfxsids.hrc>
#include "fltlst.hxx"
#include <arrdecl.hxx>
 
#include <vector>
#include <memory>
 
#if defined(DBG_UTIL)
unsigned SfxStack::nLevel = 0;
#endif
 
using namespace com::sun::star;
 
static SfxFilterList_Impl* pFilterArr = nullptr;
static bool bFirstRead = true;
 
static void CreateFilterArr()
{
    static SfxFilterList_Impl theSfxFilterArray;
    pFilterArr = &theSfxFilterArray;
    static SfxFilterListener theSfxFilterListener;
}
 
static OUString ToUpper_Impl( const OUString &rStr )
{
    return SvtSysLocale().GetCharClass().uppercase( rStr );
}
 
class SfxFilterContainer_Impl
{
public:
    OUString            aName;
 
    explicit SfxFilterContainer_Impl( OUString _aName )
        : aName(std::move( _aName ))
    {
    }
};
 
std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4EA(const OUString& rEA, SfxFilterFlags nMust, SfxFilterFlags nDont) const
{
    SfxFilterMatcher aMatch(pImpl->aName);
    return aMatch.GetFilter4EA(rEA, nMust, nDont);
}
 
std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4Extension(const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont) const
{
    SfxFilterMatcher aMatch(pImpl->aName);
    return aMatch.GetFilter4Extension(rExt, nMust, nDont);
}
 
std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4FilterName(const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont) const
{
    SfxFilterMatcher aMatch(pImpl->aName);
    return aMatch.GetFilter4FilterName(rName, nMust, nDont);
}
 
std::shared_ptr<const SfxFilter> SfxFilterContainer::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    SfxFilterMatcher aMatch( pImpl->aName );
    return aMatch.GetAnyFilter( nMust, nDont );
}
 
 
SfxFilterContainer::SfxFilterContainer( const OUString& rName )
   : pImpl( new SfxFilterContainer_Impl( rName ) )
{
}
 
 
SfxFilterContainer::~SfxFilterContainer()
{
}
 
 
OUString const & SfxFilterContainer::GetName() const
{
    return pImpl->aName;
}
 
std::shared_ptr<const SfxFilter> SfxFilterContainer::GetDefaultFilter_Impl( std::u16string_view rName )
{
    // Try to find out the type of factory.
    // Interpret given name as Service- and ShortName!
    SvtModuleOptions aOpt;
    SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(rName);
    if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
        eFactory = SvtModuleOptions::ClassifyFactoryByShortName(rName);
 
    // could not classify factory by its service nor by its short name.
    // Must be an unknown factory! => return NULL
    if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
        return nullptr;
 
    // For the following code we need some additional information.
    const OUString& sServiceName   = aOpt.GetFactoryName(eFactory);
    OUString sDefaultFilter = aOpt.GetFactoryDefaultFilter(eFactory);
 
    // Try to get the default filter. Don't forget to verify it.
    // May the set default filter does not exists any longer or
    // does not fit the given factory.
    const SfxFilterMatcher aMatcher;
    std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4FilterName(sDefaultFilter);
 
    if (
        pFilter &&
        !pFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName)
       )
    {
        pFilter = nullptr;
    }
 
    // If at least no default filter could be located - use any filter of this
    // factory.
    if (!pFilter)
    {
        if ( bFirstRead )
            ReadFilters_Impl();
 
        for (const std::shared_ptr<const SfxFilter>& pCheckFilter : *pFilterArr)
        {
            if ( pCheckFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName) )
            {
                pFilter = pCheckFilter;
                break;
            }
        }
    }
 
    return pFilter;
}
 
 
// Impl-Data is shared between all FilterMatchers of the same factory
class SfxFilterMatcher_Impl
{
public:
    OUString                    aName;
    mutable SfxFilterList_Impl* pList;      // is created on demand
 
    void InitForIterating() const;
    void Update() const;
    explicit SfxFilterMatcher_Impl(OUString _aName)
        : aName(std::move(_aName))
        , pList(nullptr)
    {
    }
    ~SfxFilterMatcher_Impl()
    {
        // SfxFilterMatcher_Impl::InitForIterating() will set pList to
        // either the global filter array matcher pFilterArr, or to
        // a new SfxFilterList_Impl.
        if (pList != pFilterArr)
            delete pList;
    }
};
 
namespace
{
    std::vector<std::unique_ptr<SfxFilterMatcher_Impl> > aImplArr;
    int nSfxFilterMatcherCount;
 
    SfxFilterMatcher_Impl & getSfxFilterMatcher_Impl(const OUString &rName)
    {
        OUString aName;
 
        if (!rName.isEmpty())
            aName = SfxObjectShell::GetServiceNameFromFactory(rName);
 
        // find the impl-Data of any comparable FilterMatcher that was created
        // previously
        for (std::unique_ptr<SfxFilterMatcher_Impl>& aImpl : aImplArr)
            if (aImpl->aName == aName)
                return *aImpl;
 
        // first Matcher created for this factory
        aImplArr.push_back(std::make_unique<SfxFilterMatcher_Impl>(aName));
        return *aImplArr.back();
    }
}
 
SfxFilterMatcher::SfxFilterMatcher( const OUString& rName )
    : m_rImpl( getSfxFilterMatcher_Impl(rName) )
{
    ++nSfxFilterMatcherCount;
}
 
SfxFilterMatcher::SfxFilterMatcher()
    : m_rImpl( getSfxFilterMatcher_Impl(OUString()) )
{
    // global FilterMatcher always uses global filter array (also created on
    // demand)
    ++nSfxFilterMatcherCount;
}
 
SfxFilterMatcher::~SfxFilterMatcher()
{
    --nSfxFilterMatcherCount;
    if (nSfxFilterMatcherCount == 0)
        aImplArr.clear();
}
 
void SfxFilterMatcher_Impl::Update() const
{
    if ( pList )
    {
        // this List was already used
        pList->clear();
        for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr)
        {
            if ( pFilter->GetServiceName() == aName )
                pList->push_back( pFilter );
        }
    }
}
 
void SfxFilterMatcher_Impl::InitForIterating() const
{
    if ( pList )
        return;
 
    if ( bFirstRead )
        // global filter array has not been created yet
        SfxFilterContainer::ReadFilters_Impl();
 
    if ( !aName.isEmpty() )
    {
        // matcher of factory: use only filters of that document type
        pList = new SfxFilterList_Impl;
        Update();
    }
    else
    {
        // global matcher: use global filter array
        pList = pFilterArr;
    }
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    m_rImpl.InitForIterating();
    for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
    {
        SfxFilterFlags nFlags = pFilter->GetFilterFlags();
        if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) )
            return pFilter;
    }
 
    return nullptr;
}
 
 
ErrCode  SfxFilterMatcher::GuessFilterIgnoringContent(
    SfxMedium const & rMedium,
    std::shared_ptr<const SfxFilter>& rpFilter ) const
{
    uno::Reference<document::XTypeDetection> xDetection(
        comphelper::getProcessServiceFactory()->createInstance(u"com.sun.star.document.TypeDetection"_ustr), uno::UNO_QUERY);
 
    OUString sTypeName;
    try
    {
        sTypeName = xDetection->queryTypeByURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
    }
    catch (uno::Exception&)
    {
    }
 
    rpFilter = nullptr;
    if ( !sTypeName.isEmpty() )
    {
        // make sure filter list is initialized
        m_rImpl.InitForIterating();
        rpFilter = GetFilter4EA( sTypeName );
    }
 
    return rpFilter ? ERRCODE_NONE : ERRCODE_ABORT;
}
 
 
ErrCode  SfxFilterMatcher::GuessFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    return GuessFilterControlDefaultUI( rMedium, rpFilter, nMust, nDont );
}
 
 
ErrCode  SfxFilterMatcher::GuessFilterControlDefaultUI( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    std::shared_ptr<const SfxFilter> pOldFilter = rpFilter;
 
    // no detection service -> nothing to do !
    uno::Reference<document::XTypeDetection> xDetection(
        comphelper::getProcessServiceFactory()->createInstance(u"com.sun.star.document.TypeDetection"_ustr), uno::UNO_QUERY);
 
    if (!xDetection.is())
        return ERRCODE_ABORT;
 
    try
    {
        // open the stream one times only ...
        // Otherwise it will be tried more than once and show the same interaction more than once ...
 
        OUString sURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
        uno::Reference< io::XInputStream > xInStream = rMedium.GetInputStream();
        OUString aFilterName;
        OUString sTypeName;
 
        // stream exists => deep detection (with preselection ... if possible)
        if (xInStream.is())
        {
            utl::MediaDescriptor aDescriptor;
 
            aDescriptor[utl::MediaDescriptor::PROP_URL               ] <<= sURL;
            aDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM       ] <<= xInStream;
            aDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= rMedium.GetInteractionHandler();
            SfxStringItem const * it = rMedium.GetItemSet().GetItem(SID_REFERER);
            if (it != nullptr) {
                aDescriptor[utl::MediaDescriptor::PROP_REFERRER]
                    <<= it->GetValue();
            }
 
            if ( !m_rImpl.aName.isEmpty() )
                aDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= m_rImpl.aName;
 
            if ( pOldFilter )
            {
                aDescriptor[utl::MediaDescriptor::PROP_TYPENAME  ] <<= pOldFilter->GetTypeName();
                aDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= pOldFilter->GetFilterName();
            }
 
            uno::Sequence< beans::PropertyValue > lDescriptor = aDescriptor.getAsConstPropertyValueList();
            sTypeName = xDetection->queryTypeByDescriptor(lDescriptor, true); // lDescriptor is used as In/Out param ... don't use aDescriptor.getAsConstPropertyValueList() directly!
 
            for (const auto& rProp : lDescriptor)
            {
                if (rProp.Name == "FilterName")
                    // Type detection picked a preferred filter for this format.
                    aFilterName = rProp.Value.get<OUString>();
            }
        }
        // no stream exists => try flat detection without preselection as fallback
        else
            sTypeName = xDetection->queryTypeByURL(sURL);
 
        if (!sTypeName.isEmpty())
        {
            std::shared_ptr<const SfxFilter> xNewFilter;
            if (!aFilterName.isEmpty())
                // Type detection returned a suitable filter for this.  Use it.
                xNewFilter = SfxFilter::GetFilterByName(aFilterName);
 
            // fdo#78742 respect requested document service if set
            if (!xNewFilter || (!m_rImpl.aName.isEmpty()
                             && m_rImpl.aName != xNewFilter->GetServiceName()))
            {
                // detect filter by given type
                // In case of this matcher is bound to a particular document type:
                // If there is no acceptable type for this document at all, the type detection has possibly returned something else.
                // The DocumentService property is only a preselection, and all preselections are considered as optional!
                // This "wrong" type will be sorted out now because we match only allowed filters to the detected type
                uno::Sequence< beans::NamedValue > lQuery { { u"Name"_ustr, css::uno::Any(sTypeName) } };
 
                xNewFilter = GetFilterForProps(lQuery, nMust, nDont);
            }
 
            if (xNewFilter)
            {
                rpFilter = std::move(xNewFilter);
                return ERRCODE_NONE;
            }
        }
    }
    catch (const uno::Exception&)
    {}
 
    return ERRCODE_ABORT;
}
 
 
bool SfxFilterMatcher::IsFilterInstalled_Impl( const std::shared_ptr<const SfxFilter>& pFilter )
{
    if ( pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL )
    {
        // Here could a  re-installation be offered
        OUString aText( SfxResId(STR_FILTER_NOT_INSTALLED) );
        aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() );
        std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
                                                       VclMessageType::Question, VclButtonsType::YesNo,
                                                       aText));
        xQueryBox->set_default_response(RET_YES);
 
        short nRet = xQueryBox->run();
        if ( nRet == RET_YES )
        {
#ifdef DBG_UTIL
            // Start Setup
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          u"Here should the Setup now be starting!"_ustr));
            xInfoBox->run();
#endif
            // Installation must still give feedback if it worked or not,
            // then the  Filterflag be deleted
        }
 
        return ( !(pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL) );
    }
    else if ( pFilter->GetFilterFlags() & SfxFilterFlags::CONSULTSERVICE )
    {
        OUString aText( SfxResId(STR_FILTER_CONSULT_SERVICE) );
        aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() );
        std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
                                                      VclMessageType::Info, VclButtonsType::Ok,
                                                      aText));
        xInfoBox->run();
        return false;
    }
    else
        return true;
}
 
 
ErrCode SfxFilterMatcher::DetectFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter ) const
/*  [Description]
 
    Here the Filter selection box is pulled up. Otherwise GuessFilter
 */
 
{
    std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
    if ( pFilter )
    {
        if( !IsFilterInstalled_Impl( pFilter ) )
            pFilter = nullptr;
        else
        {
            const SfxStringItem* pSalvageItem = rMedium.GetItemSet().GetItem(SID_DOC_SALVAGE, false);
            if ( ( pFilter->GetFilterFlags() & SfxFilterFlags::PACKED ) && pSalvageItem )
                // Salvage is always done without packing
                pFilter = nullptr;
        }
    }
 
    bool bPreview = rMedium.IsPreview_Impl();
    const SfxStringItem* pReferer = rMedium.GetItemSet().GetItem(SID_REFERER, false);
    if ( bPreview && rMedium.IsRemote() && ( !pReferer || !pReferer->GetValue().match("private:searchfolder:") ) )
        return ERRCODE_ABORT;
 
    ErrCode nErr = GuessFilter( rMedium, pFilter );
    if ( nErr == ERRCODE_ABORT )
        return nErr;
 
    if ( nErr == ERRCODE_IO_PENDING )
    {
        rpFilter = pFilter;
        return nErr;
    }
 
    if ( !pFilter )
    {
        std::shared_ptr<const SfxFilter> pInstallFilter;
 
        // Now test the filter which are not installed (ErrCode is irrelevant)
        GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::CONSULTSERVICE );
        if ( pInstallFilter )
        {
            if ( IsFilterInstalled_Impl( pInstallFilter ) )
            {
                // Maybe the filter was installed afterwards.
                pFilter = std::move(pInstallFilter);
            }
        }
        else
        {
          // Now test the filter, which first must be obtained by Star
          // (ErrCode is irrelevant)
            GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::NONE );
            if ( pInstallFilter )
                IsFilterInstalled_Impl( pInstallFilter );
        }
    }
 
    bool bHidden = bPreview;
    const SfxStringItem* pFlags = rMedium.GetItemSet().GetItem(SID_OPTIONS, false);
    if ( !bHidden && pFlags )
    {
        OUString aFlags( pFlags->GetValue() );
        aFlags = aFlags.toAsciiUpperCase();
        if( -1 != aFlags.indexOf( 'H' ) )
            bHidden = true;
    }
    rpFilter = pFilter;
 
    if ( bHidden )
        nErr = pFilter ? ERRCODE_NONE : ERRCODE_ABORT;
    return nErr;
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilterForProps( const css::uno::Sequence < beans::NamedValue >& aSeq, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
    if( !xServiceManager )
        return nullptr;
 
    static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection";
    uno::Reference< container::XContainerQuery > xTypeCFG( xServiceManager->createInstance( sTypeDetection ), uno::UNO_QUERY );
    if ( !xTypeCFG )
        return nullptr;
 
    // make query for all types matching the properties
    uno::Reference < css::container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq );
    uno::Sequence<beans::PropertyValue> aProps;
    while ( xEnum->hasMoreElements() )
    {
        static constexpr OUStringLiteral sPreferredFilter = u"PreferredFilter";
        static constexpr OUStringLiteral sName = u"Name";
 
        xEnum->nextElement() >>= aProps;
        OUString aValue, aName;
        for( const auto & rPropVal : aProps)
        {
            if (rPropVal.Name == sPreferredFilter)
                rPropVal.Value >>= aValue;
            else if (rPropVal.Name == sName)
                rPropVal.Value >>= aName;
        }
 
        // try to get the preferred filter (works without loading all filters!)
        if ( !aValue.isEmpty() )
        {
            std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName( aValue );
            if ( !pFilter || (pFilter->GetFilterFlags() & nMust) != nMust || (pFilter->GetFilterFlags() & nDont ) )
                // check for filter flags
                // pFilter == 0: if preferred filter is a Writer filter, but Writer module is not installed
                continue;
 
            if ( !m_rImpl.aName.isEmpty() )
            {
                // if this is not the global FilterMatcher: check if filter matches the document type
                if ( pFilter->GetServiceName() != m_rImpl.aName )
                {
                    // preferred filter belongs to another document type; now we must search the filter
                    m_rImpl.InitForIterating();
                    pFilter = GetFilter4EA( aName, nMust, nDont );
                    if ( pFilter )
                        return pFilter;
                }
                else
                    return pFilter;
            }
            else
                return pFilter;
        }
    }
 
    return nullptr;
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Mime( const OUString& rMediaType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    if ( m_rImpl.pList )
    {
        for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
        {
            SfxFilterFlags nFlags = pFilter->GetFilterFlags();
            if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetMimeType() == rMediaType )
                return pFilter;
        }
 
        return nullptr;
    }
 
    css::uno::Sequence < css::beans::NamedValue > aSeq { { u"MediaType"_ustr, css::uno::Any(rMediaType) } };
    return GetFilterForProps( aSeq, nMust, nDont );
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4EA( const OUString& rType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    if ( m_rImpl.pList )
    {
        std::shared_ptr<const SfxFilter> pFirst;
        for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
        {
            SfxFilterFlags nFlags = pFilter->GetFilterFlags();
            if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetTypeName() == rType )
            {
                if (nFlags & SfxFilterFlags::PREFERED)
                    return pFilter;
                if (!pFirst)
                    pFirst = pFilter;
            }
        }
        if (pFirst)
            return pFirst;
 
        return nullptr;
    }
 
    css::uno::Sequence < css::beans::NamedValue > aSeq { { u"Name"_ustr, css::uno::Any(rType) } };
    return GetFilterForProps( aSeq, nMust, nDont );
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Extension( const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    if ( m_rImpl.pList )
    {
        if (OUString sExt = ToUpper_Impl(rExt); !sExt.isEmpty())
        {
            if (sExt[0] != '.')
                sExt = "." + sExt;
 
            for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
            {
                SfxFilterFlags nFlags = pFilter->GetFilterFlags();
                if ((nFlags & nMust) == nMust && !(nFlags & nDont))
                {
                    OUString sWildCard = ToUpper_Impl(pFilter->GetWildcard().getGlob());
 
                    WildCard aCheck(sWildCard, ';');
                    if (aCheck.Matches(sExt))
                        return pFilter;
                }
            }
        }
 
        return nullptr;
    }
 
    // Use extension without dot!
    OUString sExt( rExt );
    if ( sExt.startsWith(".") )
        sExt = sExt.copy(1);
 
    css::uno::Sequence < css::beans::NamedValue > aSeq
        { { u"Extensions"_ustr, css::uno::Any(uno::Sequence < OUString > { sExt } ) } };
    return GetFilterForProps( aSeq, nMust, nDont );
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4ClipBoardId( SotClipboardFormatId nId, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    if (nId == SotClipboardFormatId::NONE)
        return nullptr;
 
    css::uno::Sequence < css::beans::NamedValue > aSeq
        { { u"ClipboardFormat"_ustr, css::uno::Any(SotExchange::GetFormatName( nId )) } };
    return GetFilterForProps( aSeq, nMust, nDont );
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4UIName( std::u16string_view rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    m_rImpl.InitForIterating();
    std::shared_ptr<const SfxFilter> pFirstFilter;
    for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
    {
        SfxFilterFlags nFlags = pFilter->GetFilterFlags();
        if ( (nFlags & nMust) == nMust &&
             !(nFlags & nDont ) && pFilter->GetUIName() == rName )
        {
            if ( pFilter->GetFilterFlags() & SfxFilterFlags::PREFERED )
                return pFilter;
            else if ( !pFirstFilter )
                pFirstFilter = pFilter;
        }
    }
    return pFirstFilter;
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4FilterName( const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
{
    std::u16string_view aName( rName );
    sal_Int32 nIndex = rName.indexOf(": ");
    if (  nIndex != -1 )
    {
        SAL_WARN( "sfx.bastyp", "Old filter name used!");
        aName = rName.subView( nIndex + 2 );
    }
 
    if ( bFirstRead )
    {
        uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
        uno::Reference< container::XNameAccess >     xFilterCFG                                                ;
        uno::Reference< container::XNameAccess >     xTypeCFG                                                  ;
        if( xServiceManager.is() )
        {
            static constexpr OUStringLiteral sFilterFactory = u"com.sun.star.document.FilterFactory";
            static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection";
            xFilterCFG.set( xServiceManager->createInstance(  sFilterFactory ), uno::UNO_QUERY );
            xTypeCFG.set( xServiceManager->createInstance(  sTypeDetection ), uno::UNO_QUERY );
        }
 
        if( xFilterCFG.is() && xTypeCFG.is() )
        {
            if ( !pFilterArr )
                CreateFilterArr();
            else
            {
                for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr)
                {
                    SfxFilterFlags nFlags = pFilter->GetFilterFlags();
                    if ((nFlags & nMust) == nMust && !(nFlags & nDont) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName))
                        return pFilter;
                }
            }
 
            SfxFilterContainer::ReadSingleFilter_Impl( rName, xTypeCFG, xFilterCFG, false );
        }
    }
 
    SfxFilterList_Impl* pList = m_rImpl.pList;
    if ( !pList )
        pList = pFilterArr;
 
    for (const std::shared_ptr<const SfxFilter>& pFilter : *pList)
    {
        SfxFilterFlags nFlags = pFilter->GetFilterFlags();
        if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName))
            return pFilter;
    }
 
    return nullptr;
}
 
IMPL_LINK( SfxFilterMatcher, MaybeFileHdl_Impl, OUString*, pString, bool )
{
    std::shared_ptr<const SfxFilter> pFilter = GetFilter4Extension( *pString );
    return pFilter &&
        !pFilter->GetWildcard().Matches(u"") &&
        !pFilter->GetWildcard().Matches(u"*.*") &&
        !pFilter->GetWildcard().Matches(u"*");
}
 
 
SfxFilterMatcherIter::SfxFilterMatcherIter(
    const SfxFilterMatcher& rMatcher,
    SfxFilterFlags nOrMaskP, SfxFilterFlags nAndMaskP )
    : nOrMask( nOrMaskP ), nAndMask( nAndMaskP ),
      nCurrent(0), m_rMatch(rMatcher.m_rImpl)
{
    if( nOrMask == static_cast<SfxFilterFlags>(0xffff) ) //Due to faulty build on s
        nOrMask = SfxFilterFlags::NONE;
    m_rMatch.InitForIterating();
}
 
 
std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Find_Impl()
{
    std::shared_ptr<const SfxFilter> pFilter;
    while( nCurrent < m_rMatch.pList->size() )
    {
        pFilter = (*m_rMatch.pList)[nCurrent++];
        SfxFilterFlags nFlags = pFilter->GetFilterFlags();
        if( ((nFlags & nOrMask) == nOrMask ) && !(nFlags & nAndMask ) )
            break;
        pFilter = nullptr;
    }
 
    return pFilter;
}
 
std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::First()
{
    nCurrent = 0;
    return Find_Impl();
}
 
 
std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Next()
{
    return Find_Impl();
}
 
/*---------------------------------------------------------------
    helper to build own formatted string from given stringlist by
    using given separator
  ---------------------------------------------------------------*/
static OUString implc_convertStringlistToString( const uno::Sequence< OUString >& lList     ,
                                                 sal_Unicode                                        cSeparator,
                                                 std::u16string_view                                sPrefix   )
{
    OUStringBuffer   sString ( 1000 )           ;
    sal_Int32               nCount  = lList.getLength();
    sal_Int32               nItem   = 0                ;
    for( nItem=0; nItem<nCount; ++nItem )
    {
        if( !sPrefix.empty() )
        {
            sString.append( sPrefix );
        }
        sString.append( lList[nItem] );
        if( nItem+1<nCount )
        {
            sString.append( cSeparator );
        }
    }
    return sString.makeStringAndClear();
}
 
 
void SfxFilterContainer::ReadSingleFilter_Impl(
    const OUString& rName,
    const uno::Reference< container::XNameAccess >& xTypeCFG,
    const uno::Reference< container::XNameAccess >& xFilterCFG,
    bool bUpdate
    )
{
    OUString sFilterName( rName );
    SfxFilterList_Impl& rList = *pFilterArr;
    uno::Sequence< beans::PropertyValue > lFilterProperties;
    uno::Any aResult;
    try
    {
        aResult = xFilterCFG->getByName( sFilterName );
    }
    catch( container::NoSuchElementException& )
    {
        aResult = uno::Any();
    }
 
    if( !(aResult >>= lFilterProperties) )
        return;
 
    // collect information to add filter to container
    // (attention: some information aren't available on filter directly ... you must search for corresponding type too!)
    SfxFilterFlags       nFlags          = SfxFilterFlags::NONE;
    SotClipboardFormatId nClipboardId    = SotClipboardFormatId::NONE;
    sal_Int32       nFormatVersion  = 0 ;
    OUString sMimeType           ;
    OUString sType               ;
    OUString sUIName             ;
    OUString sHumanName          ;
    OUString sDefaultTemplate    ;
    OUString sUserData           ;
    OUString sExtension          ;
    OUString sServiceName        ;
    bool bEnabled = true         ;
 
    // first get directly available properties
    for (const auto& rFilterProperty : lFilterProperties)
    {
        if ( rFilterProperty.Name == "FileFormatVersion" )
        {
            rFilterProperty.Value >>= nFormatVersion;
        }
        else if ( rFilterProperty.Name == "TemplateName" )
        {
            rFilterProperty.Value >>= sDefaultTemplate;
        }
        else if ( rFilterProperty.Name == "Flags" )
        {
            sal_Int32 nTmp(0);
            rFilterProperty.Value >>= nTmp;
            assert((nTmp & ~o3tl::typed_flags<SfxFilterFlags>::mask) == 0);
            nFlags = static_cast<SfxFilterFlags>(nTmp);
        }
        else if ( rFilterProperty.Name == "UIName" )
        {
            rFilterProperty.Value >>= sUIName;
        }
        else if ( rFilterProperty.Name == "UserData" )
        {
            uno::Sequence< OUString > lUserData;
            rFilterProperty.Value >>= lUserData;
            sUserData = implc_convertStringlistToString( lUserData, ',', u"" );
        }
        else if ( rFilterProperty.Name == "DocumentService" )
        {
            rFilterProperty.Value >>= sServiceName;
        }
        else if (rFilterProperty.Name == "ExportExtension")
        {
            // Extension preferred by the filter.  This takes precedence
            // over those that are given in the file format type.
            rFilterProperty.Value >>= sExtension;
            sExtension = "*." + sExtension;
        }
        else if ( rFilterProperty.Name == "Type" )
        {
            rFilterProperty.Value >>= sType;
            // Try to get filter .. but look for any exceptions!
            // May be filter was deleted by another thread ...
            try
            {
                aResult = xTypeCFG->getByName( sType );
            }
            catch (const container::NoSuchElementException&)
            {
                aResult = uno::Any();
            }
 
            uno::Sequence< beans::PropertyValue > lTypeProperties;
            if( aResult >>= lTypeProperties )
            {
                // get indirect available properties then (types)
                for (const auto& rTypeProperty : lTypeProperties)
                {
                    if ( rTypeProperty.Name == "ClipboardFormat" )
                    {
                        rTypeProperty.Value >>= sHumanName;
                    }
                    else if ( rTypeProperty.Name == "MediaType" )
                    {
                        rTypeProperty.Value >>= sMimeType;
                    }
                    else if ( rTypeProperty.Name == "Extensions" )
                    {
                        if (sExtension.isEmpty())
                        {
                            uno::Sequence< OUString > lExtensions;
                            rTypeProperty.Value >>= lExtensions;
                            sExtension = implc_convertStringlistToString( lExtensions, ';', u"*." );
                        }
                    }
                }
            }
        }
        else if ( rFilterProperty.Name == "Enabled" )
        {
            rFilterProperty.Value >>= bEnabled;
        }
 
    }
 
    if ( sServiceName.isEmpty() )
        return;
 
    // old formats are found ... using HumanPresentableName!
    if( !sHumanName.isEmpty() )
    {
        nClipboardId = SotExchange::RegisterFormatName( sHumanName );
 
        // For external filters ignore clipboard IDs
        if(nFlags & SfxFilterFlags::STARONEFILTER)
        {
            nClipboardId = SotClipboardFormatId::NONE;
        }
    }
    // register SfxFilter
    // first erase module name from old filter names!
    // e.g: "scalc: DIF" => "DIF"
    sal_Int32 nStartRealName = sFilterName.indexOf( ": " );
    if( nStartRealName != -1 )
    {
        SAL_WARN( "sfx.bastyp", "Old format, not supported!");
        sFilterName = sFilterName.copy( nStartRealName+2 );
    }
 
    std::shared_ptr<const SfxFilter> pFilter = bUpdate ? SfxFilter::GetFilterByName( sFilterName ) : nullptr;
    if (!pFilter)
    {
        pFilter = std::make_shared<SfxFilter>( sFilterName             ,
                                 sExtension              ,
                                 nFlags                  ,
                                 nClipboardId            ,
                                 sType                   ,
                                 sMimeType               ,
                                 sUserData               ,
                                 sServiceName            ,
                                 bEnabled );
        rList.push_back( pFilter );
    }
    else
    {
        SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get());
        pFilt->maFilterName  = sFilterName;
        pFilt->aWildCard    = WildCard(sExtension, ';');
        pFilt->nFormatType  = nFlags;
        pFilt->lFormat      = nClipboardId;
        pFilt->aTypeName    = sType;
        pFilt->aMimeType    = sMimeType;
        pFilt->aUserData    = sUserData;
        pFilt->aServiceName = sServiceName;
        pFilt->mbEnabled    = bEnabled;
    }
 
    SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get());
 
    // Don't forget to set right UIName!
    // Otherwise internal name is used as fallback ...
    pFilt->SetUIName( sUIName );
    pFilt->SetDefaultTemplate( sDefaultTemplate );
    if( nFormatVersion )
    {
        pFilt->SetVersion( nFormatVersion );
    }
}
 
void SfxFilterContainer::ReadFilters_Impl( bool bUpdate )
{
    if ( !pFilterArr )
    {
        CreateFilterArr();
        assert(pFilterArr);
    }
 
    bFirstRead = false;
    SfxFilterList_Impl& rList = *pFilterArr;
 
    try
    {
        // get the FilterFactory service to access the registered filters ... and types!
        uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
        uno::Reference< container::XNameAccess >     xFilterCFG                                                ;
        uno::Reference< container::XNameAccess >     xTypeCFG                                                  ;
        if( xServiceManager.is() )
        {
            xFilterCFG.set( xServiceManager->createInstance(  u"com.sun.star.document.FilterFactory"_ustr ), uno::UNO_QUERY );
            xTypeCFG.set( xServiceManager->createInstance(  u"com.sun.star.document.TypeDetection"_ustr ), uno::UNO_QUERY );
        }
 
        if( xFilterCFG.is() && xTypeCFG.is() )
        {
            // select right query to get right set of filters for search module
            const uno::Sequence< OUString > lFilterNames = xFilterCFG->getElementNames();
            if ( lFilterNames.hasElements() )
            {
                // If list of filters already exist ...
                // ReadExternalFilters must work in update mode.
                // Best way seems to mark all filters NOT_INSTALLED
                // and change it back for all valid filters afterwards.
                if( !rList.empty() )
                {
                    bUpdate = true;
                    for (const std::shared_ptr<const SfxFilter>& pFilter : rList)
                    {
                        SfxFilter* pNonConstFilter = const_cast<SfxFilter*>(pFilter.get());
                        pNonConstFilter->nFormatType |= SFX_FILTER_NOTINSTALLED;
                    }
                }
 
                // get all properties of filters ... put it into the filter container
                for( const OUString& sFilterName : lFilterNames )
                {
                    // Try to get filter .. but look for any exceptions!
                    // May be filter was deleted by another thread ...
                    ReadSingleFilter_Impl( sFilterName, xTypeCFG, xFilterCFG, bUpdate );
                }
            }
        }
    }
    catch(const uno::Exception&)
    {
        SAL_WARN( "sfx.bastyp", "SfxFilterContainer::ReadFilter()\nException detected. Possible not all filters could be cached." );
    }
 
    if ( bUpdate )
    {
        // global filter array was modified, factory specific ones might need an
        // update too
        for (const auto& aImpl : aImplArr)
            aImpl->Update();
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'GuessFilter' is required to be utilized.

V530 The return value of function 'GuessFilter' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V547 Expression 'nFormatVersion' is always false.