/* -*- 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 <limits.h>
#include <mutex>
#include <string_view>
 
#include <com/sun/star/uno/Any.h>
#include <sal/log.hxx>
 
#include <unotools/pathoptions.hxx>
#include <tools/urlobj.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <ucbhelper/content.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/document/XTypeDetection.hpp>
#include <com/sun/star/document/DocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/DocumentTemplates.hpp>
#include <com/sun/star/frame/XDocumentTemplates.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/io/XPersist.hpp>
#include <com/sun/star/lang/XLocalizable.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/ucb/XContentAccess.hpp>
#include <com/sun/star/ucb/AnyCompareFactory.hpp>
#include <com/sun/star/ucb/NumberedSortingInfo.hpp>
 
#include "doctemplateslocal.hxx"
#include <sfxurlrelocator.hxx>
 
#include <sfx2/doctempl.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/sfxresid.hxx>
#include <sfx2/strings.hrc>
#include <strings.hxx>
#include <svtools/templatefoldercache.hxx>
 
#include <memory>
#include <utility>
#include <vector>
 
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::document;
using namespace ::rtl;
using namespace ::ucbhelper;
 
constexpr OUString TITLE = u"Title"_ustr;
constexpr OUString TARGET_URL = u"TargetURL"_ustr;
 
constexpr OUStringLiteral COMMAND_TRANSFER = u"transfer";
 
namespace {
 
class RegionData_Impl;
 
}
 
namespace DocTempl {
 
namespace {
 
class DocTempl_EntryData_Impl
{
    RegionData_Impl*    mpParent;
 
    // the following member must be SfxObjectShellLock since it controls that SfxObjectShell lifetime by design
    // and users of this class expect it to be so.
    SfxObjectShellLock  mxObjShell;
 
    OUString            maTitle;
    OUString            maOwnURL;
    OUString            maTargetURL;
 
public:
                        DocTempl_EntryData_Impl( RegionData_Impl* pParent,
                                        const OUString& rTitle );
 
    const OUString&     GetTitle() const { return maTitle; }
    const OUString&     GetTargetURL();
    const OUString&     GetHierarchyURL();
 
    void                SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
    void                SetTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
    void                SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
 
    int                 Compare( std::u16string_view rTitle ) const;
};
 
}
 
}
 
using namespace ::DocTempl;
 
namespace {
 
class RegionData_Impl
{
    const SfxDocTemplate_Impl*  mpParent;
    std::vector<std::unique_ptr<DocTempl_EntryData_Impl>> maEntries;
    OUString                    maTitle;
    OUString                    maOwnURL;
 
private:
    size_t                      GetEntryPos( std::u16string_view rTitle,
                                             bool& rFound ) const;
 
public:
                        RegionData_Impl( const SfxDocTemplate_Impl* pParent,
                                         OUString aTitle );
 
    void                SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
 
    DocTempl_EntryData_Impl*     GetEntry( size_t nIndex ) const;
    DocTempl_EntryData_Impl*     GetEntry( std::u16string_view rName ) const;
 
    const OUString&     GetTitle() const { return maTitle; }
    const OUString&     GetHierarchyURL();
 
    size_t              GetCount() const;
 
    void                SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
 
    void                AddEntry( const OUString& rTitle,
                                  const OUString& rTargetURL,
                                  const size_t *pPos );
    void                DeleteEntry( size_t nIndex );
 
    int                 Compare( RegionData_Impl const * pCompareWith ) const;
};
 
}
 
class SfxDocTemplate_Impl : public SvRefBase
{
    uno::Reference< XPersist >               mxInfo;
    uno::Reference< XDocumentTemplates >     mxTemplates;
 
    std::mutex          maMutex;
    OUString            maRootURL;
    OUString            maStandardGroup;
    std::vector<std::unique_ptr<RegionData_Impl>> maRegions;
    bool            mbConstructed;
 
    uno::Reference< XAnyCompareFactory > m_rCompareFactory;
 
    // the following member is intended to prevent clearing of the global data when it is in use
    // TODO/LATER: it still does not make the implementation complete thread-safe
    sal_Int32           mnLockCounter;
 
private:
    void                Clear();
 
public:
                        SfxDocTemplate_Impl();
                        virtual ~SfxDocTemplate_Impl() override;
 
    void                IncrementLock();
    void                DecrementLock();
 
    bool            Construct( );
    void                CreateFromHierarchy( std::unique_lock<std::mutex>& rGuard, Content &rTemplRoot );
    void                ReInitFromComponent();
    void                AddRegion( std::unique_lock<std::mutex>& rGuard,
                                   const OUString& rTitle,
                                   Content& rContent );
 
    void                Rescan();
 
    void                DeleteRegion( size_t nIndex );
 
    size_t              GetRegionCount() const
                            { return maRegions.size(); }
    RegionData_Impl*    GetRegion( std::u16string_view rName ) const;
    RegionData_Impl*    GetRegion( size_t nIndex ) const;
 
    bool            GetTitleFromURL( const OUString& rURL, OUString& aTitle );
    bool            InsertRegion( std::unique_ptr<RegionData_Impl> pData, size_t nPos );
    const OUString& GetRootURL() const { return maRootURL; }
 
    const uno::Reference< XDocumentTemplates >& getDocTemplates() const { return mxTemplates; }
};
 
namespace {
 
class DocTemplLocker_Impl
{
    SfxDocTemplate_Impl& m_aDocTempl;
public:
    explicit DocTemplLocker_Impl( SfxDocTemplate_Impl& aDocTempl )
    : m_aDocTempl( aDocTempl )
    {
        m_aDocTempl.IncrementLock();
    }
 
    ~DocTemplLocker_Impl()
    {
        m_aDocTempl.DecrementLock();
    }
};
 
}
 
static SfxDocTemplate_Impl *gpTemplateData = nullptr;
 
 
static bool getTextProperty_Impl( Content& rContent,
                                      const OUString& rPropName,
                                      OUString& rPropValue );
 
 
OUString SfxDocumentTemplates::GetFullRegionName
(
    sal_uInt16 nIdx                     // Region Index
)   const
 
/*  [Description]
 
    Returns the logical name of a region and its path
 
    [Return value]                 Reference to the Region name
 
*/
 
