/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

/**************************************************************************
								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>
#ifndef _COM_SUN_STAR_UTIL_XOFFICEINSTALLTIONDIRECTORIES_HPP_
#include <com/sun/star/util/XOfficeInstallationDirectories.hpp>
#endif
#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< rtl::OUString>                          names;
    sal_Int32                                              pos;
    iterator_Impl()
    : officeDirs( 0 ), pos( -1 /* before first */ ) {};
};

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

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

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

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

            case '>':
                rBuffer.appendAscii( "&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< lang::XMultiServiceFactory >& rSMgr,
				HierarchyContentProvider* pProvider,
				const rtl::OUString& rURL )
: m_xSMgr( rSMgr ),
  m_xOfficeInstDirs( pProvider->getOfficeInstallationDirectories() ),
  m_bTriedToGetRootReadAccess( sal_False )
{
    HierarchyUri aUri( rURL );
    m_aServiceSpecifier = aUri.getService();

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

	// Note: do not init m_aPath in init list. createPathFromHierarchyURL
	//       needs m_xSMgr 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_ENSURE( sal_False, "HierarchyEntry - Invalid URL!" );
}

//=========================================================================
sal_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 sal_False;
}

//=========================================================================
sal_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() )
		{
			rtl::OUString aTitlePath = m_aPath;
			aTitlePath += rtl::OUString::createFromAscii( "/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 sal_False;

            rtl::OUString aValue;

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

            rData.setTitle( aValue );

			// Get TargetURL value.
			rtl::OUString aTargetURLPath = m_aPath;
			aTargetURLPath += rtl::OUString::createFromAscii( "/TargetURL" );
			if ( !( xRootReadAccess->getByHierarchicalName( aTargetURLPath ) 
                    >>= aValue ) )
			{
                OSL_ENSURE( sal_False,
							"HierarchyEntry::getData - "
							"Got no TargetURL value!" );
				return sal_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.getLength() > 0 ) )
                aValue = m_xOfficeInstDirs->makeAbsoluteURL( aValue );
            rData.setTargetURL( aValue );

            rtl::OUString aTypePath = m_aPath;
            aTypePath += rtl::OUString::createFromAscii( "/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_ENSURE( sal_False,
                                    "HierarchyEntry::getData - "
                                    "Unknown Type value!" );
                        return sal_False;
                    }
                }
            }

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

        OSL_ENSURE( sal_False,
					"HierarchyEntry::getData - caught NoSuchElementException!" );
	}
	return sal_False;
}

