/*************************************************************************
 *
 *  $RCSfile: hierarchycontent.cxx,v $
 *
 *  $Revision: 1.23 $
 *
 *  last change: $Author: vg $ $Date: 2003-07-02 14:59:24 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): Kai Sommerfeld ( kso@sun.com )
 *
 *
 ************************************************************************/

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

 - optimize transfer command. "Move" should be implementable much more
   efficient!

 **************************************************************************

 - Root Folder vs. 'normal' Folder
     - root doesn't support command 'delete'
    - root doesn't support command 'insert'
    - root needs not created via XContentCreator - queryContent with root
      folder id ( HIERARCHY_ROOT_FOLDER_URL ) always returns a value != 0
    - root has no parent.

 *************************************************************************/

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

#include "osl/doublecheckedlocking.h"

#ifndef _RTL_USTRING_H_
#include <rtl/ustring.h>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _COM_SUN_STAR_BEANS_PROPERTYATTRIBUTE_HPP_
#include <com/sun/star/beans/PropertyAttribute.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_PROPERTYSTATE_HPP_
#include <com/sun/star/beans/PropertyState.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_
#include <com/sun/star/beans/PropertyValue.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYACCESS_HPP_
#include <com/sun/star/beans/XPropertyAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_ILLEGALACCESSEXCEPTION_HPP_
#include <com/sun/star/lang/IllegalAccessException.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XROW_HPP_
#include <com/sun/star/sdbc/XRow.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_CONTENTINFOATTRIBUTE_HPP_
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_INSERTCOMMANDARGUMENT_HPP_
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_INTERACTIVEBADTRANSFERURLEXCEPTION_HPP_
#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_MISSINGPROPERTIESEXCEPTION_HPP_
#include <com/sun/star/ucb/MissingPropertiesException.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_NAMECLASH_HPP_
#include <com/sun/star/ucb/NameClash.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_NAMECLASHEXCEPTION_HPP_
#include <com/sun/star/ucb/NameClashException.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_OPENCOMMANDARGUMENT2_HPP_
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_TRANSFERINFO_HPP_
#include <com/sun/star/ucb/TransferInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_UNSUPPORTEDNAMECLASHEXCEPTION_HPP_
#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_XCOMMANDINFO_HPP_
#include <com/sun/star/ucb/XCommandInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_XPERSISTENTPROPERTYSET_HPP_
#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_ANY_HXX_
#include <com/sun/star/uno/Any.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif
#ifndef _UCBHELPER_CONTENTIDENTIFIER_HXX
#include <ucbhelper/contentidentifier.hxx>
#endif
#ifndef _UCBHELPER_PROPERTYVALUESET_HXX
#include <ucbhelper/propertyvalueset.hxx>
#endif
#ifndef _UCBHELPER_CANCELCOMMANDEXECUTION_HXX_
#include <ucbhelper/cancelcommandexecution.hxx>
#endif

#ifndef _HIERARCHYCONTENT_HXX
#include "hierarchycontent.hxx"
#endif
#ifndef _HIERARCHYPROVIDER_HXX
#include "hierarchyprovider.hxx"
#endif
#ifndef _DYNAMICRESULTSET_HXX
#include "dynamicresultset.hxx"
#endif
#ifndef _HIERARCHYURI_HXX
#include "hierarchyuri.hxx"
#endif

using namespace com::sun;
using namespace com::sun::star;

using namespace hierarchy_ucp;

//=========================================================================
//=========================================================================
//
// HierarchyContent Implementation.
//
//=========================================================================
//=========================================================================

// static ( "virtual" ctor )
HierarchyContent* HierarchyContent::create(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier )
{
    // Fail, if content does not exist.
    HierarchyContentProperties aProps;
    if ( !loadData( rxSMgr, pProvider, Identifier, aProps ) )
        return 0;

    return new HierarchyContent( rxSMgr, pProvider, Identifier, aProps );
}

//=========================================================================
// static ( "virtual" ctor )
HierarchyContent* HierarchyContent::create(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier,
            const star::ucb::ContentInfo& Info )
{
    if ( !Info.Type.getLength() )
        return 0;

    if ( !Info.Type.equalsAsciiL(
            RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) ) &&
         !Info.Type.equalsAsciiL(
            RTL_CONSTASCII_STRINGPARAM( HIERARCHY_LINK_CONTENT_TYPE ) ) )
        return 0;

#if 0
    // Fail, if content does exist.
    if ( hasData( rxSMgr, pProvider, Identifier ) )
        return 0;
#endif

    return new HierarchyContent( rxSMgr, pProvider, Identifier, Info );
}

//=========================================================================
HierarchyContent::HierarchyContent(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier,
            const HierarchyContentProperties& rProps )
: ContentImplHelper( rxSMgr, pProvider, Identifier ),
  m_aProps( rProps ),
  m_eState( PERSISTENT ),
  m_pProvider( pProvider ),
  m_bCheckedReadOnly( false ),
  m_bIsReadOnly( true )
{
    setKind( Identifier );
}

//=========================================================================
HierarchyContent::HierarchyContent(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier,
            const star::ucb::ContentInfo& Info )
: ContentImplHelper( rxSMgr, pProvider, Identifier, sal_False ),
  m_aProps( Info.Type.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) )
            ? HierarchyEntryData::FOLDER
            : HierarchyEntryData::LINK ),
  m_eState( TRANSIENT ),
  m_pProvider( pProvider ),
  m_bCheckedReadOnly( false ),
  m_bIsReadOnly( true )
{
    setKind( Identifier );
}

//=========================================================================
// virtual
HierarchyContent::~HierarchyContent()
{
}

//=========================================================================
//
// XInterface methods.
//
//=========================================================================

// virtual
void SAL_CALL HierarchyContent::acquire()
    throw( )
{
    ContentImplHelper::acquire();
}

//=========================================================================
// virtual
void SAL_CALL HierarchyContent::release()
    throw( )
{
    ContentImplHelper::release();
}

//=========================================================================
// virtual
uno::Any SAL_CALL HierarchyContent::queryInterface( const uno::Type & rType )
    throw ( uno::RuntimeException )
{
    uno::Any aRet = ContentImplHelper::queryInterface( rType );

    if ( !aRet.hasValue() )
    {
        // Note: isReadOnly may be relative expensive. So avoid calling it
        //       unless it is really necessary.
        aRet = cppu::queryInterface(
                rType, static_cast< star::ucb::XContentCreator * >( this ) );
        if ( aRet.hasValue() )
        {
            if ( !isFolder() || isReadOnly() )
                return uno::Any();
        }
    }

    return aRet;
}

//=========================================================================
//
// XTypeProvider methods.
//
//=========================================================================

XTYPEPROVIDER_COMMON_IMPL( HierarchyContent );