{
    // First: find the RegionData for the index
 
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( pImp->Construct() )
    {
        RegionData_Impl *pData1 = pImp->GetRegion( nIdx );
 
        if ( pData1 )
            return pData1->GetTitle();
 
        // --**-- here was some code which appended the path to the
        //      group if there was more than one with the same name.
        //      this should not happen anymore
    }
 
    return OUString();
}
 
 
OUString SfxDocumentTemplates::GetRegionName
(
    sal_uInt16 nIdx                 // Region Index
)   const
 
/*  [Description]
 
    Returns the logical name of a region
 
    [Return value]
 
    const String&                   Reference to the Region name
 
*/
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( pImp->Construct() )
    {
        RegionData_Impl *pData = pImp->GetRegion( nIdx );
 
        if ( pData )
            return pData->GetTitle();
    }
 
    return OUString();
}
 
 
sal_uInt16 SfxDocumentTemplates::GetRegionCount() const
 
/*  [Description]
 
    Returns the number of Regions
 
    [Return value]
 
    sal_uInt16                  Number of Regions
*/
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( !pImp->Construct() )
        return 0;
 
    return pImp->GetRegionCount();
}
 
 
sal_uInt16 SfxDocumentTemplates::GetCount
(
    sal_uInt16 nRegion              /* Region index whose number is
                                   to be determined */
 
)   const
 
