    /* -*- 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 .
     */

    /**************************************************************************
                                    TODO
     **************************************************************************

     - HierarchyEntry::move
       --> Rewrite to use XNamed ( once this is supported by config db api ).

     *************************************************************************/
    #include "hierarchydata.hxx"

    #include <vector>
    #include <osl/diagnose.h>
    #include <rtl/ustrbuf.hxx>
    #include <com/sun/star/beans/PropertyValue.hpp>
    #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
    #include <com/sun/star/container/XNameContainer.hpp>
    #include <com/sun/star/container/XNameReplace.hpp>
    #include <com/sun/star/util/XChangesBatch.hpp>
    #include <com/sun/star/util/XOfficeInstallationDirectories.hpp>
    #include <com/sun/star/lang/XSingleServiceFactory.hpp>
    #include <comphelper/propertysequence.hxx>
    #include "hierarchyprovider.hxx"
    #include "hierarchyuri.hxx"

    using namespace com::sun::star;

    namespace hierarchy_ucp
    {

    struct HierarchyEntry::iterator_Impl
    {
        HierarchyEntryData                                     entry;
        uno::Reference< container::XHierarchicalNameAccess >   dir;
        uno::Reference< util::XOfficeInstallationDirectories > officeDirs;
        uno::Sequence< OUString>                          names;
        sal_Int32                                              pos;
        iterator_Impl()
        : pos( -1 /* before first */ ) {};
    };

    void makeXMLName( const OUString & rIn, OUStringBuffer & rBuffer  )
    {
        sal_Int32 nCount = rIn.getLength();
        for ( sal_Int32 n = 0; n < nCount; ++n )
        {
            const sal_Unicode c = rIn[ n ];
            switch ( c )
            {
                case '&':
                    rBuffer.append( "&amp;" );
                    break;

                case '"':
                    rBuffer.append( "&quot;" );
                    break;

                case '\'':
                    rBuffer.append( "&apos;" );
                    break;

                case '<':
                    rBuffer.append( "&lt;" );
                    break;

                case '>':
                    rBuffer.append( "&gt;" );
                    break;

                default:
                    rBuffer.append( c );
                    break;
            }
        }
    }

    // HierarchyEntry Implementation.

    #define READ_SERVICE_NAME      "com.sun.star.ucb.HierarchyDataReadAccess"
    #define READWRITE_SERVICE_NAME "com.sun.star.ucb.HierarchyDataReadWriteAccess"

    // describe path of cfg entry
    #define CFGPROPERTY_NODEPATH    "nodepath"

    HierarchyEntry::HierarchyEntry(
                    const uno::Reference< uno::XComponentContext >& rxContext,
                    HierarchyContentProvider* pProvider,
                    const OUString& rURL )
    : m_xContext( rxContext ),
      m_xOfficeInstDirs( pProvider->getOfficeInstallationDirectories() ),
      m_bTriedToGetRootReadAccess( false )
    {
        HierarchyUri aUri( rURL );
        m_aServiceSpecifier = aUri.getService();

        m_xConfigProvider
            = pProvider->getConfigProvider( m_aServiceSpecifier );
        m_xRootReadAccess
            = pProvider->getRootConfigReadNameAccess( m_aServiceSpecifier );

        // Note: do not init m_aPath in init list. createPathFromHierarchyURL
        //       needs m_xContext and m_aMutex.
        m_aPath = createPathFromHierarchyURL( aUri );

        // Extract language independent name from URL.
        sal_Int32 nPos = rURL.lastIndexOf( '/' );
        if ( nPos > HIERARCHY_URL_SCHEME_LENGTH )
            m_aName = rURL.copy( nPos + 1 );
        else
            OSL_FAIL( "HierarchyEntry - Invalid URL!" );
    }

    bool HierarchyEntry::hasData()
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );
        uno::Reference< container::XHierarchicalNameAccess > xRootReadAccess
            = getRootReadAccess();

        OSL_ENSURE( xRootReadAccess.is(), "HierarchyEntry::hasData - No root!" );

        if ( xRootReadAccess.is() )
            return xRootReadAccess->hasByHierarchicalName( m_aPath );

        return false;
    }

    bool HierarchyEntry::getData( HierarchyEntryData& rData )
    {
        try
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );

            uno::Reference< container::XHierarchicalNameAccess > xRootReadAccess
                = getRootReadAccess();

            OSL_ENSURE( xRootReadAccess.is(),
                        "HierarchyEntry::getData - No root!" );

            if ( xRootReadAccess.is() )
            {
                OUString aTitlePath = m_aPath;
                aTitlePath += "/Title";

                // Note: Avoid NoSuchElementExceptions, because exceptions are
                //       relatively 'expensive'. Checking for availability of
                //       title value is sufficient here, because if it is
                //       there, the other values will be available too.
                if ( !xRootReadAccess->hasByHierarchicalName( aTitlePath ) )
                    return false;

                OUString aValue;

                // Get Title value.
                if ( !( xRootReadAccess->getByHierarchicalName( aTitlePath )
                        >>= aValue ) )
                {
                    OSL_FAIL( "HierarchyEntry::getData - "
                                "Got no Title value!" );
                    return false;
                }

                rData.setTitle( aValue );

                // Get TargetURL value.
                OUString aTargetURLPath = m_aPath;
                aTargetURLPath += "/TargetURL";
                if ( !( xRootReadAccess->getByHierarchicalName( aTargetURLPath )
                        >>= aValue ) )
                {
                    OSL_FAIL( "HierarchyEntry::getData - "
                                "Got no TargetURL value!" );
                    return false;
                }

                // TargetURL property may contain a reference to the Office
                // installation directory. To ensure a reloctable office
                // installation, the path to the office installtion directory must
                // never be stored directly. A placeholder is used instead. Replace
                // it by actual installation directory.
                if ( m_xOfficeInstDirs.is() &&  !aValue.isEmpty()  )
                    aValue = m_xOfficeInstDirs->makeAbsoluteURL( aValue );
                rData.setTargetURL( aValue );

                OUString aTypePath = m_aPath;
                aTypePath += "/Type";
                if ( xRootReadAccess->hasByHierarchicalName( aTypePath ) )
                {
                    // Might not be present since it was introduced long after
                    // Title and TargetURL (#82433#)... So not getting it is
                    // not an error.

                    // Get Type value.
                    sal_Int32 nType = 0;
                    if ( xRootReadAccess->getByHierarchicalName( aTypePath )
                         >>= nType )
                    {
                        if ( nType == 0 )
                        {
                            rData.setType( HierarchyEntryData::LINK );
                        }
                        else if ( nType == 1 )
                        {
                            rData.setType( HierarchyEntryData::FOLDER );
                        }
                        else
                        {
                            OSL_FAIL( "HierarchyEntry::getData - "
                                        "Unknown Type value!" );
                            return false;
                        }
                    }
                }

                rData.setName( m_aName );
                return true;
            }
        }
        catch ( uno::RuntimeException const & )
        {
            throw;
        }
        catch ( container::NoSuchElementException const & )
        {
            // getByHierarchicalName

            OSL_FAIL( "HierarchyEntry::getData - caught NoSuchElementException!" );
        }
        return false;
    }

    bool HierarchyEntry::setData( const HierarchyEntryData& rData )
    {
        try
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );

            if ( !m_xConfigProvider.is() )
                m_xConfigProvider.set(
                    m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
                    uno::UNO_QUERY );

            if ( m_xConfigProvider.is() )
            {
                // Create parent's key. It must exist!

                OUString aParentPath;
                bool bRoot = true;

                sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
                if ( nPos != -1 )
                {
                    // Skip "/Children" segment of the path, too.
                    nPos = m_aPath.lastIndexOf( '/', nPos - 1 );

                    OSL_ENSURE( nPos != -1,
                                "HierarchyEntry::setData - Wrong path!" );

                    aParentPath += m_aPath.copy( 0, nPos );
                    bRoot = false;
                }

                uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
                {
                    {CFGPROPERTY_NODEPATH, uno::Any(aParentPath)}
                }));

                uno::Reference< util::XChangesBatch > xBatch(
                        m_xConfigProvider->createInstanceWithArguments(
                            READWRITE_SERVICE_NAME,
                            aArguments ),
                        uno::UNO_QUERY );

                OSL_ENSURE( xBatch.is(),
                            "HierarchyEntry::setData - No batch!" );

                uno::Reference< container::XNameAccess > xParentNameAccess(
                    xBatch, uno::UNO_QUERY );

                OSL_ENSURE( xParentNameAccess.is(),
                            "HierarchyEntry::setData - No name access!" );

                if ( xBatch.is() && xParentNameAccess.is() )
                {
                    // Try to create own key. It must not exist!

                    bool bExists = true;
                    uno::Any aMyKey;

                    try
                    {
                        uno::Reference< container::XNameAccess > xNameAccess;

                        if ( bRoot )
                        {
                            xNameAccess = xParentNameAccess;
                        }
                        else
                        {
                            xParentNameAccess->getByName("Children") >>= xNameAccess;
                        }

                        if ( xNameAccess->hasByName( m_aName ) )
                            aMyKey = xNameAccess->getByName( m_aName );
                        else
                            bExists = false;
                    }
                    catch ( container::NoSuchElementException const & )
                    {
                        bExists = false;
                    }

                    uno::Reference< container::XNameReplace >   xNameReplace;
                    uno::Reference< container::XNameContainer > xContainer;

                    if ( bExists )
                    {
                        // Key exists. Replace values.

                        aMyKey >>= xNameReplace;

                        OSL_ENSURE( xNameReplace.is(),
                                    "HierarchyEntry::setData - No name replace!" );
                    }
                    else
                    {
                        // Key does not exist. Create / fill / insert it.

                        uno::Reference< lang::XSingleServiceFactory > xFac;

                        if ( bRoot )
                        {
                            // Special handling for children of root,
                            // which is not an entry. It's only a set
                            // of entries.
                            xFac.set( xParentNameAccess, uno::UNO_QUERY );
                        }
                        else
                        {
                            // Append new entry to parents child list,
                            // which is a set of entries.
                            xParentNameAccess->getByName("Children") >>= xFac;
                        }

                        OSL_ENSURE( xFac.is(),
                                    "HierarchyEntry::setData - No factory!" );

                        if ( xFac.is() )
                        {
                            xNameReplace.set( xFac->createInstance(), uno::UNO_QUERY );

                            OSL_ENSURE( xNameReplace.is(),
                                    "HierarchyEntry::setData - No name replace!" );

                            if ( xNameReplace.is() )
                            {
                                xContainer.set( xFac, uno::UNO_QUERY );

                                OSL_ENSURE( xContainer.is(),
                                    "HierarchyEntry::setData - No container!" );
                            }
                        }
                    }

                    if ( xNameReplace.is() )
                    {
                        // Set Title value.
                        xNameReplace->replaceByName(
                            "Title",
                            uno::makeAny( rData.getTitle() ) );

                        // Set TargetURL value.

                        // TargetURL property may contain a reference to the Office
                        // installation directory. To ensure a reloctable office
                        // installation, the path to the office installtion
                        // directory must never be stored directly. Use a
                        // placeholder instead.
                        OUString aValue( rData.getTargetURL() );
                        if ( m_xOfficeInstDirs.is() &&  !aValue.isEmpty() )
                            aValue
                                = m_xOfficeInstDirs->makeRelocatableURL( aValue );

                        xNameReplace->replaceByName(
                            "TargetURL",
                            uno::makeAny( aValue ) );

                        // Set Type value.
                        sal_Int32 nType
                            = rData.getType() == HierarchyEntryData::LINK ? 0 : 1;
                        xNameReplace->replaceByName(
                            "Type",
                            uno::makeAny( nType ) );

                        if ( xContainer.is() )
                            xContainer->insertByName(
                                m_aName, uno::makeAny( xNameReplace ) );

                        // Commit changes.
                        xBatch->commitChanges();
                        return true;
                    }
                }
            }
        }
        catch ( lang::IllegalArgumentException const & )
        {
            // replaceByName, insertByName

            OSL_FAIL(
                "HierarchyEntry::setData - caught IllegalArgumentException!" );
        }
        catch ( uno::RuntimeException const & )
        {
            throw;
        }
        catch ( container::NoSuchElementException const & )
        {
            // replaceByName, getByName

            OSL_FAIL(
                "HierarchyEntry::setData - caught NoSuchElementException!" );
        }
        catch ( container::ElementExistException const & )
        {
            // insertByName

            OSL_FAIL(
                "HierarchyEntry::setData - caught ElementExistException!" );
        }
        catch ( lang::WrappedTargetException const & )
        {
            // replaceByName, insertByName, getByName, commitChanges

            OSL_FAIL(
                "HierarchyEntry::setData - caught WrappedTargetException!" );
        }
        catch ( uno::Exception const & )
        {
            // createInstance, createInstanceWithArguments

            OSL_FAIL(
                "HierarchyEntry::setData - caught Exception!" );
        }

        return false;
    }

    bool HierarchyEntry::move(
        const OUString& rNewURL, const HierarchyEntryData& rData )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        OUString aNewPath = createPathFromHierarchyURL( HierarchyUri(rNewURL) );

        if ( aNewPath == m_aPath )
            return true;

        bool bOldRoot = true;
        uno::Reference< util::XChangesBatch > xOldParentBatch;

        OUString aNewKey;
        sal_Int32 nURLPos = rNewURL.lastIndexOf( '/' );
        if ( nURLPos > HIERARCHY_URL_SCHEME_LENGTH )
            aNewKey = rNewURL.copy( nURLPos + 1 );
        else
        {
            OSL_FAIL( "HierarchyEntry::move - Invalid URL!" );
            return false;
        }

        bool bNewRoot = true;
        uno::Reference< util::XChangesBatch > xNewParentBatch;

        bool bDifferentParents = true;

        try
        {
            if ( !m_xConfigProvider.is() )
                m_xConfigProvider.set(
                    m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
                    uno::UNO_QUERY );

            if ( !m_xConfigProvider.is() )
                return false;

            OUString aOldParentPath;
            sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
            if ( nPos != -1 )
            {
                // Skip "/Children" segment of the path, too.
                nPos = m_aPath.lastIndexOf( '/', nPos - 1 );

                OSL_ENSURE( nPos != -1, "HierarchyEntry::move - Wrong path!" );

                aOldParentPath += m_aPath.copy( 0, nPos );
                bOldRoot = false;
            }

            OUString aNewParentPath;
            nPos = aNewPath.lastIndexOf( '/' );
            if ( nPos != -1 )
            {
                // Skip "/Children" segment of the path, too.
                nPos = aNewPath.lastIndexOf( '/', nPos - 1 );

                OSL_ENSURE( nPos != -1, "HierarchyEntry::move - Wrong path!" );

                aNewParentPath += aNewPath.copy( 0, nPos );
                bNewRoot = false;
            }

            uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
            {
                {CFGPROPERTY_NODEPATH, uno::Any(aOldParentPath)}
            }));

            xOldParentBatch.set(
                m_xConfigProvider->createInstanceWithArguments(
                    READWRITE_SERVICE_NAME,
                    aArguments ),
                uno::UNO_QUERY );

            OSL_ENSURE( xOldParentBatch.is(), "HierarchyEntry::move - No batch!" );

            if ( !xOldParentBatch.is() )
                return false;

            if ( aOldParentPath == aNewParentPath )
            {
                bDifferentParents = false;
                xNewParentBatch = xOldParentBatch;
            }
            else
            {
                bDifferentParents = true;

                uno::Sequence<uno::Any> aArguments2(comphelper::InitAnyPropertySequence(
                {
                    {CFGPROPERTY_NODEPATH, uno::Any(aNewParentPath)}
                }));

                xNewParentBatch.set(
                    m_xConfigProvider->createInstanceWithArguments(
                        READWRITE_SERVICE_NAME,
                        aArguments2 ),
                    uno::UNO_QUERY );

                OSL_ENSURE(
                    xNewParentBatch.is(), "HierarchyEntry::move - No batch!" );

                if ( !xNewParentBatch.is() )
                    return false;
            }
        }
        catch ( uno::RuntimeException const & )
        {
            throw;
        }
        catch ( uno::Exception const & )
        {
            // createInstance, createInstanceWithArguments

            OSL_FAIL( "HierarchyEntry::move - caught Exception!" );
            return false;
        }

        // (1) Get entry...

        uno::Any aEntry;
        uno::Reference< container::XNameAccess >    xOldParentNameAccess;
        uno::Reference< container::XNameContainer > xOldNameContainer;

        try
        {
            xOldParentNameAccess.set( xOldParentBatch, uno::UNO_QUERY );

            OSL_ENSURE( xOldParentNameAccess.is(),
                        "HierarchyEntry::move - No name access!" );

            if ( !xOldParentNameAccess.is() )
                return false;

            if ( bOldRoot )
            {
                xOldNameContainer.set( xOldParentNameAccess, uno::UNO_QUERY );
            }
            else
            {
                xOldParentNameAccess->getByName("Children") >>= xOldNameContainer;
            }

            aEntry = xOldNameContainer->getByName( m_aName );
        }
        catch ( container::NoSuchElementException const & )
        {
            // getByName

            OSL_FAIL( "HierarchyEntry::move - caught NoSuchElementException!" );
            return false;
        }
        catch ( lang::WrappedTargetException const & )
        {
            // getByName

            OSL_FAIL( "HierarchyEntry::move - caught WrappedTargetException!" );
            return false;
        }

        // (2) Remove entry... Note: Insert BEFORE remove does not work!

        try
        {
            xOldNameContainer->removeByName( m_aName );
            xOldParentBatch->commitChanges();
        }
        catch ( container::NoSuchElementException const & )
        {
            // getByName, removeByName

            OSL_FAIL( "HierarchyEntry::move - caught NoSuchElementException!" );
            return false;
        }

        // (3) Insert entry at new parent...

        try
        {
            uno::Reference< container::XNameReplace > xNewNameReplace;
            aEntry >>= xNewNameReplace;

            OSL_ENSURE( xNewNameReplace.is(),
                        "HierarchyEntry::move - No name replace!" );

            if ( !xNewNameReplace.is() )
                return false;

            uno::Reference< container::XNameAccess > xNewParentNameAccess;
            if ( bDifferentParents )
                xNewParentNameAccess.set( xNewParentBatch, uno::UNO_QUERY );
            else
                xNewParentNameAccess = xOldParentNameAccess;

            OSL_ENSURE( xNewParentNameAccess.is(),
                        "HierarchyEntry::move - No name access!" );

            if ( !xNewParentNameAccess.is() )
                return false;

            uno::Reference< container::XNameContainer > xNewNameContainer;
            if ( bDifferentParents )
            {
                if ( bNewRoot )
                {
                    xNewNameContainer.set( xNewParentNameAccess, uno::UNO_QUERY );
                }
                else
                {
                    xNewParentNameAccess->getByName("Children") >>= xNewNameContainer;
                }
            }
            else
                xNewNameContainer = xOldNameContainer;

            if ( !xNewNameContainer.is() )
                return false;

            xNewNameReplace->replaceByName(
                "Title",
                uno::makeAny( rData.getTitle() ) );

            // TargetURL property may contain a reference to the Office
            // installation directory. To ensure a reloctable office
            // installation, the path to the office installtion
            // directory must never be stored directly. Use a placeholder
            // instead.
            OUString aValue( rData.getTargetURL() );
            if ( m_xOfficeInstDirs.is() &&  !aValue.isEmpty() )
                aValue = m_xOfficeInstDirs->makeRelocatableURL( aValue );
            xNewNameReplace->replaceByName(
                "TargetURL",
                uno::makeAny( aValue ) );
            sal_Int32 nType = rData.getType() == HierarchyEntryData::LINK ? 0 : 1;
            xNewNameReplace->replaceByName(
                "Type",
                uno::makeAny( nType ) );

            xNewNameContainer->insertByName( aNewKey, aEntry );
            xNewParentBatch->commitChanges();
        }
        catch ( container::NoSuchElementException const & )
        {
            // replaceByName, insertByName, getByName

            OSL_FAIL( "HierarchyEntry::move - caught NoSuchElementException!" );
            return false;
        }
        catch ( lang::IllegalArgumentException const & )
        {
            // replaceByName, insertByName

            OSL_FAIL(
                "HierarchyEntry::move - caught IllegalArgumentException!" );
            return false;
        }
        catch ( container::ElementExistException const & )
        {
            // insertByName

            OSL_FAIL( "HierarchyEntry::move - caught ElementExistException!" );
            return false;
        }
        catch ( lang::WrappedTargetException const & )
        {
            // replaceByName, insertByName, getByName

            OSL_FAIL( "HierarchyEntry::move - caught WrappedTargetException!" );
            return false;
        }

        return true;
    }

    bool HierarchyEntry::remove()
    {
        try
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );

            if ( !m_xConfigProvider.is() )
                m_xConfigProvider.set(
                    m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
                    uno::UNO_QUERY );

            if ( m_xConfigProvider.is() )
            {
                // Create parent's key. It must exist!

                OUString aParentPath;
                bool bRoot = true;

                sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
                if ( nPos != -1 )
                {
                    // Skip "/Children" segment of the path, too.
                    nPos = m_aPath.lastIndexOf( '/', nPos - 1 );

                    OSL_ENSURE( nPos != -1,
                                "HierarchyEntry::remove - Wrong path!" );

                    aParentPath += m_aPath.copy( 0, nPos );
                    bRoot = false;
                }

                uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
                {
                    {CFGPROPERTY_NODEPATH, uno::Any(aParentPath)}
                }));

                uno::Reference< util::XChangesBatch > xBatch(
                    m_xConfigProvider->createInstanceWithArguments(
                        READWRITE_SERVICE_NAME,
                        aArguments ),
                    uno::UNO_QUERY );

                OSL_ENSURE( xBatch.is(),
                            "HierarchyEntry::remove - No batch!" );

                uno::Reference< container::XNameAccess > xParentNameAccess(
                    xBatch, uno::UNO_QUERY );

                OSL_ENSURE( xParentNameAccess.is(),
                            "HierarchyEntry::remove - No name access!" );

                if ( xBatch.is() && xParentNameAccess.is() )
                {
                    uno::Reference< container::XNameContainer > xContainer;

                    if ( bRoot )
                    {
                        // Special handling for children of root,
                        // which is not an entry. It's only a set
                        // of entries.
                        xContainer.set( xParentNameAccess, uno::UNO_QUERY );
                    }
                    else
                    {
                        // Append new entry to parents child list,
                        // which is a set of entries.
                         xParentNameAccess->getByName("Children") >>= xContainer;
                    }

                    OSL_ENSURE( xContainer.is(),
                                "HierarchyEntry::remove - No container!" );

                    if ( xContainer.is() )
                    {
                        xContainer->removeByName( m_aName );
                        xBatch->commitChanges();
                        return true;
                    }
                }
            }
        }
        catch ( uno::RuntimeException const & )
        {
            throw;
        }
        catch ( container::NoSuchElementException const & )
        {
            // getByName, removeByName

            OSL_FAIL(
                "HierarchyEntry::remove - caught NoSuchElementException!" );
        }
        catch ( lang::WrappedTargetException const & )
        {
            // getByName, commitChanges

            OSL_FAIL(
                "HierarchyEntry::remove - caught WrappedTargetException!" );
        }
        catch ( uno::Exception const & )
        {
            // createInstance, createInstanceWithArguments

            OSL_FAIL( "HierarchyEntry::remove - caught Exception!" );
        }

        return false;
    }

    bool HierarchyEntry::first( iterator const & it )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        if ( it.m_pImpl->pos == -1 )
        {
            // Init...

            try
            {
                uno::Reference< container::XHierarchicalNameAccess >
                    xRootHierNameAccess = getRootReadAccess();

                if ( xRootHierNameAccess.is() )
                {
                    uno::Reference< container::XNameAccess > xNameAccess;

                    if ( !m_aPath.isEmpty() )
                    {
                        OUString aPath = m_aPath;
                        aPath += "/Children";

                        xRootHierNameAccess->getByHierarchicalName( aPath )
                            >>= xNameAccess;
                    }
                    else
                        xNameAccess.set( xRootHierNameAccess, uno::UNO_QUERY );

                    OSL_ENSURE( xNameAccess.is(),
                                "HierarchyEntry::first - No name access!" );

                    if ( xNameAccess.is() )
                        it.m_pImpl->names = xNameAccess->getElementNames();

                    uno::Reference< container::XHierarchicalNameAccess >
                        xHierNameAccess( xNameAccess, uno::UNO_QUERY );

                    OSL_ENSURE( xHierNameAccess.is(),
                                "HierarchyEntry::first - No hier. name access!" );

                    it.m_pImpl->dir = xHierNameAccess;

                    it.m_pImpl->officeDirs = m_xOfficeInstDirs;
                }
            }
            catch ( uno::RuntimeException const & )
            {
                throw;
            }
            catch ( container::NoSuchElementException const& )
            {
                // getByHierarchicalName

                OSL_FAIL(
                    "HierarchyEntry::first - caught NoSuchElementException!" );
            }
            catch ( uno::Exception const & )
            {
                OSL_FAIL( "HierarchyEntry::first - caught Exception!" );
            }
        }

        if ( it.m_pImpl->names.getLength() == 0 )
            return false;

        it.m_pImpl->pos = 0;
        return true;
    }

    bool HierarchyEntry::next( iterator const & it )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        if ( it.m_pImpl->pos == -1 )
            return first( it );

        ++(it.m_pImpl->pos);

        return ( it.m_pImpl->pos < it.m_pImpl->names.getLength() );
    }

    OUString HierarchyEntry::createPathFromHierarchyURL(
        const HierarchyUri& rURI )
    {
        // Transform path....
        // folder/subfolder/subsubfolder
        //      --> ['folder']/Children/['subfolder']/Children/['subsubfolder']

        const OUString aPath = rURI.getPath().copy( 1 ); // skip leading slash.
        sal_Int32 nLen = aPath.getLength();

        if ( nLen )
        {
            OUStringBuffer aNewPath;
            aNewPath.append( "['" );

            sal_Int32 nStart = 0;
            sal_Int32 nEnd   = aPath.indexOf( '/' );

            do
            {
                if ( nEnd == -1 )
                    nEnd = nLen;

                OUString aToken = aPath.copy( nStart, nEnd - nStart );
                makeXMLName( aToken, aNewPath );

                if ( nEnd != nLen )
                {
                    aNewPath.append( "']/Children/['" );
                    nStart = nEnd + 1;
                    nEnd   = aPath.indexOf( '/', nStart );
                }
                else
                    aNewPath.append( "']" );
            }
            while ( nEnd != nLen );

            return aNewPath.makeStringAndClear();
        }

        return aPath;
    }

    uno::Reference< container::XHierarchicalNameAccess >
    HierarchyEntry::getRootReadAccess()
    {
        if ( !m_xRootReadAccess.is() )
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );
            if ( !m_xRootReadAccess.is() )
            {
                if ( m_bTriedToGetRootReadAccess )
                {
                    OSL_FAIL( "HierarchyEntry::getRootReadAccess - "
                                "Unable to read any config data! -> #82494#" );
                    return uno::Reference< container::XHierarchicalNameAccess >();
                }

                try
                {
                    if ( !m_xConfigProvider.is() )
                        m_xConfigProvider.set(
                                m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
                                uno::UNO_QUERY );

                    if ( m_xConfigProvider.is() )
                    {
                        // Create Root object.

                        uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
                        {
                            {CFGPROPERTY_NODEPATH, uno::Any(OUString())} // root path
                        }));

                        m_bTriedToGetRootReadAccess = true;

                        m_xRootReadAccess.set(
                                m_xConfigProvider->createInstanceWithArguments(
                                    READ_SERVICE_NAME,
                                    aArguments ),
                                uno::UNO_QUERY );
                    }
                }
                catch ( uno::RuntimeException const & )
                {
                    throw;
                }
                catch ( uno::Exception const & )
                {
                    // createInstance, createInstanceWithArguments

                    OSL_FAIL( "HierarchyEntry::getRootReadAccess - "
                                "caught Exception!" );
                }
            }
        }
        return m_xRootReadAccess;
    }

    // HierarchyEntry::iterator Implementation.

    HierarchyEntry::iterator::iterator()
        : m_pImpl( new iterator_Impl )
    {
    }

    HierarchyEntry::iterator::~iterator()
    {
    }

    const HierarchyEntryData& HierarchyEntry::iterator::operator*() const
    {
        if ( ( m_pImpl->pos != -1 )
             && ( m_pImpl->dir.is() )
             && ( m_pImpl->pos < m_pImpl->names.getLength() ) )
        {
            try
            {
                OUStringBuffer aKey;
                aKey.append( "['" );
                makeXMLName( m_pImpl->names.getConstArray()[ m_pImpl->pos ], aKey );
                aKey.append( "']" );

                OUString aTitle     = aKey.makeStringAndClear();
                OUString aTargetURL = aTitle;
                OUString aType      = aTitle;

                aTitle     += "/Title";
                aTargetURL += "/TargetURL";
                aType      += "/Type";

                OUString aValue;
                m_pImpl->dir->getByHierarchicalName( aTitle ) >>= aValue;
                m_pImpl->entry.setTitle( aValue );

                m_pImpl->dir->getByHierarchicalName( aTargetURL ) >>= aValue;

                // TargetURL property may contain a reference to the Office
                // installation directory. To ensure a reloctable office
                // installation, the path to the office installtion directory must
                // never be stored directly. A placeholder is used instead. Replace
                // it by actual installation directory.
                if ( m_pImpl->officeDirs.is() && !aValue.isEmpty() )
                    aValue = m_pImpl->officeDirs->makeAbsoluteURL( aValue );
                m_pImpl->entry.setTargetURL( aValue );

                if ( m_pImpl->dir->hasByHierarchicalName( aType ) )
                {
                    // Might not be present since it was introduced long
                    // after Title and TargetURL (#82433#)... So not getting
                    // it is not an error.

                    // Get Type value.
                    sal_Int32 nType = 0;
                    if ( m_pImpl->dir->getByHierarchicalName( aType ) >>= nType )
                    {
                        if ( nType == 0 )
                        {
                            m_pImpl->entry.setType( HierarchyEntryData::LINK );
                        }
                        else if ( nType == 1 )
                        {
                            m_pImpl->entry.setType( HierarchyEntryData::FOLDER );
                        }
                        else
                        {
                            OSL_FAIL( "HierarchyEntry::getData - "
                                        "Unknown Type value!" );
                        }
                    }
                }

                m_pImpl->entry.setName(
                    m_pImpl->names.getConstArray()[ m_pImpl->pos ] );
            }
            catch ( container::NoSuchElementException const & )
            {
                m_pImpl->entry = HierarchyEntryData();
            }
        }

        return m_pImpl->entry;
    }

    } // namespace hierarchy_ucp

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression 'nType == 0' is always true.