//=========================================================================
// virtual
uno::Sequence< uno::Type > SAL_CALL HierarchyContent::getTypes()
    throw( uno::RuntimeException )
{
    cppu::OTypeCollection * pCollection = 0;

    if ( isFolder() && !isReadOnly() )
    {
        static cppu::OTypeCollection* pFolderTypes = 0;

        pCollection = pFolderTypes;
        if ( !pCollection )
        {
            osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );

            pCollection = pFolderTypes;
            if ( !pCollection )
            {
                static cppu::OTypeCollection aCollection(
                    CPPU_TYPE_REF( lang::XTypeProvider ),
                    CPPU_TYPE_REF( lang::XServiceInfo ),
                    CPPU_TYPE_REF( lang::XComponent ),
                    CPPU_TYPE_REF( star::ucb::XContent ),
                    CPPU_TYPE_REF( star::ucb::XCommandProcessor ),
                    CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
                    CPPU_TYPE_REF( star::ucb::XCommandInfoChangeNotifier ),
                    CPPU_TYPE_REF( beans::XPropertyContainer ),
                    CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
                    CPPU_TYPE_REF( container::XChild ),
                    CPPU_TYPE_REF( star::ucb::XContentCreator ) ); // !!
                pCollection = &aCollection;
                OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
                pFolderTypes = pCollection;
            }
        }
        else
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
    }
    else
    {
        static cppu::OTypeCollection* pDocumentTypes = 0;

        pCollection = pDocumentTypes;
        if ( !pCollection )
        {
            osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );

            pCollection = pDocumentTypes;
            if ( !pCollection )
            {
                static cppu::OTypeCollection aCollection(
                    CPPU_TYPE_REF( lang::XTypeProvider ),
                    CPPU_TYPE_REF( lang::XServiceInfo ),
                    CPPU_TYPE_REF( lang::XComponent ),
                    CPPU_TYPE_REF( star::ucb::XContent ),
                    CPPU_TYPE_REF( star::ucb::XCommandProcessor ),
                    CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
                    CPPU_TYPE_REF( star::ucb::XCommandInfoChangeNotifier ),
                    CPPU_TYPE_REF( beans::XPropertyContainer ),
                    CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
                    CPPU_TYPE_REF( container::XChild ) );
                pCollection = &aCollection;
                OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
                pDocumentTypes = pCollection;
            }
        }
        else
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
    }

    return (*pCollection).getTypes();
}

//=========================================================================
//
// XServiceInfo methods.
//
//=========================================================================

// virtual
rtl::OUString SAL_CALL HierarchyContent::getImplementationName()
    throw( uno::RuntimeException )
{
    return rtl::OUString::createFromAscii(
                            "com.sun.star.comp.ucb.HierarchyContent" );
}

//=========================================================================
// virtual
uno::Sequence< rtl::OUString > SAL_CALL
HierarchyContent::getSupportedServiceNames()
    throw( uno::RuntimeException )
{
    uno::Sequence< rtl::OUString > aSNS( 1 );

    if ( m_eKind == LINK )
        aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii(
                                HIERARCHY_LINK_CONTENT_SERVICE_NAME );
    else if ( m_eKind == FOLDER )
        aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii(
                                HIERARCHY_FOLDER_CONTENT_SERVICE_NAME );
    else
        aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii(
                                HIERARCHY_ROOT_FOLDER_CONTENT_SERVICE_NAME );

    return aSNS;
}

//=========================================================================
//
// XContent methods.
//
//=========================================================================

// virtual
rtl::OUString SAL_CALL HierarchyContent::getContentType()
    throw( uno::RuntimeException )
{
    return m_aProps.getContentType();
}

//=========================================================================
// virtual
uno::Reference< star::ucb::XContentIdentifier > SAL_CALL
HierarchyContent::getIdentifier()
    throw( uno::RuntimeException )
{
    // Transient?
    if ( m_eState == TRANSIENT )
    {
        // Transient contents have no identifier.
        return uno::Reference< star::ucb::XContentIdentifier >();
    }

    return ContentImplHelper::getIdentifier();
}

//=========================================================================
//
// XCommandProcessor methods.
//
//=========================================================================