/*  [Description]
 
    Number of entries in Region
 
    [Return value]                 Number of entries
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( !pImp->Construct() )
        return 0;
 
    RegionData_Impl *pData = pImp->GetRegion( nRegion );
 
    if ( !pData )
        return 0;
 
    return pData->GetCount();
}
 
 
OUString SfxDocumentTemplates::GetName
(
    sal_uInt16 nRegion,     //  Region Index, in which the entry lies
    sal_uInt16 nIdx         //  Index of the entry
)   const
 
/*  [Description]
 
    Returns the logical name of an entry in Region
 
    [Return value]
 
    const String&           Entry Name
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( pImp->Construct() )
    {
        RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
 
        if ( pRegion )
        {
            DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
            if ( pEntry )
                return pEntry->GetTitle();
        }
    }
 
    return OUString();
}
 
 
OUString SfxDocumentTemplates::GetPath
(
    sal_uInt16  nRegion,    //  Region Index, in which the entry lies
    sal_uInt16  nIdx        //  Index of the entry
)   const
 
/*  [Description]
 
    Returns the file name with full path to the file assigned to an entry
 
    [Return value]
 
    String                  File name with full path
*/
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( !pImp->Construct() )
        return OUString();
 
    RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
 
    if ( pRegion )
    {
        DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
        if ( pEntry )
            return pEntry->GetTargetURL();
    }
 
    return OUString();
}
 
 
OUString SfxDocumentTemplates::GetTemplateTargetURLFromComponent( std::u16string_view aGroupName,
                                                                    std::u16string_view aTitle )
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    INetURLObject aTemplateObj( pImp->GetRootURL() );
 
    aTemplateObj.insertName( aGroupName, false,
                        INetURLObject::LAST_SEGMENT,
                        INetURLObject::EncodeMechanism::All );
 
    aTemplateObj.insertName( aTitle, false,
                        INetURLObject::LAST_SEGMENT,
                        INetURLObject::EncodeMechanism::All );
 
 
    Content aTemplate;
    uno::Reference< XCommandEnvironment > aCmdEnv;
    if ( Content::create( aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
    {
        OUString aResult;
        getTextProperty_Impl( aTemplate, TARGET_URL, aResult );
        return SvtPathOptions().SubstituteVariable( aResult );
    }
 
    return OUString();
}
 
 
/** Convert a template name to its localised pair if it exists.
    @param rString
        Name to be translated.
    @return
        The localised pair of rString or rString if the former does not exist.
*/
OUString SfxDocumentTemplates::ConvertResourceString(const OUString& rString)
{
    static constexpr OUString aTemplateNames[] =
    {
        STR_TEMPLATE_NAME1_DEF,
        STR_TEMPLATE_NAME2_DEF,
        STR_TEMPLATE_NAME3_DEF,
        STR_TEMPLATE_NAME4_DEF,
        STR_TEMPLATE_NAME5_DEF,
        STR_TEMPLATE_NAME6_DEF,
        STR_TEMPLATE_NAME7_DEF,
        STR_TEMPLATE_NAME8_DEF,
        STR_TEMPLATE_NAME9_DEF,
        STR_TEMPLATE_NAME10_DEF,
        STR_TEMPLATE_NAME11_DEF,
        STR_TEMPLATE_NAME12_DEF,
        STR_TEMPLATE_NAME13_DEF,
        STR_TEMPLATE_NAME14_DEF,
        STR_TEMPLATE_NAME15_DEF,
        STR_TEMPLATE_NAME16_DEF,
        STR_TEMPLATE_NAME17_DEF,
        STR_TEMPLATE_NAME18_DEF,
        STR_TEMPLATE_NAME19_DEF,
        STR_TEMPLATE_NAME20_DEF,
        STR_TEMPLATE_NAME21_DEF,
        STR_TEMPLATE_NAME22_DEF,
        STR_TEMPLATE_NAME23_DEF,
        STR_TEMPLATE_NAME24_DEF,
        STR_TEMPLATE_NAME25_DEF,
        STR_TEMPLATE_NAME26_DEF,
        STR_TEMPLATE_NAME27_DEF,
        STR_TEMPLATE_NAME28_DEF,
        STR_TEMPLATE_NAME29_DEF,
        STR_TEMPLATE_NAME30_DEF,
        STR_TEMPLATE_NAME31_DEF,
        STR_TEMPLATE_NAME32_DEF,
        STR_TEMPLATE_NAME33_DEF,
        STR_TEMPLATE_NAME34_DEF
    };
 
    TranslateId STR_TEMPLATE_NAME[] =
    {
        STR_TEMPLATE_NAME1,
        STR_TEMPLATE_NAME2,
        STR_TEMPLATE_NAME3,
        STR_TEMPLATE_NAME4,
        STR_TEMPLATE_NAME5,
        STR_TEMPLATE_NAME6,
        STR_TEMPLATE_NAME7,
        STR_TEMPLATE_NAME8,
        STR_TEMPLATE_NAME9,
        STR_TEMPLATE_NAME10,
        STR_TEMPLATE_NAME11,
        STR_TEMPLATE_NAME12,
        STR_TEMPLATE_NAME13,
        STR_TEMPLATE_NAME14,
        STR_TEMPLATE_NAME15,
        STR_TEMPLATE_NAME16,
        STR_TEMPLATE_NAME17,
        STR_TEMPLATE_NAME18,
        STR_TEMPLATE_NAME19,
        STR_TEMPLATE_NAME20,
        STR_TEMPLATE_NAME21,
        STR_TEMPLATE_NAME22,
        STR_TEMPLATE_NAME23,
        STR_TEMPLATE_NAME24,
        STR_TEMPLATE_NAME25,
        STR_TEMPLATE_NAME26,
        STR_TEMPLATE_NAME27,
        STR_TEMPLATE_NAME28,
        STR_TEMPLATE_NAME29,
        STR_TEMPLATE_NAME30,
        STR_TEMPLATE_NAME31,
        STR_TEMPLATE_NAME32,
        STR_TEMPLATE_NAME33,
        STR_TEMPLATE_NAME34
    };
 
    static_assert(SAL_N_ELEMENTS(aTemplateNames) == SAL_N_ELEMENTS(STR_TEMPLATE_NAME));
 
    for (size_t i = 0; i < SAL_N_ELEMENTS(STR_TEMPLATE_NAME); ++i)
    {
        if (rString == aTemplateNames[i])
            return SfxResId(STR_TEMPLATE_NAME[i]);
    }
    return rString;
}
 
 
bool SfxDocumentTemplates::CopyOrMove
(
    sal_uInt16  nTargetRegion,      //  Target Region Index
    sal_uInt16  nTargetIdx,         //  Target position Index
    sal_uInt16  nSourceRegion,      //  Source Region Index
    sal_uInt16  nSourceIdx,         /*  Index to be copied / to moved template */
    bool        bMove               //  Copy / Move
)
 
/*  [Description]
 
    Copy or move a document template
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::Move(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
    <SfxDocumentTemplates::Copy(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
*/
 
{
    /* to perform a copy or move, we need to send a transfer command to
       the destination folder with the URL of the source as parameter.
       ( If the destination content doesn't support the transfer command,
       we could try a copy ( and delete ) instead. )
       We need two transfers ( one for the real template and one for its
       representation in the hierarchy )
       ...
    */
 
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( !pImp->Construct() )
        return false;
 
    // Don't copy or move any folders
    if( nSourceIdx == USHRT_MAX )
        return false ;
 
    if ( nSourceRegion == nTargetRegion )
    {
        SAL_WARN( "sfx.doc", "Don't know, what to do!" );
        return false;
    }
 
    RegionData_Impl *pSourceRgn = pImp->GetRegion( nSourceRegion );
    if ( !pSourceRgn )
        return false;
 
    DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nSourceIdx );
    if ( !pSource )
        return false;
 
    RegionData_Impl *pTargetRgn = pImp->GetRegion( nTargetRegion );
    if ( !pTargetRgn )
        return false;
 
    const OUString aTitle = pSource->GetTitle();
 
    uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
 
    if ( xTemplates->addTemplate( pTargetRgn->GetTitle(),
                                  aTitle,
                                  pSource->GetTargetURL() ) )
    {
        const OUString aNewTargetURL = GetTemplateTargetURLFromComponent( pTargetRgn->GetTitle(), aTitle );
        if ( aNewTargetURL.isEmpty() )
            return false;
 
        if ( bMove )
        {
            // --**-- delete the original file
            bool bDeleted = xTemplates->removeTemplate( pSourceRgn->GetTitle(),
                                                            pSource->GetTitle() );
            if ( bDeleted )
                pSourceRgn->DeleteEntry( nSourceIdx );
            else
            {
                if ( xTemplates->removeTemplate( pTargetRgn->GetTitle(), aTitle ) )
                    return false; // will trigger retry with copy instead of move
 
                // if it is not possible to remove just created template ( must be possible! )
                // it is better to report success here, since at least the copy has succeeded
                // TODO/LATER: solve it more gracefully in future
            }
        }
 
        // todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
        size_t temp_nTargetIdx = nTargetIdx;
        pTargetRgn->AddEntry( aTitle, aNewTargetURL, &temp_nTargetIdx );
 
        return true;
    }
 
    // --**-- if the current file is opened,
    // it must be re-opened afterwards.
 
    return false;
}
 
 
bool SfxDocumentTemplates::Move
(
    sal_uInt16 nTargetRegion,       //  Target Region Index
    sal_uInt16 nTargetIdx,          //  Target position Index
    sal_uInt16 nSourceRegion,       //  Source Region Index
    sal_uInt16 nSourceIdx           /*  Index to be copied / to moved template */
)
 