//=========================================================================
sal_Bool HierarchyEntry::setData(
					const HierarchyEntryData& rData, sal_Bool bCreate )
{
	try
	{
		osl::Guard< osl::Mutex > aGuard( m_aMutex );

		if ( !m_xConfigProvider.is() )
			m_xConfigProvider = uno::Reference< lang::XMultiServiceFactory >(
                m_xSMgr->createInstance( m_aServiceSpecifier ),
				uno::UNO_QUERY );

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

            rtl::OUString aParentPath;
			sal_Bool bRoot = sal_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 = sal_False;
			}

            uno::Sequence< uno::Any > aArguments( 1 );
			beans::PropertyValue      aProperty;

			aProperty.Name	  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
													CFGPROPERTY_NODEPATH ) );
			aProperty.Value	<<= aParentPath;
			aArguments[ 0 ]	<<= aProperty;

			uno::Reference< util::XChangesBatch > xBatch(
					m_xConfigProvider->createInstanceWithArguments(
                        rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                            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!

				sal_Bool bExists = sal_True;
				uno::Any aMyKey;

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

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

                    if ( xNameAccess->hasByName( m_aName ) )
                        aMyKey = xNameAccess->getByName( m_aName );
					else
						bExists = sal_False;
				}
				catch ( container::NoSuchElementException const & )
				{
					bExists = sal_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
				{
					if ( !bCreate )
						return sal_True;

					// 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 = uno::Reference< lang::XSingleServiceFactory >(
                            xParentNameAccess, uno::UNO_QUERY );
					}
					else
					{
						// Append new entry to parents child list,
						// which is a set of entries.
						xParentNameAccess->getByName(
										rtl::OUString::createFromAscii(
											"Children" ) ) >>= xFac;
					}

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

					if ( xFac.is() )
					{
						xNameReplace 
                            = uno::Reference< container::XNameReplace >(
                                xFac->createInstance(), uno::UNO_QUERY );

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

						if ( xNameReplace.is() )
						{
							xContainer 
                                = uno::Reference< container::XNameContainer >(
                                    xFac, uno::UNO_QUERY );

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

				if ( xNameReplace.is() )
				{
					// Set Title value.
					xNameReplace->replaceByName(
                        rtl::OUString::createFromAscii( "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.
                    rtl::OUString aValue( rData.getTargetURL() );
                    if ( m_xOfficeInstDirs.is() && ( aValue.getLength() > 0 ) )
                        aValue 
                            = m_xOfficeInstDirs->makeRelocatableURL( aValue );

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

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

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

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

        OSL_ENSURE( 
            sal_False,
            "HierarchyEntry::setData - caught IllegalArgumentException!" );
	}
	catch ( container::NoSuchElementException const & )
	{
		// replaceByName, getByName

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

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

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

        OSL_ENSURE( 
            sal_False,
            "HierarchyEntry::setData - caught Exception!" );
	}

	return sal_False;
}

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

	rtl::OUString aNewPath = createPathFromHierarchyURL( rNewURL );

	if ( aNewPath == m_aPath )
		return sal_True;

#if 0
       // In the "near future"... ( not yet implemented in config db )

       - get update access for m_aPath
       - update access -> XNamed
       - xNamed::setName( newName )
       - updateaccess commit
#else

	sal_Bool bOldRoot = sal_True;
	uno::Reference< util::XChangesBatch > xOldParentBatch;

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

	sal_Bool bNewRoot = sal_True;
	uno::Reference< util::XChangesBatch > xNewParentBatch;

	sal_Bool bDifferentParents = sal_True;

	try
	{
		if ( !m_xConfigProvider.is() )
			m_xConfigProvider = uno::Reference< lang::XMultiServiceFactory >(
                m_xSMgr->createInstance( m_aServiceSpecifier ),
				uno::UNO_QUERY );

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

        rtl::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 = sal_False;
		}

        rtl::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 = sal_False;
		}

        uno::Sequence< uno::Any > aArguments( 1 );
		beans::PropertyValue	  aProperty;

        aProperty.Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
													CFGPROPERTY_NODEPATH ) );
		aProperty.Value	<<= aOldParentPath;
		aArguments[ 0 ]	<<= aProperty;

		xOldParentBatch = uno::Reference< util::XChangesBatch >(
			m_xConfigProvider->createInstanceWithArguments(
                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                    READWRITE_SERVICE_NAME ) ),
				aArguments ),
			uno::UNO_QUERY );

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

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

		if ( aOldParentPath == aNewParentPath )
		{
			bDifferentParents = sal_False;
			xNewParentBatch = xOldParentBatch;
		}
		else
		{
			bDifferentParents = sal_True;

			aProperty.Name	  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
													CFGPROPERTY_NODEPATH ) );
			aProperty.Value	<<= aNewParentPath;
			aArguments[ 0 ]	<<= aProperty;

			xNewParentBatch = uno::Reference< util::XChangesBatch >(
				m_xConfigProvider->createInstanceWithArguments(
                    rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                        READWRITE_SERVICE_NAME ) ),
					aArguments ),
				uno::UNO_QUERY );

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

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

        OSL_ENSURE( sal_False, "HierarchyEntry::move - caught Exception!" );
		return sal_False;
	}

	//////////////////////////////////////////////////////////////////////
	// (1) Get entry...
	//////////////////////////////////////////////////////////////////////

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

	try
	{
		xOldParentNameAccess
			= uno::Reference< container::XNameAccess >(	
                xOldParentBatch, uno::UNO_QUERY );

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

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

		if ( bOldRoot )
		{
			xOldNameContainer = uno::Reference< container::XNameContainer >(
										xOldParentNameAccess, uno::UNO_QUERY );
		}
		else
		{
			xOldParentNameAccess->getByName(
 				rtl::OUString::createFromAscii( "Children" ) ) 
                    >>= xOldNameContainer;
		}

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

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

        OSL_ENSURE( sal_False,
					"HierarchyEntry::move - caught WrappedTargetException!" );
		return sal_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_ENSURE( sal_False,
					"HierarchyEntry::move - caught NoSuchElementException!" );
		return sal_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 sal_False;

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

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

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

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

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

		xNewNameReplace->replaceByName(
            rtl::OUString::createFromAscii( "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.
        rtl::OUString aValue( rData.getTargetURL() );
        if ( m_xOfficeInstDirs.is() && ( aValue.getLength() > 0 ) )
            aValue = m_xOfficeInstDirs->makeRelocatableURL( aValue );
		xNewNameReplace->replaceByName(
            rtl::OUString::createFromAscii( "TargetURL" ),
            uno::makeAny( aValue ) );
        sal_Int32 nType = rData.getType() == HierarchyEntryData::LINK ? 0 : 1;
        xNewNameReplace->replaceByName(
            rtl::OUString::createFromAscii( "Type" ),
            uno::makeAny( nType ) );

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

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

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

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

        OSL_ENSURE( sal_False,
					"HierarchyEntry::move - caught WrappedTargetException!" );
		return sal_False;
	}

#if 0
	//////////////////////////////////////////////////////////////////////
	// (4) Commit changes...
	//////////////////////////////////////////////////////////////////////

	try
	{
		xNewParentBatch->commitChanges();

		if ( bDifferentParents )
			xOldParentBatch->commitChanges();
	}
	catch ( lang::WrappedTargetException const & )
	{
		// commitChanges

        OSL_ENSURE( sal_False,
					"HierarchyEntry::move - caught WrappedTargetException!" );
		return sal_False;
	}
#endif

	return sal_True;
#endif
}

//=========================================================================
sal_Bool HierarchyEntry::remove()
{
	try
	{
		osl::Guard< osl::Mutex > aGuard( m_aMutex );

		if ( !m_xConfigProvider.is() )
			m_xConfigProvider = uno::Reference< lang::XMultiServiceFactory >(
                m_xSMgr->createInstance( m_aServiceSpecifier ),
				uno::UNO_QUERY );

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

            rtl::OUString aParentPath;
			sal_Bool bRoot = sal_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 = sal_False;
			}

            uno::Sequence< uno::Any > aArguments( 1 );
			beans::PropertyValue	  aProperty;

			aProperty.Name	  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
													CFGPROPERTY_NODEPATH ) );
			aProperty.Value	<<= aParentPath;
			aArguments[ 0 ]	<<= aProperty;

			uno::Reference< util::XChangesBatch > xBatch(
				m_xConfigProvider->createInstanceWithArguments(
                    rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                        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 = uno::Reference< container::XNameContainer >(
                        xParentNameAccess, uno::UNO_QUERY );
				}
				else
				{
					// Append new entry to parents child list,
					// which is a set of entries.
 					xParentNameAccess->getByName(
                        rtl::OUString::createFromAscii( "Children" ) )
                            >>= xContainer;
				}

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

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

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

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

        OSL_ENSURE( sal_False,
					"HierarchyEntry::remove - caught Exception!" );
	}

	return sal_False;
}