// virtual
uno::Any SAL_CALL HierarchyContent::execute(
        const star::ucb::Command& aCommand,
        sal_Int32 CommandId,
        const uno::Reference< star::ucb::XCommandEnvironment >& Environment )
    throw( uno::Exception,
           star::ucb::CommandAbortedException,
           uno::RuntimeException )
{
    uno::Any aRet;

    if ( aCommand.Name.equalsAsciiL(
            RTL_CONSTASCII_STRINGPARAM( "getPropertyValues" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // getPropertyValues
        //////////////////////////////////////////////////////////////////

        uno::Sequence< beans::Property > Properties;
        if ( !( aCommand.Argument >>= Properties ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        aRet <<= getPropertyValues( Properties );
    }
    else if ( aCommand.Name.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "setPropertyValues" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // setPropertyValues
        //////////////////////////////////////////////////////////////////

        uno::Sequence< beans::PropertyValue > aProperties;
        if ( !( aCommand.Argument >>= aProperties ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        if ( !aProperties.getLength() )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "No properties!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        aRet <<= setPropertyValues( aProperties, Environment );
    }
    else if ( aCommand.Name.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "getPropertySetInfo" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // getPropertySetInfo
        //////////////////////////////////////////////////////////////////

        aRet <<= getPropertySetInfo( Environment );
    }
    else if ( aCommand.Name.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "getCommandInfo" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // getCommandInfo
        //////////////////////////////////////////////////////////////////

        aRet <<= getCommandInfo( Environment );
    }
    else if ( isFolder() && aCommand.Name.equalsAsciiL(
                                RTL_CONSTASCII_STRINGPARAM( "open" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // open command for a folder content
        //////////////////////////////////////////////////////////////////

        star::ucb::OpenCommandArgument2 aOpenCommand;
        if ( !( aCommand.Argument >>= aOpenCommand ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        uno::Reference< star::ucb::XDynamicResultSet > xSet
                = new DynamicResultSet( m_xSMgr, this, aOpenCommand );
        aRet <<= xSet;
    }
    else if ( ( m_eKind != ROOT ) && !isReadOnly() &&
              aCommand.Name.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "insert" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // insert
        //  ( Not available at root folder )
        //////////////////////////////////////////////////////////////////

        star::ucb::InsertCommandArgument aArg;
        if ( !( aCommand.Argument >>= aArg ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        sal_Int32 nNameClash = aArg.ReplaceExisting
                             ? star::ucb::NameClash::OVERWRITE
                             : star::ucb::NameClash::ERROR;
        insert( nNameClash, Environment );
    }
    else if ( ( m_eKind != ROOT ) && !isReadOnly() &&
              aCommand.Name.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "delete" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // delete
        //  ( Not available at root folder )
        //////////////////////////////////////////////////////////////////

        sal_Bool bDeletePhysical = sal_False;
        aCommand.Argument >>= bDeletePhysical;
        destroy( bDeletePhysical, Environment );

        // Remove own and all children's persistent data.
        if ( !removeData() )
        {
            uno::Any aProps
                = uno::makeAny(
                         beans::PropertyValue(
                             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                               "Uri")),
                             -1,
                             uno::makeAny(m_xIdentifier->
                                              getContentIdentifier()),
                             beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                star::ucb::IOErrorCode_CANT_WRITE,
                uno::Sequence< uno::Any >(&aProps, 1),
                Environment,
                rtl::OUString::createFromAscii(
                    "Cannot remove persistent data!" ),
                this );
            // Unreachable
        }

        // Remove own and all children's Additional Core Properties.
        removeAdditionalPropertySet( sal_True );
    }
    else if ( isFolder() && !isReadOnly() &&
              aCommand.Name.equalsAsciiL(
                                RTL_CONSTASCII_STRINGPARAM( "transfer" ) ) )
    {
        //////////////////////////////////////////////////////////////////
        // transfer
        //  ( Not available at link objects )
        //////////////////////////////////////////////////////////////////

        star::ucb::TransferInfo aInfo;
        if ( !( aCommand.Argument >>= aInfo ) )
        {
            OSL_ENSURE( sal_False, "Wrong argument type!" );
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                        "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        transfer( aInfo, Environment );
    }
    else
    {
        //////////////////////////////////////////////////////////////////
        // Unsupported command
        //////////////////////////////////////////////////////////////////

        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::UnsupportedCommandException(
                                rtl::OUString(),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
            Environment );
        // Unreachable
    }

    return aRet;
}

//=========================================================================
// virtual
void SAL_CALL HierarchyContent::abort( sal_Int32 CommandId )
    throw( uno::RuntimeException )
{
    // @@@ Generally, no action takes much time...
}

//=========================================================================
//
// XContentCreator methods.
//
//=========================================================================

// virtual
uno::Sequence< star::ucb::ContentInfo > SAL_CALL
HierarchyContent::queryCreatableContentsInfo()
    throw( uno::RuntimeException )
{
    if ( isFolder() )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        uno::Sequence< star::ucb::ContentInfo > aSeq( 2 );

        // Folder.
        aSeq.getArray()[ 0 ].Type
            = rtl::OUString::createFromAscii( HIERARCHY_FOLDER_CONTENT_TYPE );
        aSeq.getArray()[ 0 ].Attributes
            = star::ucb::ContentInfoAttribute::KIND_FOLDER;

        uno::Sequence< beans::Property > aFolderProps( 1 );
        aFolderProps.getArray()[ 0 ] = beans::Property(
                    rtl::OUString::createFromAscii( "Title" ),
                    -1,
                    getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                    beans::PropertyAttribute::BOUND );
        aSeq.getArray()[ 0 ].Properties = aFolderProps;

        // Link.
        aSeq.getArray()[ 1 ].Type
            = rtl::OUString::createFromAscii( HIERARCHY_LINK_CONTENT_TYPE );
        aSeq.getArray()[ 1 ].Attributes
            = star::ucb::ContentInfoAttribute::KIND_LINK;

        uno::Sequence< beans::Property > aLinkProps( 2 );
        aLinkProps.getArray()[ 0 ] = beans::Property(
                    rtl::OUString::createFromAscii( "Title" ),
                    -1,
                    getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                    beans::PropertyAttribute::BOUND );
        aLinkProps.getArray()[ 1 ] = beans::Property(
                    rtl::OUString::createFromAscii( "TargetURL" ),
                    -1,
                    getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                    beans::PropertyAttribute::BOUND );
        aSeq.getArray()[ 1 ].Properties = aLinkProps;

        return aSeq;
    }
    else
    {
        OSL_ENSURE( sal_False,
                    "queryCreatableContentsInfo called on non-folder object!" );

        return uno::Sequence< star::ucb::ContentInfo >( 0 );
    }
}

//=========================================================================
// virtual
uno::Reference< star::ucb::XContent > SAL_CALL
HierarchyContent::createNewContent( const star::ucb::ContentInfo& Info )
    throw( uno::RuntimeException )
{
    if ( isFolder() )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        if ( !Info.Type.getLength() )
            return uno::Reference< star::ucb::XContent >();

        sal_Bool bCreateFolder =
            Info.Type.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) );

        if ( !bCreateFolder &&
             !Info.Type.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( HIERARCHY_LINK_CONTENT_TYPE ) ) )
            return uno::Reference< star::ucb::XContent >();

        rtl::OUString aURL = m_xIdentifier->getContentIdentifier();

        OSL_ENSURE( aURL.getLength() > 0,
                    "HierarchyContent::createNewContent - empty identifier!" );

        if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
            aURL += rtl::OUString::createFromAscii( "/" );

        if ( bCreateFolder )
            aURL += rtl::OUString::createFromAscii( "New_Folder" );
        else
            aURL += rtl::OUString::createFromAscii( "New_Link" );

        uno::Reference< star::ucb::XContentIdentifier > xId
            = new ::ucb::ContentIdentifier( m_xSMgr, aURL );

        return create( m_xSMgr, m_pProvider, xId, Info );
    }
    else
    {
        OSL_ENSURE( sal_False,
                    "createNewContent called on non-folder object!" );
        return uno::Reference< star::ucb::XContent >();
    }
}

//=========================================================================
// virtual
rtl::OUString HierarchyContent::getParentURL()
{
    HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
    return aUri.getParentUri();
}

//=========================================================================
//static
sal_Bool HierarchyContent::hasData(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier )
{
    rtl::OUString aURL = Identifier->getContentIdentifier();

    // Am I a root folder?
    HierarchyUri aUri( aURL );
    if ( aUri.isRootFolder() )
    {
        // hasData must always return 'true' for root folder
        // even if no persistent data exist!!!
        return sal_True;
    }

    return HierarchyEntry( rxSMgr, pProvider, aURL ).hasData();
}

//=========================================================================
//static
sal_Bool HierarchyContent::loadData(
            const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
            HierarchyContentProvider* pProvider,
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier,
            HierarchyContentProperties& rProps )
{
    rtl::OUString aURL = Identifier->getContentIdentifier();

    // Am I a root folder?
    HierarchyUri aUri( aURL );
    if ( aUri.isRootFolder() )
    {
        rProps = HierarchyContentProperties( HierarchyEntryData::FOLDER );
    }
    else
    {
        HierarchyEntry aEntry( rxSMgr, pProvider, aURL );
        HierarchyEntryData aData;
        if ( !aEntry.getData( aData ) )
            return sal_False;

        rProps = HierarchyContentProperties( aData );
    }
    return sal_True;
}

//=========================================================================
sal_Bool HierarchyContent::storeData()
{
    HierarchyEntry aEntry(
            m_xSMgr, m_pProvider, m_xIdentifier->getContentIdentifier() );
    return aEntry.setData( m_aProps.getHierarchyEntryData(), sal_True );
}

//=========================================================================
sal_Bool HierarchyContent::renameData(
            const uno::Reference< star::ucb::XContentIdentifier >& xOldId,
            const uno::Reference< star::ucb::XContentIdentifier >& xNewId )
{
    HierarchyEntry aEntry(
            m_xSMgr, m_pProvider, xOldId->getContentIdentifier() );
    return aEntry.move( xNewId->getContentIdentifier(),
                        m_aProps.getHierarchyEntryData() );
}

//=========================================================================
sal_Bool HierarchyContent::removeData()
{
    HierarchyEntry aEntry(
        m_xSMgr, m_pProvider, m_xIdentifier->getContentIdentifier() );
    return aEntry.remove();
}

//=========================================================================
void HierarchyContent::setKind(
            const uno::Reference< star::ucb::XContentIdentifier >& Identifier )
{
    if ( m_aProps.getIsFolder() )
    {
        // Am I a root folder?
        HierarchyUri aUri( Identifier->getContentIdentifier() );
        if ( aUri.isRootFolder() )
            m_eKind = ROOT;
        else
            m_eKind = FOLDER;
    }
    else
        m_eKind = LINK;
}

//=========================================================================
bool HierarchyContent::isReadOnly()
{
    if ( !m_bCheckedReadOnly )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );
        if ( !m_bCheckedReadOnly )
        {
            m_bCheckedReadOnly = true;
            m_bIsReadOnly      = true;

            HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
            uno::Reference< lang::XMultiServiceFactory > xConfigProv
                = m_pProvider->getConfigProvider( aUri.getService() );
            if ( xConfigProv.is() )
            {
                uno::Sequence< rtl::OUString > aNames
                    = xConfigProv->getAvailableServiceNames();
                sal_Int32 nCount = aNames.getLength();
                for ( sal_Int32 n = 0; n < nCount; ++n )
                {
                    if ( aNames[ n ].equalsAsciiL(
                            RTL_CONSTASCII_STRINGPARAM(
                                "com.sun.star.ucb.HierarchyDataReadWriteAccess"
                             ) ) )
                    {
                        m_bIsReadOnly = false;
                        break;
                    }
                }
            }
        }
    }

    return m_bIsReadOnly;
}

//=========================================================================
uno::Reference< star::ucb::XContentIdentifier >
HierarchyContent::makeNewIdentifier( const rtl::OUString& rTitle )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    // Assemble new content identifier...
    HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
    rtl::OUString aNewURL = aUri.getParentUri();
    aNewURL += rtl::OUString::createFromAscii( "/" );
    aNewURL += HierarchyUri::encodeSegment( rTitle );

    return uno::Reference< star::ucb::XContentIdentifier >(
                           new ::ucb::ContentIdentifier( m_xSMgr, aNewURL ) );
}

//=========================================================================
void HierarchyContent::queryChildren( HierarchyContentRefList& rChildren )
{
    if ( ( m_eKind != FOLDER ) && ( m_eKind != ROOT ) )
        return;

    // Obtain a list with a snapshot of all currently instanciated contents
    // from provider and extract the contents which are direct children
    // of this content.

    ::ucb::ContentRefList aAllContents;
    m_xProvider->queryExistingContents( aAllContents );

    rtl::OUString aURL = m_xIdentifier->getContentIdentifier();
    sal_Int32 nPos = aURL.lastIndexOf( '/' );

    if ( nPos != ( aURL.getLength() - 1 ) )
    {
        // No trailing slash found. Append.
        aURL += rtl::OUString::createFromAscii( "/" );
    }

    sal_Int32 nLen = aURL.getLength();

    ::ucb::ContentRefList::const_iterator it  = aAllContents.begin();
    ::ucb::ContentRefList::const_iterator end = aAllContents.end();

    while ( it != end )
    {
        ::ucb::ContentImplHelperRef xChild = (*it);
        rtl::OUString aChildURL
            = xChild->getIdentifier()->getContentIdentifier();

        // Is aURL a prefix of aChildURL?
        if ( ( aChildURL.getLength() > nLen ) &&
             ( aChildURL.compareTo( aURL, nLen ) == 0 ) )
        {
            sal_Int32 nPos = nLen;
            nPos = aChildURL.indexOf( '/', nPos );

            if ( ( nPos == -1 ) ||
                 ( nPos == ( aChildURL.getLength() - 1 ) ) )
            {
                // No further slashes/ only a final slash. It's a child!
                rChildren.push_back(
                    HierarchyContentRef(
                        static_cast< HierarchyContent * >(
                            xChild.getBodyPtr() ) ) );
            }
        }
        ++it;
    }
}

//=========================================================================
sal_Bool HierarchyContent::exchangeIdentity(
            const uno::Reference< star::ucb::XContentIdentifier >& xNewId )
{
    if ( !xNewId.is() )
        return sal_False;

    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    uno::Reference< star::ucb::XContent > xThis = this;

    // Already persistent?
    if ( m_eState != PERSISTENT )
    {
        OSL_ENSURE( sal_False,
                    "HierarchyContent::exchangeIdentity - Not persistent!" );
        return sal_False;
    }

    // Am I the root folder?
    if ( m_eKind == ROOT )
    {
        OSL_ENSURE( sal_False, "HierarchyContent::exchangeIdentity - "
                               "Not supported by root folder!" );
        return sal_False;
    }

    // Exchange own identitity.

    // Fail, if a content with given id already exists.
    if ( !hasData( xNewId ) )
    {
        rtl::OUString aOldURL = m_xIdentifier->getContentIdentifier();

        aGuard.clear();
        if ( exchange( xNewId ) )
        {
            if ( m_eKind == FOLDER )
            {
                // Process instanciated children...

                HierarchyContentRefList aChildren;
                queryChildren( aChildren );

                HierarchyContentRefList::const_iterator it  = aChildren.begin();
                HierarchyContentRefList::const_iterator end = aChildren.end();

                while ( it != end )
                {
                    HierarchyContentRef xChild = (*it);

                    // Create new content identifier for the child...
                    uno::Reference< star::ucb::XContentIdentifier > xOldChildId
                                                    = xChild->getIdentifier();
                    rtl::OUString aOldChildURL
                        = xOldChildId->getContentIdentifier();
                    rtl::OUString aNewChildURL
                        = aOldChildURL.replaceAt(
                                        0,
                                        aOldURL.getLength(),
                                        xNewId->getContentIdentifier() );
                    uno::Reference< star::ucb::XContentIdentifier > xNewChildId
                        = new ::ucb::ContentIdentifier( m_xSMgr, aNewChildURL );

                    if ( !xChild->exchangeIdentity( xNewChildId ) )
                        return sal_False;

                    ++it;
                }
            }
            return sal_True;
        }
    }

    OSL_ENSURE( sal_False,
                "HierarchyContent::exchangeIdentity - "
                "Panic! Cannot exchange identity!" );
    return sal_False;
}

//=========================================================================
// static
uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues(
                const uno::Reference< lang::XMultiServiceFactory >& rSMgr,
                const uno::Sequence< beans::Property >& rProperties,
                const HierarchyContentProperties& rData,
                HierarchyContentProvider* pProvider,
                const rtl::OUString& rContentId )
{
    // Note: Empty sequence means "get values of all supported properties".

    rtl::Reference< ::ucb::PropertyValueSet > xRow
        = new ::ucb::PropertyValueSet( rSMgr );

    sal_Int32 nCount = rProperties.getLength();
    if ( nCount )
    {
        uno::Reference< beans::XPropertySet > xAdditionalPropSet;
        sal_Bool bTriedToGetAdditonalPropSet = sal_False;

        const beans::Property* pProps = rProperties.getConstArray();
        for ( sal_Int32 n = 0; n < nCount; ++n )
        {
            const beans::Property& rProp = pProps[ n ];

            // Process Core properties.

            if ( rProp.Name.equalsAsciiL(
                        RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) )
            {
                xRow->appendString ( rProp, rData.getContentType() );
            }
            else if ( rProp.Name.equalsAsciiL(
                        RTL_CONSTASCII_STRINGPARAM(  "Title" ) ) )
            {
                xRow->appendString ( rProp, rData.getTitle() );
            }
            else if ( rProp.Name.equalsAsciiL(
                        RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) )
            {
                xRow->appendBoolean( rProp, rData.getIsDocument() );
            }
            else if ( rProp.Name.equalsAsciiL(
                        RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) )
            {
                xRow->appendBoolean( rProp, rData.getIsFolder() );
            }
            else if ( rProp.Name.equalsAsciiL(
                        RTL_CONSTASCII_STRINGPARAM( "TargetURL" ) ) )
            {
                // TargetURL is only supported by links.

                if ( rData.getIsDocument() )
                    xRow->appendString( rProp, rData.getTargetURL() );
                else
                    xRow->appendVoid( rProp );
            }
            else
            {
                // Not a Core Property! Maybe it's an Additional Core Property?!

                if ( !bTriedToGetAdditonalPropSet && !xAdditionalPropSet.is() )
                {
                    xAdditionalPropSet
                        = uno::Reference< beans::XPropertySet >(
                            pProvider->getAdditionalPropertySet( rContentId,
                                                                 sal_False ),
                            uno::UNO_QUERY );
                    bTriedToGetAdditonalPropSet = sal_True;
                }

                if ( xAdditionalPropSet.is() )
                {
                    if ( !xRow->appendPropertySetValue(
                                                xAdditionalPropSet,
                                                rProp ) )
                    {
                        // Append empty entry.
                        xRow->appendVoid( rProp );
                    }
                }
                else
                {
                    // Append empty entry.
                    xRow->appendVoid( rProp );
                }
            }
        }
    }
    else
    {
        // Append all Core Properties.
        xRow->appendString (
            beans::Property( rtl::OUString::createFromAscii( "ContentType" ),
                      -1,
                      getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getContentType() );
        xRow->appendString (
            beans::Property( rtl::OUString::createFromAscii( "Title" ),
                      -1,
                      getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                          // @@@ Might actually be read-only!
                      beans::PropertyAttribute::BOUND ),
            rData.getTitle() );
        xRow->appendBoolean(
            beans::Property( rtl::OUString::createFromAscii( "IsDocument" ),
                      -1,
                      getCppuBooleanType(),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getIsDocument() );
        xRow->appendBoolean(
            beans::Property( rtl::OUString::createFromAscii( "IsFolder" ),
                      -1,
                      getCppuBooleanType(),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getIsFolder() );

        if ( rData.getIsDocument() )
            xRow->appendString(
                beans::Property( rtl::OUString::createFromAscii( "TargetURL" ),
                          -1,
                          getCppuType(
                            static_cast< const rtl::OUString * >( 0 ) ),
                          // @@@ Might actually be read-only!
                          beans::PropertyAttribute::BOUND ),
                rData.getTargetURL() );

        // Append all Additional Core Properties.

        uno::Reference< beans::XPropertySet > xSet(
            pProvider->getAdditionalPropertySet( rContentId, sal_False ),
            uno::UNO_QUERY );
        xRow->appendPropertySet( xSet );
    }

    return uno::Reference< sdbc::XRow >( xRow.get() );
}

//=========================================================================
uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues(
                        const uno::Sequence< beans::Property >& rProperties )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );
    return getPropertyValues( m_xSMgr,
                              rProperties,
                              m_aProps,
                              m_pProvider,
                              m_xIdentifier->getContentIdentifier() );
}

//=========================================================================
uno::Sequence< uno::Any > HierarchyContent::setPropertyValues(
        const uno::Sequence< beans::PropertyValue >& rValues,
        const uno::Reference< star::ucb::XCommandEnvironment > & xEnv )
    throw( ::com::sun::star::uno::Exception )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    uno::Sequence< uno::Any > aRet( rValues.getLength() );
    uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
    sal_Int32 nChanged = 0;

    beans::PropertyChangeEvent aEvent;
    aEvent.Source         = static_cast< cppu::OWeakObject * >( this );
    aEvent.Further        = sal_False;
//  aEvent.PropertyName   =
    aEvent.PropertyHandle = -1;
//  aEvent.OldValue       =
//  aEvent.NewValue       =

    const beans::PropertyValue* pValues = rValues.getConstArray();
    sal_Int32 nCount = rValues.getLength();

    uno::Reference< star::ucb::XPersistentPropertySet > xAdditionalPropSet;
    sal_Bool bTriedToGetAdditonalPropSet = sal_False;

    sal_Bool bExchange = sal_False;
    rtl::OUString aOldTitle;
    rtl::OUString aOldName;
    sal_Int32 nTitlePos = -1;

    for ( sal_Int32 n = 0; n < nCount; ++n )
    {
        const beans::PropertyValue& rValue = pValues[ n ];

        if ( rValue.Name.equalsAsciiL(
                    RTL_CONSTASCII_STRINGPARAM(  "ContentType" ) ) )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString::createFromAscii(
                                "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name.equalsAsciiL(
                    RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString::createFromAscii(
                                "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name.equalsAsciiL(
                    RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString::createFromAscii(
                                "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name.equalsAsciiL(
                    RTL_CONSTASCII_STRINGPARAM( "Title" ) ) )
        {
            if ( isReadOnly() )
            {
                aRet[ n ] <<= lang::IllegalAccessException(
                                rtl::OUString::createFromAscii(
                                    "Property is read-only!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else
            {
                rtl::OUString aNewValue;
                if ( rValue.Value >>= aNewValue )
                {
                    // No empty titles!
                    if ( aNewValue.getLength() > 0 )
                    {
                        if ( aNewValue != m_aProps.getTitle() )
                        {
                            // modified title -> modified URL -> exchange !
                            if ( m_eState == PERSISTENT )
                                bExchange = sal_True;

                            aOldTitle = m_aProps.getTitle();
                            aOldName  = m_aProps.getName();

                            m_aProps.setTitle( aNewValue );
                            m_aProps.setName(
                                    HierarchyUri::encodeSegment( aNewValue ) );

                            // property change event will be set later...

                            // remember position within sequence of values
                            // (for error handling).
                            nTitlePos = n;
                        }
                    }
                    else
                    {
                        aRet[ n ] <<= lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                            "Empty title not allowed!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 );
                    }
                }
                else
                {
                    aRet[ n ] <<= beans::IllegalTypeException(
                                rtl::OUString::createFromAscii(
                                        "Property value has wrong type!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
                }
            }
        }
        else if ( rValue.Name.equalsAsciiL(
                    RTL_CONSTASCII_STRINGPARAM( "TargetURL" ) ) )
        {
            if ( isReadOnly() )
            {
                aRet[ n ] <<= lang::IllegalAccessException(
                                rtl::OUString::createFromAscii(
                                    "Property is read-only!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else
            {
                // TargetURL is only supported by links.

                if ( m_eKind == LINK )
                {
                    rtl::OUString aNewValue;
                    if ( rValue.Value >>= aNewValue )
                    {
                        // No empty target URL's!
                        if ( aNewValue.getLength() > 0 )
                        {
                            if ( aNewValue != m_aProps.getTargetURL() )
                            {
                                aEvent.PropertyName = rValue.Name;
                                aEvent.OldValue
                                    = uno::makeAny( m_aProps.getTargetURL() );
                                aEvent.NewValue
                                    = uno::makeAny( aNewValue );

                                aChanges.getArray()[ nChanged ] = aEvent;

                                m_aProps.setTargetURL( aNewValue );
                                nChanged++;
                            }
                        }
                        else
                        {
                            aRet[ n ] <<= lang::IllegalArgumentException(
                                    rtl::OUString::createFromAscii(
                                            "Empty target URL not allowed!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 );
                        }
                    }
                    else
                    {
                        aRet[ n ] <<= beans::IllegalTypeException(
                                rtl::OUString::createFromAscii(
                                        "Property value has wrong type!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
                    }
                }
                else
                {
                    aRet[ n ] <<= beans::UnknownPropertyException(
                                rtl::OUString::createFromAscii(
                                    "TargetURL only supported by links!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
                }
            }
        }
        else
        {
            // Not a Core Property! Maybe it's an Additional Core Property?!

            if ( !bTriedToGetAdditonalPropSet && !xAdditionalPropSet.is() )
            {
                xAdditionalPropSet = getAdditionalPropertySet( sal_False );
                bTriedToGetAdditonalPropSet = sal_True;
            }

            if ( xAdditionalPropSet.is() )
            {
                try
                {
                    uno::Any aOldValue = xAdditionalPropSet->getPropertyValue(
                                                                rValue.Name );
                    if ( aOldValue != rValue.Value )
                    {
                        xAdditionalPropSet->setPropertyValue(
                                                rValue.Name, rValue.Value );

                        aEvent.PropertyName = rValue.Name;
                        aEvent.OldValue     = aOldValue;
                        aEvent.NewValue     = rValue.Value;

                        aChanges.getArray()[ nChanged ] = aEvent;
                        nChanged++;
                    }
                }
                catch ( beans::UnknownPropertyException const & e )
                {
                    aRet[ n ] <<= e;
                }
                catch ( lang::WrappedTargetException const & e )
                {
                    aRet[ n ] <<= e;
                }
                catch ( beans::PropertyVetoException const & e )
                {
                    aRet[ n ] <<= e;
                }
                catch ( lang::IllegalArgumentException const & e )
                {
                    aRet[ n ] <<= e;
                }
            }
            else
            {
                aRet[ n ] <<= uno::Exception(
                                rtl::OUString::createFromAscii(
                                    "No property set for storing the value!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
        }
    }

    if ( bExchange )
    {
        uno::Reference< star::ucb::XContentIdentifier > xOldId
            = m_xIdentifier;
        uno::Reference< star::ucb::XContentIdentifier > xNewId
            = makeNewIdentifier( m_aProps.getTitle() );

        aGuard.clear();
        if ( exchangeIdentity( xNewId ) )
        {
            // Adapt persistent data.
            renameData( xOldId, xNewId );

            // Adapt Additional Core Properties.
            renameAdditionalPropertySet( xOldId->getContentIdentifier(),
                                         xNewId->getContentIdentifier(),
                                         sal_True );
        }
        else
        {
            // Roll-back.
            m_aProps.setTitle( aOldTitle );
            m_aProps.setName ( aOldName );

            aOldTitle = aOldName = rtl::OUString();

            // Set error .
            aRet[ nTitlePos ] <<= uno::Exception(
                    rtl::OUString::createFromAscii( "Exchange failed!" ),
                    static_cast< cppu::OWeakObject * >( this ) );
        }
    }

    if ( aOldTitle.getLength() )
    {
        aEvent.PropertyName = rtl::OUString::createFromAscii( "Title" );
        aEvent.OldValue     = uno::makeAny( aOldTitle );
        aEvent.NewValue     = uno::makeAny( m_aProps.getTitle() );

        aChanges.getArray()[ nChanged ] = aEvent;
        nChanged++;
    }

    if ( nChanged > 0 )
    {
        // Save changes, if content was already made persistent.
        if ( !bExchange && ( m_eState == PERSISTENT ) )
        {
            if ( !storeData() )
            {
                uno::Any aProps
                    = uno::makeAny(
                             beans::PropertyValue(
                                 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                     "Uri")),
                                 -1,
                                 uno::makeAny(m_xIdentifier->
                                                  getContentIdentifier()),
                                 beans::PropertyState_DIRECT_VALUE));
                ucbhelper::cancelCommandExecution(
                    star::ucb::IOErrorCode_CANT_WRITE,
                    uno::Sequence< uno::Any >(&aProps, 1),
                    xEnv,
                    rtl::OUString::createFromAscii(
                        "Cannot store persistent data!" ),
                    this );
                // Unreachable
            }
        }

        aChanges.realloc( nChanged );

        aGuard.clear();
        notifyPropertiesChange( aChanges );
    }

    return aRet;
}

//=========================================================================
void HierarchyContent::insert( sal_Int32 nNameClashResolve,
                               const uno::Reference<
                                    star::ucb::XCommandEnvironment > & xEnv )
    throw( uno::Exception )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    // Am I the root folder?
    if ( m_eKind == ROOT )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::UnsupportedCommandException(
                                rtl::OUString::createFromAscii(
                                    "Not supported by root folder!" ),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    // Check, if all required properties were set.
    if ( m_aProps.getTitle().getLength() == 0 )
    {
        uno::Sequence< rtl::OUString > aProps( 1 );
        aProps[ 0 ] = rtl::OUString::createFromAscii( "Title" );
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::MissingPropertiesException(
                                rtl::OUString(),
                                static_cast< cppu::OWeakObject * >( this ),
                                aProps ) ),
            xEnv );
        // Unreachable
    }

    // Assemble new content identifier...

    uno::Reference< star::ucb::XContentIdentifier > xId
        = makeNewIdentifier( m_aProps.getTitle() );

    // Handle possible name clash...

    switch ( nNameClashResolve )
    {
        // fail.
        case star::ucb::NameClash::ERROR:
            if ( hasData( xId ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny(
                        star::ucb::NameClashException(
                            rtl::OUString(),
                            static_cast< cppu::OWeakObject * >( this ),
                            task::InteractionClassification_ERROR,
                            m_aProps.getTitle() ) ),
                    xEnv );
                // Unreachable
            }
            break;

        // replace existing object.
        case star::ucb::NameClash::OVERWRITE:
            break;

        // "invent" a new valid title.
        case star::ucb::NameClash::RENAME:
            if ( hasData( xId ) )
            {
                sal_Int32 nTry = 0;

                do
                {
                    rtl::OUString aNewId = xId->getContentIdentifier();
                    aNewId += rtl::OUString::createFromAscii( "_" );
                    aNewId += rtl::OUString::valueOf( ++nTry );
                    xId = new ::ucb::ContentIdentifier( m_xSMgr, aNewId );
                }
                while ( hasData( xId ) && ( nTry < 1000 ) );

                if ( nTry == 1000 )
                {
                    ucbhelper::cancelCommandExecution(
                        uno::makeAny(
                            star::ucb::UnsupportedNameClashException(
                                rtl::OUString::createFromAscii(
                                    "Unable to resolve name clash!" ),
                                static_cast< cppu::OWeakObject * >( this ),
                                nNameClashResolve ) ),
                    xEnv );
                // Unreachable
                }
                else
                {
                    rtl::OUString aNewTitle( m_aProps.getTitle() );
                    aNewTitle += rtl::OUString::createFromAscii( "_" );
                    aNewTitle += rtl::OUString::valueOf( nTry );
                    m_aProps.setTitle( aNewTitle );
                }
            }
            break;

        case star::ucb::NameClash::KEEP: // deprecated
        case star::ucb::NameClash::ASK:
        default:
            if ( hasData( xId ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny(
                        star::ucb::UnsupportedNameClashException(
                                rtl::OUString(),
                                static_cast< cppu::OWeakObject * >( this ),
                                nNameClashResolve ) ),
                    xEnv );
                // Unreachable
            }
            break;
    }

    // Identifier changed?
    sal_Bool bNewId = ( xId->getContentIdentifier()
                            != m_xIdentifier->getContentIdentifier() );
    m_xIdentifier = xId;

    if ( !storeData() )
    {
        uno::Any aProps
            = uno::makeAny(beans::PropertyValue(
                                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                                    "Uri")),
                                  -1,
                                  uno::makeAny(m_xIdentifier->
                                                   getContentIdentifier()),
                                  beans::PropertyState_DIRECT_VALUE));
        ucbhelper::cancelCommandExecution(
            star::ucb::IOErrorCode_CANT_WRITE,
            uno::Sequence< uno::Any >(&aProps, 1),
            xEnv,
            rtl::OUString::createFromAscii( "Cannot store persistent data!" ),
            this );
        // Unreachable
    }

    m_eState = PERSISTENT;

    if ( bNewId )
    {
        aGuard.clear();
        inserted();
    }
}

//=========================================================================
void HierarchyContent::destroy( sal_Bool bDeletePhysical,
                                const uno::Reference<
                                    star::ucb::XCommandEnvironment > & xEnv )
    throw( uno::Exception )
{
    // @@@ take care about bDeletePhysical -> trashcan support

    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    uno::Reference< star::ucb::XContent > xThis = this;

    // Persistent?
    if ( m_eState != PERSISTENT )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::UnsupportedCommandException(
                                rtl::OUString::createFromAscii(
                                    "Not persistent!" ),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    // Am I the root folder?
    if ( m_eKind == ROOT )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::UnsupportedCommandException(
                                rtl::OUString::createFromAscii(
                                    "Not supported by root folder!" ),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    m_eState = DEAD;

    aGuard.clear();
    deleted();

    if ( m_eKind == FOLDER )
    {
        // Process instanciated children...

        HierarchyContentRefList aChildren;
        queryChildren( aChildren );

        HierarchyContentRefList::const_iterator it  = aChildren.begin();
        HierarchyContentRefList::const_iterator end = aChildren.end();

        while ( it != end )
        {
            (*it)->destroy( bDeletePhysical, xEnv );
            ++it;
        }
    }
}

//=========================================================================
void HierarchyContent::transfer(
            const star::ucb::TransferInfo& rInfo,
            const uno::Reference< star::ucb::XCommandEnvironment > & xEnv )
    throw( uno::Exception )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    // Persistent?
    if ( m_eState != PERSISTENT )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::UnsupportedCommandException(
                                rtl::OUString::createFromAscii(
                                    "Not persistent!" ),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    // Is source a hierarchy content?
    if ( ( rInfo.SourceURL.getLength() < HIERARCHY_URL_SCHEME_LENGTH + 2 ) ||
         ( rInfo.SourceURL.compareToAscii( HIERARCHY_URL_SCHEME ":/",
                                           HIERARCHY_URL_SCHEME_LENGTH + 2 )
            != 0 ) )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( star::ucb::InteractiveBadTransferURLException(
                            rtl::OUString(),
                            static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    // Is source not a parent of me / not me?
    rtl::OUString aId = m_xIdentifier->getContentIdentifier();
    sal_Int32 nPos = aId.lastIndexOf( '/' );
    if ( nPos != ( aId.getLength() - 1 ) )
    {
        // No trailing slash found. Append.
        aId += rtl::OUString::createFromAscii( "/" );
    }

    if ( rInfo.SourceURL.getLength() <= aId.getLength() )
    {
        if ( aId.compareTo(
                rInfo.SourceURL, rInfo.SourceURL.getLength() ) == 0 )
        {
            uno::Any aProps
                = uno::makeAny(beans::PropertyValue(
                                      rtl::OUString(
                                          RTL_CONSTASCII_USTRINGPARAM("Uri")),
                                      -1,
                                      uno::makeAny(rInfo.SourceURL),
                                      beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                star::ucb::IOErrorCode_RECURSIVE,
                uno::Sequence< uno::Any >(&aProps, 1),
                xEnv,
                rtl::OUString::createFromAscii(
                    "Target is equal to or is a child of source!" ),
                this );
            // Unreachable
        }
    }

    //////////////////////////////////////////////////////////////////////
    // 0) Obtain content object for source.
    //////////////////////////////////////////////////////////////////////

    uno::Reference< star::ucb::XContentIdentifier > xId
        = new ::ucb::ContentIdentifier( m_xSMgr, rInfo.SourceURL );

    // Note: The static cast is okay here, because its sure that
    //       m_xProvider is always the HierarchyContentProvider.
    rtl::Reference< HierarchyContent > xSource;

    try
    {
        xSource = static_cast< HierarchyContent * >(
                        m_xProvider->queryContent( xId ).get() );
    }
    catch ( star::ucb::IllegalIdentifierException const & )
    {
        // queryContent
    }

    if ( !xSource.is() )
    {
        uno::Any aProps
            = uno::makeAny(beans::PropertyValue(
                                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                      "Uri")),
                                  -1,
                                  uno::makeAny(xId->getContentIdentifier()),
                                  beans::PropertyState_DIRECT_VALUE));
        ucbhelper::cancelCommandExecution(
            star::ucb::IOErrorCode_CANT_READ,
            uno::Sequence< uno::Any >(&aProps, 1),
            xEnv,
            rtl::OUString::createFromAscii(
                "Cannot instanciate source object!" ),
            this );
        // Unreachable
    }

    //////////////////////////////////////////////////////////////////////
    // 1) Create new child content.
    //////////////////////////////////////////////////////////////////////

    rtl::OUString aType = xSource->isFolder()
        ? rtl::OUString::createFromAscii( HIERARCHY_FOLDER_CONTENT_TYPE )
        : rtl::OUString::createFromAscii( HIERARCHY_LINK_CONTENT_TYPE );
    star::ucb::ContentInfo aInfo;
    aInfo.Type = aType;
    aInfo.Attributes = 0;

    // Note: The static cast is okay here, because its sure that
    //       createNewContent always creates a HierarchyContent.
    rtl::Reference< HierarchyContent > xTarget
        = static_cast< HierarchyContent * >(
            createNewContent( aInfo ).get() );
    if ( !xTarget.is() )
    {
        uno::Any aProps
            = uno::makeAny(beans::PropertyValue(
                                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                      "Folder")),
                                  -1,
                                  uno::makeAny(aId),
                                  beans::PropertyState_DIRECT_VALUE));
        ucbhelper::cancelCommandExecution(
            star::ucb::IOErrorCode_CANT_CREATE,
            uno::Sequence< uno::Any >(&aProps, 1),
            xEnv,
            rtl::OUString::createFromAscii(
                "XContentCreator::createNewContent failed!" ),
            this );
        // Unreachable
    }

    //////////////////////////////////////////////////////////////////////
    // 2) Copy data from source content to child content.
    //////////////////////////////////////////////////////////////////////

    uno::Sequence< beans::Property > aProps
                    = xSource->getPropertySetInfo( xEnv )->getProperties();
    sal_Int32 nCount = aProps.getLength();

    if ( nCount )
    {
        sal_Bool bHadTitle = ( rInfo.NewTitle.getLength() == 0 );

        // Get all source values.
        uno::Reference< sdbc::XRow > xRow
            = xSource->getPropertyValues( aProps );

        uno::Sequence< beans::PropertyValue > aValues( nCount );
        beans::PropertyValue* pValues = aValues.getArray();

        const beans::Property* pProps = aProps.getConstArray();
        for ( sal_Int32 n = 0; n < nCount; ++n )
        {
            const beans::Property& rProp  = pProps[ n ];
            beans::PropertyValue&  rValue = pValues[ n ];

            rValue.Name   = rProp.Name;
            rValue.Handle = rProp.Handle;

            if ( !bHadTitle && rProp.Name.equalsAsciiL(
                                RTL_CONSTASCII_STRINGPARAM( "Title" ) ) )
            {
                // Set new title instead of original.
                bHadTitle = sal_True;
                rValue.Value <<= rInfo.NewTitle;
            }
            else
                rValue.Value = xRow->getObject(
                                n + 1,
                                uno::Reference< container::XNameAccess >() );

            rValue.State = beans::PropertyState_DIRECT_VALUE;

            if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE )
            {
                // Add Additional Core Property.
                try
                {
                    xTarget->addProperty( rProp.Name,
                                          rProp.Attributes,
                                          rValue.Value );
                }
                catch ( beans::PropertyExistException const & )
                {
                }
                catch ( beans::IllegalTypeException const & )
                {
                }
                catch ( lang::IllegalArgumentException const & )
                {
                }
            }
        }

        // Set target values.
        xTarget->setPropertyValues( aValues, xEnv );
    }

    //////////////////////////////////////////////////////////////////////
    // 3) Commit (insert) child.
    //////////////////////////////////////////////////////////////////////

    xTarget->insert( rInfo.NameClash, xEnv );

    //////////////////////////////////////////////////////////////////////
    // 4) Transfer (copy) children of source.
    //////////////////////////////////////////////////////////////////////

    if ( xSource->isFolder() )
    {
        HierarchyEntry aFolder(
            m_xSMgr, m_pProvider, xId->getContentIdentifier() );
        HierarchyEntry::iterator it;

        while ( aFolder.next( it ) )
        {
            const HierarchyEntryData& rResult = *it;

            rtl::OUString aChildId = xId->getContentIdentifier();
            if ( ( aChildId.lastIndexOf( '/' ) + 1 ) != aChildId.getLength() )
                aChildId += rtl::OUString::createFromAscii( "/" );

            aChildId += rResult.getName();

            star::ucb::TransferInfo aInfo;
            aInfo.MoveData  = sal_False;
            aInfo.NewTitle  = rtl::OUString();
            aInfo.SourceURL = aChildId;
            aInfo.NameClash = rInfo.NameClash;

            // Transfer child to target.
            xTarget->transfer( aInfo, xEnv );
        }
    }

    //////////////////////////////////////////////////////////////////////
    // 5) Destroy source ( when moving only ) .
    //////////////////////////////////////////////////////////////////////

    if ( rInfo.MoveData )
    {
        xSource->destroy( sal_True, xEnv );

        // Remove all persistent data of source and its children.
        if ( !xSource->removeData() )
        {
            uno::Any aProps
                = uno::makeAny(
                         beans::PropertyValue(
                             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
                                               "Uri")),
                             -1,
                             uno::makeAny(
                                 xSource->m_xIdentifier->
                                              getContentIdentifier()),
                             beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                star::ucb::IOErrorCode_CANT_WRITE,
                uno::Sequence< uno::Any >(&aProps, 1),
                xEnv,
                rtl::OUString::createFromAscii(
                    "Cannot remove persistent data of source object!" ),
                this );
            // Unreachable
        }

        // Remove own and all children's Additional Core Properties.
        xSource->removeAdditionalPropertySet( sal_True );
    }
}