/*  [Description]
 
    Moving a template
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
*/
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    return CopyOrMove( nTargetRegion, nTargetIdx,
                       nSourceRegion, nSourceIdx, true );
}
 
 
bool SfxDocumentTemplates::Copy
(
    sal_uInt16 nTargetRegion,       //  Target Region Index
    sal_uInt16 nTargetIdx,          //  Target position Index
    sal_uInt16 nSourceRegion,       //  Source Region Index
    sal_uInt16 nSourceIdx           /*  Index to be copied / to moved template */
)
 
/*  [Description]
 
    Copying a template
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    return CopyOrMove( nTargetRegion, nTargetIdx,
                       nSourceRegion, nSourceIdx, false );
}
 
 
bool SfxDocumentTemplates::CopyTo
(
    sal_uInt16          nRegion,    //  Region of the template to be exported
    sal_uInt16          nIdx,       //  Index of the template to be exported
    std::u16string_view rName       /*  File name under which the template is to
                                    be created */
)   const
 
/*  [Description]
 
    Exporting a template into the file system
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::CopyFrom(sal_uInt16,sal_uInt16,String&)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pSourceRgn = pImp->GetRegion( nRegion );
    if ( !pSourceRgn )
        return false;
 
    DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nIdx );
    if ( !pSource )
        return false;
 
    INetURLObject aTargetURL( rName );
 
    const OUString aTitle( aTargetURL.getName( INetURLObject::LAST_SEGMENT, true,
                                         INetURLObject::DecodeMechanism::WithCharset ) );
    aTargetURL.removeSegment();
 
    const OUString aParentURL = aTargetURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
 
    uno::Reference< XCommandEnvironment > aCmdEnv;
    Content aTarget;
 
    try
    {
        aTarget = Content( aParentURL, aCmdEnv, comphelper::getProcessComponentContext() );
 
        TransferInfo aTransferInfo;
        aTransferInfo.MoveData = false;
        aTransferInfo.SourceURL = pSource->GetTargetURL();
        aTransferInfo.NewTitle = aTitle;
        aTransferInfo.NameClash = NameClash::RENAME;
 
        Any aArg( aTransferInfo );
        aTarget.executeCommand( COMMAND_TRANSFER, aArg );
    }
    catch ( ContentCreationException& )
    { return false; }
    catch ( Exception& )
    { return false; }
 
    return true;
}
 
 
bool SfxDocumentTemplates::CopyFrom
(
    sal_uInt16      nRegion,        /*  Region in which the template is to be
                                    imported */
    sal_uInt16      nIdx,           //  Index of the new template in this Region
    OUString&       rName           /*  File name of the template to be imported
                                    as an out parameter of the (automatically
                                    generated from the file name) logical name
                                    of the template */
)
 