//=========================================================================
sal_Bool HierarchyEntry::first( iterator& 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.getLength() > 0 )
				{
					rtl::OUString aPath = m_aPath;
					aPath += rtl::OUString::createFromAscii( "/Children" );

					xRootHierNameAccess->getByHierarchicalName( aPath )
						>>= xNameAccess;
				}
				else
					xNameAccess
						= uno::Reference< container::XNameAccess >(
								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_ENSURE( 
                sal_False,
                "HierarchyEntry::first - caught NoSuchElementException!" );
		}
		catch ( uno::Exception const & )
		{
            OSL_ENSURE( sal_False,
					"HierarchyEntry::first - caught Exception!" );
		}
	}

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

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

//=========================================================================
sal_Bool HierarchyEntry::next( iterator& 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() );
}

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

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

	if ( nLen )
	{
        rtl::OUStringBuffer aNewPath;
        aNewPath.appendAscii( "['" );

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

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

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

			if ( nEnd != nLen )
			{
                aNewPath.appendAscii( "']/Children/['" );
				nStart = nEnd + 1;
                nEnd   = aPath.indexOf( '/', nStart );
			}
            else
                aNewPath.appendAscii( "']" );
		}
		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 ) // #82494#
			{
				OSL_ENSURE( sal_False,
							"HierarchyEntry::getRootReadAccess - "
							"Unable to read any config data! -> #82494#" );
				return uno::Reference< container::XHierarchicalNameAccess >();
			}

			try
			{
				if ( !m_xConfigProvider.is() )
					m_xConfigProvider 
                        = uno::Reference< lang::XMultiServiceFactory >(
                            m_xSMgr->createInstance( m_aServiceSpecifier ),
                            uno::UNO_QUERY );

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

					uno::Sequence< uno::Any > aArguments( 1 );
                    beans::PropertyValue      aProperty;
                    aProperty.Name = rtl::OUString( 
                        RTL_CONSTASCII_USTRINGPARAM( CFGPROPERTY_NODEPATH ) );
                    aProperty.Value <<= rtl::OUString(); // root path
                    aArguments[ 0 ] <<= aProperty;

					m_bTriedToGetRootReadAccess = sal_True;

					m_xRootReadAccess 
                        = uno::Reference< container::XHierarchicalNameAccess >(
                            m_xConfigProvider->createInstanceWithArguments(
                                rtl::OUString(
                                    RTL_CONSTASCII_USTRINGPARAM(
                                        READ_SERVICE_NAME ) ),
                                aArguments ),
                            uno::UNO_QUERY );
				}
			}
			catch ( uno::RuntimeException const & )
			{
				throw;
			}
			catch ( uno::Exception const & )
			{
				// createInstance, createInstanceWithArguments

                OSL_ENSURE( sal_False,
							"HierarchyEntry::getRootReadAccess - "
							"caught Exception!" );
			}
		}
	}
	return m_xRootReadAccess;
}

//=========================================================================
//=========================================================================
//
// HierarchyEntry::iterator Implementation.
//
//=========================================================================
//=========================================================================

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

//=========================================================================
HierarchyEntry::iterator::~iterator()
{
	delete m_pImpl;
}

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

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

			aTitle     += rtl::OUString::createFromAscii( "/Title" );
			aTargetURL += rtl::OUString::createFromAscii( "/TargetURL" );
            aType      += rtl::OUString::createFromAscii( "/Type" );

            rtl::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.getLength() > 0 ) )
                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_ENSURE( sal_False,
                                    "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