/*  [Description]
 
    Import a template from the file system
 
    [Return value]                 Success (sal_True) or serfpTargetDirectory->GetContent());
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::CopyTo(sal_uInt16,sal_uInt16,const String&)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pTargetRgn = pImp->GetRegion( nRegion );
 
    if ( !pTargetRgn )
        return false;
 
    uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
    if ( !xTemplates.is() )
        return false;
 
    OUString aTitle;
    bool bTemplateAdded = false;
 
    if( pImp->GetTitleFromURL( rName, aTitle ) )
    {
        bTemplateAdded = xTemplates->addTemplate( pTargetRgn->GetTitle(), aTitle, rName );
    }
    else
    {
        uno::Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
 
        Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(u"Hidden"_ustr, true) };
 
        INetURLObject   aTemplURL( rName );
        uno::Reference< XDocumentPropertiesSupplier > xDocPropsSupplier;
        uno::Reference< XStorable > xStorable;
        try
        {
            xStorable.set(
                xDesktop->loadComponentFromURL( aTemplURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
                                                u"_blank"_ustr,
                                                0,
                                                aArgs ),
                UNO_QUERY );
 
            xDocPropsSupplier.set( xStorable, UNO_QUERY );
        }
        catch( Exception& )
        {
        }
 
        if( xStorable.is() )
        {
            // get Title from XDocumentPropertiesSupplier
            if( xDocPropsSupplier.is() )
            {
                uno::Reference< XDocumentProperties > xDocProps
                    = xDocPropsSupplier->getDocumentProperties();
                if (xDocProps.is() ) {
                    aTitle = xDocProps->getTitle();
                }
            }
 
            if( aTitle.isEmpty() )
            {
                INetURLObject aURL(std::move(aTemplURL));
                aURL.CutExtension();
                aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
                                        INetURLObject::DecodeMechanism::WithCharset );
            }
 
            // write a template using XStorable interface
            bTemplateAdded = xTemplates->storeTemplate( pTargetRgn->GetTitle(), aTitle, xStorable );
        }
    }
 
 
    if( bTemplateAdded )
    {
        INetURLObject aTemplObj( pTargetRgn->GetHierarchyURL() );
        aTemplObj.insertName( aTitle, false,
                              INetURLObject::LAST_SEGMENT,
                              INetURLObject::EncodeMechanism::All );
        const OUString aTemplURL = aTemplObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
 
        uno::Reference< XCommandEnvironment > aCmdEnv;
        Content aTemplCont;
 
        if( Content::create( aTemplURL, aCmdEnv, comphelper::getProcessComponentContext(), aTemplCont ) )
        {
            OUString aTemplName;
            if( getTextProperty_Impl( aTemplCont, TARGET_URL, aTemplName ) )
            {
                if ( nIdx == USHRT_MAX )
                    nIdx = 0;
                else
                    ++nIdx;
 
                // todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
                size_t temp_nIdx = nIdx;
                pTargetRgn->AddEntry( aTitle, aTemplName, &temp_nIdx );
                rName = aTitle;
                return true;
            }
            else
            {
                SAL_WARN( "sfx.doc", "CopyFrom(): The content should contain target URL!" );
            }
        }
        else
        {
            SAL_WARN( "sfx.doc", "CopyFrom(): The content just was created!" );
        }
    }
 
    return false;
}
 
 
bool SfxDocumentTemplates::Delete
(
    sal_uInt16 nRegion,             //  Region Index
    sal_uInt16 nIdx                 /*  Index of the entry or USHRT_MAX,
                                    if a directory is meant. */
)
 
/*  [Description]
 
    Deleting an entry or a directory
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::InsertDir(const String&,sal_uInt16)>
    <SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    /* delete the template or folder in the hierarchy and in the
       template folder by sending a delete command to the content.
       Then remove the data from the lists
    */
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
 
    if ( !pRegion )
        return false;
 
    bool bRet;
    uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
 
    if ( nIdx == USHRT_MAX )
    {
        bRet = xTemplates->removeGroup( pRegion->GetTitle() );
        if ( bRet )
            pImp->DeleteRegion( nRegion );
    }
    else
    {
        DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
 
        if ( !pEntry )
            return false;
 
        bRet = xTemplates->removeTemplate( pRegion->GetTitle(),
                                           pEntry->GetTitle() );
        if( bRet )
            pRegion->DeleteEntry( nIdx );
    }
 
    return bRet;
}
 
 
bool SfxDocumentTemplates::InsertDir
(
    const OUString&     rText,      //  the logical name of the new Region
    sal_uInt16          nRegion     //  Region Index
)
 
/*  [Description]
 
    Insert an index
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
*/
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pRegion = pImp->GetRegion( rText );
 
    if ( pRegion )
        return false;
 
    uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
 
    if ( xTemplates->addGroup( rText ) )
    {
        return pImp->InsertRegion( std::make_unique<RegionData_Impl>( pImp.get(), rText ), nRegion );
    }
 
    return false;
}
 
bool SfxDocumentTemplates::InsertTemplate(sal_uInt16 nSourceRegion, sal_uInt16 nIdx, const OUString &rName, const OUString &rPath)
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pRegion = pImp->GetRegion( nSourceRegion );
 
    if ( !pRegion )
        return false;
 
    size_t pos = nIdx;
    pRegion->AddEntry( rName, rPath, &pos );
 
    return true;
}
 
bool SfxDocumentTemplates::SetName( const OUString& rName, sal_uInt16 nRegion, sal_uInt16 nIdx )
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
 
    if ( !pRegion )
        return false;
 
    uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
 
    if ( nIdx == USHRT_MAX )
    {
        if ( pRegion->GetTitle() == rName )
            return true;
 
        // we have to rename a region
        if ( xTemplates->renameGroup( pRegion->GetTitle(), rName ) )
        {
            pRegion->SetTitle( rName );
            pRegion->SetHierarchyURL( u""_ustr );
            return true;
        }
    }
    else
    {
        DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
 
        if ( !pEntry )
            return false;
 
        if ( pEntry->GetTitle() == rName )
            return true;
 
        if ( xTemplates->renameTemplate( pRegion->GetTitle(),
                                         pEntry->GetTitle(),
                                         rName ) )
        {
            pEntry->SetTitle( rName );
            pEntry->SetTargetURL( u""_ustr );
            pEntry->SetHierarchyURL( u""_ustr );
            return true;
        }
    }
 
    return false;
}
 
 
bool SfxDocumentTemplates::GetFull
(
    std::u16string_view rRegion,      // Region Name
    std::u16string_view rName,    // Template Name
    OUString &rPath               // Out: Path + File name
)
 
/*  [Description]
 
    Returns Path + File name of the template specified by rRegion and rName.
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::GetLogicNames(const String&,String&,String&)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    // We don't search for empty names!
    if ( rName.empty() )
        return false;
 
    if ( ! pImp->Construct() )
        return false;
 
    DocTempl_EntryData_Impl* pEntry = nullptr;
    const sal_uInt16 nCount = GetRegionCount();
 
    for ( sal_uInt16 i = 0; i < nCount; ++i )
    {
        RegionData_Impl *pRegion = pImp->GetRegion( i );
 
        if( pRegion &&
            ( rRegion.empty() || ( rRegion == pRegion->GetTitle() ) ) )
        {
            pEntry = pRegion->GetEntry( rName );
 
            if ( pEntry )
            {
                rPath = pEntry->GetTargetURL();
                break;
            }
        }
    }
 
    return ( pEntry != nullptr );
}
 
 
bool SfxDocumentTemplates::GetLogicNames
(
    std::u16string_view rPath,        // Full Path to the template
    OUString &rRegion,                // Out: Region name
    OUString &rName                   // Out: Template name
) const
 
/*  [Description]
 
    Returns and logical path name to the template specified by rPath
 
    [Return value]
 
    sal_Bool            sal_True,   Action could be performed
                        sal_False,  Action could not be performed
 
    [Cross-references]
 
    <SfxDocumentTemplates::GetFull(const String&,const String&,DirEntry&)>
*/
 
{
    DocTemplLocker_Impl aLocker( *pImp );
 
    if ( ! pImp->Construct() )
        return false;
 
    INetURLObject aFullPath;
 
    aFullPath.SetSmartProtocol( INetProtocol::File );
    aFullPath.SetURL( rPath );
    const OUString aPath( aFullPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
 
    const sal_uInt16 nCount = GetRegionCount();
 
    for ( sal_uInt16 i=0; i<nCount; ++i )
    {
        RegionData_Impl *pData = pImp->GetRegion( i );
        if ( pData )
        {
            const sal_uInt16 nChildCount = pData->GetCount();
 
            for ( sal_uInt16 j=0; j<nChildCount; ++j )
            {
                DocTempl_EntryData_Impl *pEntry = pData->GetEntry( j );
                if ( pEntry && pEntry->GetTargetURL() == aPath )
                {
                    rRegion = pData->GetTitle();
                    rName = pEntry->GetTitle();
                    return true;
                }
            }
        }
    }
 
    return false;
}
 
 
SfxDocumentTemplates::SfxDocumentTemplates()
 
/*  [Description]
 
    Constructor
*/
{
    if ( !gpTemplateData )
        gpTemplateData = new SfxDocTemplate_Impl;
 
    pImp = gpTemplateData;
}
 
 
SfxDocumentTemplates::~SfxDocumentTemplates()
 
/*  [Description]
 
    Destructor
    Release of administrative data
*/
 
{
    pImp = nullptr;
}
 
void SfxDocumentTemplates::Update( )
{
    if ( ::svt::TemplateFolderCache( true ).needsUpdate() )   // update is really necessary
    {
        if ( pImp->Construct() )
            pImp->Rescan();
    }
}
 
void SfxDocumentTemplates::ReInitFromComponent()
{
    pImp->ReInitFromComponent();
}
 
DocTempl_EntryData_Impl::DocTempl_EntryData_Impl( RegionData_Impl* pParent,
                                const OUString& rTitle )
{
    mpParent    = pParent;
    maTitle     = SfxDocumentTemplates::ConvertResourceString(rTitle);
}
 
 
int DocTempl_EntryData_Impl::Compare( std::u16string_view rTitle ) const
{
    return maTitle.compareTo( rTitle );
}
 
 
const OUString& DocTempl_EntryData_Impl::GetHierarchyURL()
{
    if ( maOwnURL.isEmpty() )
    {
        INetURLObject aTemplateObj( mpParent->GetHierarchyURL() );
 
        aTemplateObj.insertName( GetTitle(), false,
                     INetURLObject::LAST_SEGMENT,
                     INetURLObject::EncodeMechanism::All );
 
        maOwnURL = aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
        DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
    }
 
    return maOwnURL;
}
 
 
const OUString& DocTempl_EntryData_Impl::GetTargetURL()
{
    if ( maTargetURL.isEmpty() )
    {
        uno::Reference< XCommandEnvironment > aCmdEnv;
        Content aRegion;
 
        if ( Content::create( GetHierarchyURL(), aCmdEnv, comphelper::getProcessComponentContext(), aRegion ) )
        {
            getTextProperty_Impl( aRegion, TARGET_URL, maTargetURL );
        }
        else
        {
            SAL_WARN( "sfx.doc", "GetTargetURL(): Could not create hierarchy content!" );
        }
    }
 
    return maTargetURL;
}
 
 
RegionData_Impl::RegionData_Impl( const SfxDocTemplate_Impl* pParent,
                                  OUString aTitle )
                                  : mpParent(pParent), maTitle(std::move(aTitle))
{
}
 
 
size_t RegionData_Impl::GetEntryPos( std::u16string_view rTitle, bool& rFound ) const
{
    const size_t nCount = maEntries.size();
 
    for ( size_t i=0; i<nCount; ++i )
    {
        auto &pData = maEntries[ i ];
 
        if ( pData->Compare( rTitle ) == 0 )
        {
            rFound = true;
            return i;
        }
    }
 
    rFound = false;
    return nCount;
}
 
 
void RegionData_Impl::AddEntry( const OUString& rTitle,
                                const OUString& rTargetURL,
                                const size_t *pPos )
{
    INetURLObject aLinkObj( GetHierarchyURL() );
    aLinkObj.insertName( rTitle, false,
                      INetURLObject::LAST_SEGMENT,
                      INetURLObject::EncodeMechanism::All );
    const OUString aLinkURL = aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
 
    bool        bFound = false;
    size_t          nPos = GetEntryPos( rTitle, bFound );
 
    if ( bFound )
        return;
 
    if ( pPos )
        nPos = *pPos;
 
    auto pEntry = std::make_unique<DocTempl_EntryData_Impl>(
        this, rTitle );
    pEntry->SetTargetURL( rTargetURL );
    pEntry->SetHierarchyURL( aLinkURL );
    if ( nPos < maEntries.size() ) {
        auto it = maEntries.begin();
        std::advance( it, nPos );
        maEntries.insert( it, std::move(pEntry) );
    }
    else
        maEntries.push_back( std::move(pEntry) );
}
 
 
size_t RegionData_Impl::GetCount() const
{
    return maEntries.size();
}
 
 
const OUString& RegionData_Impl::GetHierarchyURL()
{
    if ( maOwnURL.isEmpty() )
    {
        INetURLObject aRegionObj( mpParent->GetRootURL() );
 
        aRegionObj.insertName( GetTitle(), false,
                     INetURLObject::LAST_SEGMENT,
                     INetURLObject::EncodeMechanism::All );
 
        maOwnURL = aRegionObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
        DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
    }
 
    return maOwnURL;
}
 
 
DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( std::u16string_view rName ) const
{
    bool    bFound = false;
    tools::Long        nPos = GetEntryPos( rName, bFound );
 
    if ( bFound )
        return maEntries[ nPos ].get();
    return nullptr;
}
 
 
DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( size_t nIndex ) const
{
    if ( nIndex < maEntries.size() )
        return maEntries[ nIndex ].get();
    return nullptr;
}
 
void RegionData_Impl::DeleteEntry( size_t nIndex )
{
    if ( nIndex < maEntries.size() )
    {
        auto it = maEntries.begin();
        std::advance( it, nIndex );
        maEntries.erase( it );
    }
}
 
int RegionData_Impl::Compare( RegionData_Impl const * pCompare ) const
{
    return maTitle.compareTo( pCompare->maTitle );
}
 
SfxDocTemplate_Impl::SfxDocTemplate_Impl()
    : maStandardGroup(DocTemplLocaleHelper::GetStandardGroupString())
    , mbConstructed(false)
    , mnLockCounter(0)
{
}
 
SfxDocTemplate_Impl::~SfxDocTemplate_Impl()
{
    gpTemplateData = nullptr;
}
 
void SfxDocTemplate_Impl::IncrementLock()
{
    std::unique_lock aGuard( maMutex );
    mnLockCounter++;
}
 
void SfxDocTemplate_Impl::DecrementLock()
{
    std::unique_lock aGuard( maMutex );
    if ( mnLockCounter )
        mnLockCounter--;
}
 
RegionData_Impl* SfxDocTemplate_Impl::GetRegion( size_t nIndex ) const
{
    if ( nIndex < maRegions.size() )
        return maRegions[ nIndex ].get();
    return nullptr;
}
 
RegionData_Impl* SfxDocTemplate_Impl::GetRegion( std::u16string_view rName )
    const
{
    for (auto& pData : maRegions)
    {
        if( pData->GetTitle() == rName )
            return pData.get();
    }
    return nullptr;
}
 
 
void SfxDocTemplate_Impl::DeleteRegion( size_t nIndex )
{
    if ( nIndex < maRegions.size() )
    {
        auto it = maRegions.begin();
        std::advance( it, nIndex );
        maRegions.erase( it );
    }
}
 
 
/*  AddRegion adds a Region to the RegionList
*/
void SfxDocTemplate_Impl::AddRegion( std::unique_lock<std::mutex>& /*rGuard*/,
                                     const OUString& rTitle,
                                     Content& rContent )
{
    auto pRegion = std::make_unique<RegionData_Impl>( this, rTitle );
    auto pRegionTmp = pRegion.get();
 
    if ( ! InsertRegion( std::move(pRegion), size_t(-1) ) )
    {
        return;
    }
 
    // now get the content of the region
    uno::Reference< XResultSet > xResultSet;
 
    try
    {
        xResultSet = rContent.createSortedCursor( { TITLE, TARGET_URL }, { { 1, true } }, m_rCompareFactory, INCLUDE_DOCUMENTS_ONLY );
    }
    catch ( Exception& ) {}
 
    if ( !xResultSet.is() )
        return;
 
    uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
 
    try
    {
        while ( xResultSet->next() )
        {
            pRegionTmp->AddEntry( xRow->getString( 1 ), xRow->getString( 2 ), nullptr );
        }
    }
    catch ( Exception& ) {}
}
 
 
void SfxDocTemplate_Impl::CreateFromHierarchy( std::unique_lock<std::mutex>& rGuard, Content &rTemplRoot )
{
    uno::Reference< XResultSet > xResultSet;
    Sequence< OUString > aProps { TITLE };
 
    try
    {
        xResultSet = rTemplRoot.createSortedCursor(
                         aProps,
                         { // Sequence
                              { // NumberedSortingInfo
                                  /* ColumnIndex */ 1, /* Ascending */ true
                              }
                         },
                         m_rCompareFactory,
                         INCLUDE_FOLDERS_ONLY
                     );
    }
    catch ( Exception& ) {}
 
    if ( !xResultSet.is() )
        return;
 
    uno::Reference< XCommandEnvironment > aCmdEnv;
    uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
    uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
 
    try
    {
        while ( xResultSet->next() )
        {
            const OUString aId = xContentAccess->queryContentIdentifierString();
            Content  aContent( aId, aCmdEnv, comphelper::getProcessComponentContext() );
 
            AddRegion( rGuard, xRow->getString( 1 ), aContent );
        }
    }
    catch ( Exception& ) {}
}
 
 
bool SfxDocTemplate_Impl::Construct( )
{
    std::unique_lock aGuard( maMutex );
 
    if ( mbConstructed )
        return true;
 
    const uno::Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
 
    mxInfo.set(document::DocumentProperties::create(xContext), UNO_QUERY);
 
    mxTemplates = frame::DocumentTemplates::create(xContext);
 
    uno::Reference< XLocalizable > xLocalizable( mxTemplates, UNO_QUERY );
 
    m_rCompareFactory = AnyCompareFactory::createWithLocale(xContext, xLocalizable->getLocale());
 
    uno::Reference < XContent > aRootContent = mxTemplates->getContent();
    uno::Reference < XCommandEnvironment > aCmdEnv;
 
    if ( ! aRootContent.is() )
        return false;
 
    mbConstructed = true;
    maRootURL = aRootContent->getIdentifier()->getContentIdentifier();
 
    Content aTemplRoot( aRootContent, aCmdEnv, xContext );
    CreateFromHierarchy( aGuard, aTemplRoot );
 
    return true;
}
 
 
void SfxDocTemplate_Impl::ReInitFromComponent()
{
    uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
    if ( xTemplates.is() )
    {
        uno::Reference < XContent > aRootContent = xTemplates->getContent();
        uno::Reference < XCommandEnvironment > aCmdEnv;
        Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
        Clear();
        std::unique_lock aGuard(maMutex);
        CreateFromHierarchy( aGuard, aTemplRoot );
    }
}
 
 
bool SfxDocTemplate_Impl::InsertRegion( std::unique_ptr<RegionData_Impl> pNew, size_t nPos )
{
    // return false (not inserted) if the entry already exists
    for (auto const& pRegion : maRegions)
        if ( pRegion->Compare( pNew.get() ) == 0 )
            return false;
 
    size_t newPos = nPos;
    if ( pNew->GetTitle() == maStandardGroup )
        newPos = 0;
 
    if ( newPos < maRegions.size() )
    {
        auto it = maRegions.begin();
        std::advance( it, newPos );
        maRegions.emplace( it, std::move(pNew) );
    }
    else
        maRegions.emplace_back( std::move(pNew) );
 
    return true;
}
 
 
void SfxDocTemplate_Impl::Rescan()
{
    Clear();
 
    try
    {
        uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
        DBG_ASSERT( xTemplates.is(), "SfxDocTemplate_Impl::Rescan:invalid template instance!" );
        if ( xTemplates.is() )
        {
            xTemplates->update();
 
            uno::Reference < XContent > aRootContent = xTemplates->getContent();
            uno::Reference < XCommandEnvironment > aCmdEnv;
 
            Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
            std::unique_lock aGuard(maMutex);
            CreateFromHierarchy( aGuard, aTemplRoot );
        }
    }
    catch( const Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sfx.doc", "SfxDocTemplate_Impl::Rescan: caught an exception while doing the update" );
    }
}
 
 
bool SfxDocTemplate_Impl::GetTitleFromURL( const OUString& rURL,
                                           OUString& aTitle )
{
    if ( mxInfo.is() )
    {
        try
        {
            mxInfo->read( rURL );
        }
        catch ( Exception& )
        {
            // the document is not a StarOffice document
            return false;
        }
 
 
        try
        {
            uno::Reference< XPropertySet > aPropSet( mxInfo, UNO_QUERY );
            if ( aPropSet.is() )
            {
                Any aValue = aPropSet->getPropertyValue( TITLE );
                aValue >>= aTitle;
            }
        }
        catch ( IOException& ) {}
        catch ( UnknownPropertyException& ) {}
        catch ( Exception& ) {}
    }
 
    if ( aTitle.isEmpty() )
    {
        INetURLObject aURL( rURL );
        aURL.CutExtension();
        aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
                               INetURLObject::DecodeMechanism::WithCharset );
    }
 
    return true;
}
 
 
void SfxDocTemplate_Impl::Clear()
{
    std::unique_lock aGuard( maMutex );
    if ( mnLockCounter )
        return;
    maRegions.clear();
}
 
 
bool getTextProperty_Impl( Content& rContent,
                               const OUString& rPropName,
                               OUString& rPropValue )
{
    bool bGotProperty = false;
 
    // Get the property
    try
    {
        uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
 
        // check, whether or not the property exists
        if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
        {
            return false;
        }
 
        // now get the property
        Any aAnyValue = rContent.getPropertyValue( rPropName );
        aAnyValue >>= rPropValue;
 
        if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
        {
            SfxURLRelocator_Impl aRelocImpl( ::comphelper::getProcessComponentContext() );
            aRelocImpl.makeAbsoluteURL( rPropValue );
        }
 
        bGotProperty = true;
    }
    catch ( RuntimeException& ) {}
    catch ( Exception& ) {}
 
    return bGotProperty;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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