/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


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

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

#include "osl/diagnose.h"
#include "osl/doublecheckedlocking.h"
#include "rtl/ustrbuf.hxx"

#include "com/sun/star/beans/PropertyAttribute.hpp"
#include "com/sun/star/beans/PropertyValue.hpp"
#include "com/sun/star/beans/XPropertySet.hpp"
#include "com/sun/star/embed/ElementModes.hpp"
#include "com/sun/star/embed/XStorage.hpp"
#include "com/sun/star/embed/XTransactedObject.hpp"
#include "com/sun/star/io/XActiveDataSink.hpp"
#include "com/sun/star/io/XActiveDataStreamer.hpp"
#include "com/sun/star/lang/IllegalAccessException.hpp"
#include "com/sun/star/sdbc/XRow.hpp"
#include "com/sun/star/ucb/ContentAction.hpp"
#include "com/sun/star/ucb/ContentInfoAttribute.hpp"
#include "com/sun/star/ucb/InsertCommandArgument.hpp"
#include "com/sun/star/ucb/InteractiveBadTransferURLException.hpp"
#include "com/sun/star/ucb/MissingInputStreamException.hpp"
#include "com/sun/star/ucb/MissingPropertiesException.hpp"
#include "com/sun/star/ucb/NameClash.hpp"
#include "com/sun/star/ucb/NameClashException.hpp"
#include "com/sun/star/ucb/OpenCommandArgument2.hpp"
#include "com/sun/star/ucb/OpenMode.hpp"
#include "com/sun/star/ucb/TransferInfo.hpp"
#include "com/sun/star/ucb/UnsupportedCommandException.hpp"
#include "com/sun/star/ucb/UnsupportedDataSinkException.hpp"
#include "com/sun/star/ucb/UnsupportedNameClashException.hpp"
#include "com/sun/star/ucb/UnsupportedOpenModeException.hpp"
#include "com/sun/star/ucb/XCommandInfo.hpp"
#include "com/sun/star/ucb/XPersistentPropertySet.hpp"

#include "comphelper/processfactory.hxx"
#include "ucbhelper/cancelcommandexecution.hxx"
#include "ucbhelper/contentidentifier.hxx"
#include "ucbhelper/propertyvalueset.hxx"

#include "tdoc_content.hxx"
#include "tdoc_resultset.hxx"
#include "tdoc_passwordrequest.hxx"

#include "../inc/urihelper.hxx"

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

//=========================================================================
static ContentType lcl_getContentType( const rtl::OUString & rType )
{
    if ( rType == TDOC_ROOT_CONTENT_TYPE )
        return ROOT;
    else if ( rType == TDOC_DOCUMENT_CONTENT_TYPE )
        return DOCUMENT;
    else if ( rType == TDOC_FOLDER_CONTENT_TYPE )
        return FOLDER;
    else if ( rType == TDOC_STREAM_CONTENT_TYPE )
        return STREAM;
    else
    {
        OSL_FAIL( "Content::Content - unsupported content type string" );
        return STREAM;
    }
}

//=========================================================================
//=========================================================================
//
// Content Implementation.
//
//=========================================================================
//=========================================================================

// static ( "virtual" ctor )
Content* Content::create(
            const uno::Reference< uno::XComponentContext >& rxContext,
            ContentProvider* pProvider,
            const uno::Reference< ucb::XContentIdentifier >& Identifier )
{
    // Fail, if resource does not exist.
    ContentProperties aProps;
    if ( !Content::loadData( pProvider,
                             Uri( Identifier->getContentIdentifier() ),
                             aProps ) )
        return 0;

    return new Content( rxContext, pProvider, Identifier, aProps );
}

//=========================================================================
// static ( "virtual" ctor )
Content* Content::create(
            const uno::Reference< uno::XComponentContext >& rxContext,
            ContentProvider* pProvider,
            const uno::Reference< ucb::XContentIdentifier >& Identifier,
            const ucb::ContentInfo& Info )
{
    if ( Info.Type.isEmpty() )
        return 0;

    if ( Info.Type != TDOC_FOLDER_CONTENT_TYPE && Info.Type != TDOC_STREAM_CONTENT_TYPE )
    {
        OSL_FAIL( "Content::create - unsupported content type!" );
        return 0;
    }

    return new Content( rxContext, pProvider, Identifier, Info );
}

//=========================================================================
Content::Content(
            const uno::Reference< uno::XComponentContext > & rxContext,
            ContentProvider * pProvider,
            const uno::Reference< ucb::XContentIdentifier > & Identifier,
            const ContentProperties & rProps )
: ContentImplHelper( rxContext, pProvider, Identifier ),
  m_aProps( rProps ),
  m_eState( PERSISTENT ),
  m_pProvider( pProvider )
{
}

//=========================================================================
// ctor for a content just created via XContentCreator::createNewContent()
Content::Content(
            const uno::Reference< uno::XComponentContext >& rxContext,
            ContentProvider* pProvider,
            const uno::Reference< ucb::XContentIdentifier >& Identifier,
            const ucb::ContentInfo& Info )
  : ContentImplHelper( rxContext, pProvider, Identifier ),
  m_aProps( lcl_getContentType( Info.Type ), rtl::OUString() ), // no Title (yet)
  m_eState( TRANSIENT ),
  m_pProvider( pProvider )
{
}

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

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

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

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

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

    if ( !aRet.hasValue() )
    {
        aRet = cppu::queryInterface(
                rType, static_cast< ucb::XContentCreator * >( this ) );
        if ( aRet.hasValue() )
        {
            if ( !m_aProps.isContentCreator() )
                return uno::Any();
        }
    }

    return aRet;
}

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

XTYPEPROVIDER_COMMON_IMPL( Content );

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

    if ( m_aProps.isContentCreator() )
    {
        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( ucb::XContent ),
                    CPPU_TYPE_REF( ucb::XCommandProcessor ),
                    CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
                    CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
                    CPPU_TYPE_REF( beans::XPropertyContainer ),
                    CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
                    CPPU_TYPE_REF( container::XChild ),
                    CPPU_TYPE_REF( 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( ucb::XContent ),
                    CPPU_TYPE_REF( ucb::XCommandProcessor ),
                    CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
                    CPPU_TYPE_REF( 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 Content::getImplementationName()
    throw( uno::RuntimeException )
{
    return rtl::OUString( "com.sun.star.comp.ucb.TransientDocumentsContent" );
}

//=========================================================================
// virtual
uno::Sequence< rtl::OUString > SAL_CALL Content::getSupportedServiceNames()
    throw( uno::RuntimeException )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    uno::Sequence< rtl::OUString > aSNS( 1 );

    if ( m_aProps.getType() == STREAM )
        aSNS.getArray()[ 0 ] = rtl::OUString( TDOC_STREAM_CONTENT_SERVICE_NAME );
    else if ( m_aProps.getType() == FOLDER )
        aSNS.getArray()[ 0 ] = rtl::OUString( TDOC_FOLDER_CONTENT_SERVICE_NAME );
    else if ( m_aProps.getType() == DOCUMENT )
        aSNS.getArray()[ 0 ] = rtl::OUString( TDOC_DOCUMENT_CONTENT_SERVICE_NAME );
    else
        aSNS.getArray()[ 0 ] = rtl::OUString( TDOC_ROOT_CONTENT_SERVICE_NAME );

    return aSNS;
}

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

// virtual
rtl::OUString SAL_CALL Content::getContentType()
    throw( uno::RuntimeException )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );
    return m_aProps.getContentType();
}

//=========================================================================
// virtual
uno::Reference< ucb::XContentIdentifier > SAL_CALL
Content::getIdentifier()
    throw( uno::RuntimeException )
{
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        // Transient?
        if ( m_eState == TRANSIENT )
        {
            // Transient contents have no identifier.
            return uno::Reference< ucb::XContentIdentifier >();
        }
    }
    return ContentImplHelper::getIdentifier();
}

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

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

    if ( aCommand.Name == "getPropertyValues" )
    {
        //////////////////////////////////////////////////////////////////
        // getPropertyValues
        //////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

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

        aRet = open( aOpenCommand, Environment );
    }
    else if ( aCommand.Name == "insert" )
    {
        //////////////////////////////////////////////////////////////////
        // insert ( Supported by folders and streams only )
        //////////////////////////////////////////////////////////////////

        ContentType eType = m_aProps.getType();
        if ( ( eType != FOLDER ) && ( eType != STREAM ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedCommandException(
                                rtl::OUString( "insert command only supported by "
                                        "folders and streams!"  ),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
                Environment );
            // Unreachable
        }

#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
        if ( eType == STREAM )
        {
            Uri aUri( m_xIdentifier->getContentIdentifier() );
            Uri aParentUri( aUri.getParentUri() );
            if ( aParentUri.isDocument() )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( ucb::UnsupportedCommandException(
                                    rtl::OUString( "insert command not supported by "
                                            "streams that are direct children "
                                            "of document root!"  ),
                                    static_cast< cppu::OWeakObject * >(
                                        this ) ) ),
                    Environment );
                // Unreachable
            }
        }
#endif
        ucb::InsertCommandArgument aArg;
        if ( !( aCommand.Argument >>= aArg ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString( "Wrong argument type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }

        sal_Int32 nNameClash = aArg.ReplaceExisting
                             ? ucb::NameClash::OVERWRITE
                             : ucb::NameClash::ERROR;
        insert( aArg.Data, nNameClash, Environment );
    }
    else if ( aCommand.Name == "delete" )
    {
        //////////////////////////////////////////////////////////////////
        // delete ( Supported by folders and streams only )
        //////////////////////////////////////////////////////////////////

        {
            osl::MutexGuard aGuard( m_aMutex );

            ContentType eType = m_aProps.getType();
            if ( ( eType != FOLDER ) && ( eType != STREAM ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( ucb::UnsupportedCommandException(
                                    rtl::OUString( "delete command only supported by "
                                            "folders and streams!"  ),
                                    static_cast< cppu::OWeakObject * >(
                                        this ) ) ),
                    Environment );
                // Unreachable
            }
        }

        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( "Uri"),
                             -1,
                             uno::makeAny(m_xIdentifier->
                                              getContentIdentifier()),
                             beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                ucb::IOErrorCode_CANT_WRITE,
                uno::Sequence< uno::Any >(&aProps, 1),
                Environment,
                rtl::OUString( "Cannot remove persistent data!" ),
                this );
            // Unreachable
        }

        // Remove own and all children's Additional Core Properties.
        removeAdditionalPropertySet( sal_True );
    }
    else if ( aCommand.Name == "transfer" )
    {
        //////////////////////////////////////////////////////////////////
        // transfer ( Supported by document and folders only )
        //////////////////////////////////////////////////////////////////

        {
            osl::MutexGuard aGuard( m_aMutex );

            ContentType eType = m_aProps.getType();
            if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( ucb::UnsupportedCommandException(
                                    rtl::OUString( "transfer command only supported "
                                            "by folders and documents!"  ),
                                    static_cast< cppu::OWeakObject * >(
                                        this ) ) ),
                    Environment );
                // Unreachable
            }
        }

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

        transfer( aInfo, Environment );
    }
    else if ( aCommand.Name == "createNewContent" )
    {
        //////////////////////////////////////////////////////////////////
        // createNewContent ( Supported by document and folders only )
        //////////////////////////////////////////////////////////////////

        {
            osl::MutexGuard aGuard( m_aMutex );

            ContentType eType = m_aProps.getType();
            if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( ucb::UnsupportedCommandException(
                                    rtl::OUString( "createNewContent command only "
                                            "supported by folders and "
                                            "documents!"  ),
                                    static_cast< cppu::OWeakObject * >(
                                        this ) ) ),
                    Environment );
                // Unreachable
            }
        }

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

        aRet <<= createNewContent( aInfo );
    }
    else
    {
        //////////////////////////////////////////////////////////////////
        // Unsupported command
        //////////////////////////////////////////////////////////////////

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

    return aRet;
}

//=========================================================================
// virtual
void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
    throw( uno::RuntimeException )
{
}

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

// virtual
uno::Sequence< ucb::ContentInfo > SAL_CALL
Content::queryCreatableContentsInfo()
    throw( uno::RuntimeException )
{
    return m_aProps.getCreatableContentsInfo();
}

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

        if ( Info.Type.isEmpty() )
            return uno::Reference< ucb::XContent >();

        sal_Bool bCreateFolder = Info.Type == TDOC_FOLDER_CONTENT_TYPE;

#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
        // streams cannot be created as direct children of document root
        if ( !bCreateFolder && ( m_aProps.getType() == DOCUMENT ) )
        {
            OSL_FAIL( "Content::createNewContent - streams cannot be "
                        "created as direct children of document root!" );
            return uno::Reference< ucb::XContent >();
        }
#endif
        if ( !bCreateFolder && Info.Type != TDOC_STREAM_CONTENT_TYPE )
        {
            OSL_FAIL( "Content::createNewContent - unsupported type!" );
            return uno::Reference< ucb::XContent >();
        }

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

        OSL_ENSURE( !aURL.isEmpty(),
                    "Content::createNewContent - empty identifier!" );

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

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

        uno::Reference< ucb::XContentIdentifier > xId
            = new ::ucbhelper::ContentIdentifier( aURL );

        return create( m_xContext, m_pProvider, xId, Info );
    }
    else
    {
        OSL_FAIL( "createNewContent called on non-contentcreator object!" );
        return uno::Reference< ucb::XContent >();
    }
}

//=========================================================================
// virtual
rtl::OUString Content::getParentURL()
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );
    Uri aUri( m_xIdentifier->getContentIdentifier() );
    return aUri.getParentUri();
}

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

    // Assemble new content identifier...
    Uri aUri( m_xIdentifier->getContentIdentifier() );
    rtl::OUStringBuffer aNewURL = aUri.getParentUri();
    aNewURL.append( ::ucb_impl::urihelper::encodeSegment( rTitle ) );

    return
        uno::Reference< ucb::XContentIdentifier >(
            new ::ucbhelper::ContentIdentifier( aNewURL.makeStringAndClear() ) );
}

//=========================================================================
void Content::queryChildren( ContentRefList& rChildren )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    // Only folders (root, documents, folders) have children.
    if ( !m_aProps.getIsFolder() )
        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.

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

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

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

    sal_Int32 nLen = aURL.getLength();

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

    while ( it != end )
    {
        ::ucbhelper::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(
                    ContentRef(
                        static_cast< Content * >( xChild.get() ) ) );
            }
        }
        ++it;
    }
}

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

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

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

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

    // Only folders and streams can be renamed -> exchange identity.
    ContentType eType = m_aProps.getType();
    if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
    {
        OSL_FAIL( "Content::exchangeIdentity - "
                               "Not supported by root or document!" );
        return sal_False;
    }

    // Exchange own identitity.

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

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

                ContentRefList aChildren;
                queryChildren( aChildren );

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

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

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

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

                    ++it;
                }
            }
            return sal_True;
        }
    }

    OSL_FAIL( "Content::exchangeIdentity - "
                "Panic! Cannot exchange identity!" );
    return sal_False;
}

//=========================================================================
// static
uno::Reference< sdbc::XRow > Content::getPropertyValues(
                const uno::Reference< uno::XComponentContext >& rxContext,
                const uno::Sequence< beans::Property >& rProperties,
                ContentProvider* pProvider,
                const rtl::OUString& rContentId )
{
    ContentProperties aData;
    if ( loadData( pProvider, rContentId, aData ) )
    {
        return getPropertyValues(
            rxContext, rProperties, aData, pProvider, rContentId );
    }
    else
    {
        rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
            = new ::ucbhelper::PropertyValueSet( rxContext );

        sal_Int32 nCount = rProperties.getLength();
        if ( nCount )
        {
            const beans::Property* pProps = rProperties.getConstArray();
            for ( sal_Int32 n = 0; n < nCount; ++n )
                xRow->appendVoid( pProps[ n ] );
        }

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

//=========================================================================
// static
uno::Reference< sdbc::XRow > Content::getPropertyValues(
                const uno::Reference< uno::XComponentContext >& rxContext,
                const uno::Sequence< beans::Property >& rProperties,
                const ContentProperties& rData,
                ContentProvider* pProvider,
                const rtl::OUString& rContentId )
{
    // Note: Empty sequence means "get values of all supported properties".

    rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
        = new ::ucbhelper::PropertyValueSet( rxContext );

    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 == "ContentType" )
            {
                xRow->appendString ( rProp, rData.getContentType() );
            }
            else if ( rProp.Name == "Title" )
            {
                xRow->appendString ( rProp, rData.getTitle() );
            }
            else if ( rProp.Name == "IsDocument" )
            {
                xRow->appendBoolean( rProp, rData.getIsDocument() );
            }
            else if ( rProp.Name == "IsFolder" )
            {
                xRow->appendBoolean( rProp, rData.getIsFolder() );
            }
            else if ( rProp.Name == "CreatableContentsInfo" )
            {
                xRow->appendObject(
                    rProp, uno::makeAny( rData.getCreatableContentsInfo() ) );
            }
            else if ( rProp.Name == "Storage" )
            {
                // Storage is only supported by folders.
                ContentType eType = rData.getType();
                if ( eType == FOLDER )
                    xRow->appendObject(
                        rProp,
                        uno::makeAny(
                            pProvider->queryStorageClone( rContentId ) ) );
                else
                    xRow->appendVoid( rProp );
            }
            else if ( rProp.Name == "DocumentModel" )
            {
                // DocumentModel is only supported by documents.
                ContentType eType = rData.getType();
                if ( eType == DOCUMENT )
                    xRow->appendObject(
                        rProp,
                        uno::makeAny(
                            pProvider->queryDocumentModel( rContentId ) ) );
                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("ContentType"),
                      -1,
                      getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getContentType() );

        ContentType eType = rData.getType();

        xRow->appendString (
            beans::Property( rtl::OUString("Title"),
                      -1,
                      getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                      // Title is read-only for root and documents.
                      beans::PropertyAttribute::BOUND ||
                      ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
                        ? beans::PropertyAttribute::READONLY
                        : 0 ),
            rData.getTitle() );
        xRow->appendBoolean(
            beans::Property( rtl::OUString("IsDocument"),
                      -1,
                      getCppuBooleanType(),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getIsDocument() );
        xRow->appendBoolean(
            beans::Property( rtl::OUString("IsFolder"),
                      -1,
                      getCppuBooleanType(),
                      beans::PropertyAttribute::BOUND
                        | beans::PropertyAttribute::READONLY ),
            rData.getIsFolder() );
        xRow->appendObject(
            beans::Property(
                rtl::OUString("CreatableContentsInfo"),
                -1,
                getCppuType( static_cast<
                        const uno::Sequence< ucb::ContentInfo > * >( 0 ) ),
                beans::PropertyAttribute::BOUND
                | beans::PropertyAttribute::READONLY ),
            uno::makeAny( rData.getCreatableContentsInfo() ) );

        // Storage is only supported by folders.
        if ( eType == FOLDER )
            xRow->appendObject(
                beans::Property( rtl::OUString("Storage"),
                          -1,
                          getCppuType(
                            static_cast<
                                const uno::Reference< embed::XStorage > * >( 0 ) ),
                          beans::PropertyAttribute::BOUND
                            | beans::PropertyAttribute::READONLY ),
                uno::makeAny( pProvider->queryStorageClone( rContentId ) ) );

        // DocumentModel is only supported by documents.
        if ( eType == DOCUMENT )
            xRow->appendObject(
                beans::Property( rtl::OUString("DocumentModel"),
                          -1,
                          getCppuType(
                            static_cast<
                                const uno::Reference< frame::XModel > * >( 0 ) ),
                          beans::PropertyAttribute::BOUND
                            | beans::PropertyAttribute::READONLY ),
                uno::makeAny(
                    pProvider->queryDocumentModel( rContentId ) ) );

        // 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 > Content::getPropertyValues(
                        const uno::Sequence< beans::Property >& rProperties )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );
    return getPropertyValues( m_xContext,
                              rProperties,
                              m_aProps,
                              m_pProvider,
                              m_xIdentifier->getContentIdentifier() );
}

//=========================================================================
uno::Sequence< uno::Any > Content::setPropertyValues(
        const uno::Sequence< beans::PropertyValue >& rValues,
        const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw( 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< ucb::XPersistentPropertySet > xAdditionalPropSet;
    sal_Bool bTriedToGetAdditonalPropSet = sal_False;

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

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

        if ( rValue.Name == "ContentType" )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString( "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name == "IsDocument" )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString( "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name == "IsFolder" )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString( "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name == "CreatableContentsInfo" )
        {
            // Read-only property!
            aRet[ n ] <<= lang::IllegalAccessException(
                            rtl::OUString( "Property is read-only!" ),
                            static_cast< cppu::OWeakObject * >( this ) );
        }
        else if ( rValue.Name == "Title" )
        {
            // Title is read-only for root and documents.
            ContentType eType = m_aProps.getType();
            if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
            {
                aRet[ n ] <<= lang::IllegalAccessException(
                                rtl::OUString( "Property is read-only!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else
            {
                rtl::OUString aNewValue;
                if ( rValue.Value >>= aNewValue )
                {
                    // No empty titles!
                    if ( !aNewValue.isEmpty() )
                    {
                        if ( aNewValue != m_aProps.getTitle() )
                        {
                            // modified title -> modified URL -> exchange !
                            if ( m_eState == PERSISTENT )
                                bExchange = sal_True;

                            aOldTitle = m_aProps.getTitle();
                            m_aProps.setTitle( aNewValue );

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

                            // remember position within sequence of values
                            // (for error handling).
                            nTitlePos = n;
                        }
                    }
                    else
                    {
                        aRet[ n ] <<= lang::IllegalArgumentException(
                                    rtl::OUString( "Empty Title not allowed!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 );
                    }
                }
                else
                {
                    aRet[ n ] <<= beans::IllegalTypeException(
                                rtl::OUString( "Title Property value has wrong type!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
                }
            }
        }
        else if ( rValue.Name == "Storage" )
        {
            ContentType eType = m_aProps.getType();
            if ( eType == FOLDER )
            {
                aRet[ n ] <<= lang::IllegalAccessException(
                                rtl::OUString( "Property is read-only!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else
            {
                // Storage is only supported by folders.
                aRet[ n ] <<= beans::UnknownPropertyException(
                            rtl::OUString( "Storage property only supported by folders" ),
                            static_cast< cppu::OWeakObject * >( this ) );
            }
        }
        else if ( rValue.Name == "DocumentModel" )
        {
            ContentType eType = m_aProps.getType();
            if ( eType == DOCUMENT )
            {
                aRet[ n ] <<= lang::IllegalAccessException(
                                rtl::OUString( "Property is read-only!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else
            {
                // Storage is only supported by folders.
                aRet[ n ] <<= beans::UnknownPropertyException(
                            rtl::OUString( "DocumentModel property only supported by "
                                "documents" ),
                            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( "No property set for storing the value!" ),
                                static_cast< cppu::OWeakObject * >( this ) );
            }
        }
    }

    if ( bExchange )
    {
        uno::Reference< ucb::XContentIdentifier > xOldId
            = m_xIdentifier;
        uno::Reference< 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 );
            aOldTitle = rtl::OUString();

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

    if ( !aOldTitle.isEmpty() )
    {
        aEvent.PropertyName = rtl::OUString("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::Reference< io::XInputStream >(), xEnv ) )
            {
                uno::Any aProps
                    = uno::makeAny(
                             beans::PropertyValue(
                                 rtl::OUString( "Uri"),
                                 -1,
                                 uno::makeAny(m_xIdentifier->
                                                  getContentIdentifier()),
                                 beans::PropertyState_DIRECT_VALUE));
                ucbhelper::cancelCommandExecution(
                    ucb::IOErrorCode_CANT_WRITE,
                    uno::Sequence< uno::Any >(&aProps, 1),
                    xEnv,
                    rtl::OUString( "Cannot store persistent data!" ),
                    this );
                // Unreachable
            }
        }

        aChanges.realloc( nChanged );

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

    return aRet;
}

//=========================================================================
uno::Any Content::open(
                const ucb::OpenCommandArgument2& rArg,
                const uno::Reference< ucb::XCommandEnvironment >& xEnv )
    throw( uno::Exception )
{
    if ( rArg.Mode == ucb::OpenMode::ALL ||
         rArg.Mode == ucb::OpenMode::FOLDERS ||
         rArg.Mode == ucb::OpenMode::DOCUMENTS )
    {
        //////////////////////////////////////////////////////////////////
        // open command for a folder content
        //////////////////////////////////////////////////////////////////

        uno::Reference< ucb::XDynamicResultSet > xSet
            = new DynamicResultSet( m_xContext, this, rArg );
        return uno::makeAny( xSet );
    }
    else
    {
        //////////////////////////////////////////////////////////////////
        // open command for a document content
        //////////////////////////////////////////////////////////////////

        if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
             ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
        {
            // Currently(?) unsupported.
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedOpenModeException(
                                    rtl::OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    sal_Int16( rArg.Mode ) ) ),
                xEnv );
            // Unreachable
        }

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

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

        uno::Reference< io::XActiveDataStreamer > xDataStreamer(
                                        rArg.Sink, uno::UNO_QUERY );
        if ( xDataStreamer.is() )
        {
            // May throw CommandFailedException, DocumentPasswordRequest!
            uno::Reference< io::XStream > xStream = getStream( xEnv );
            if ( !xStream.is() )
            {
                // No interaction if we are not persistent!
                uno::Any aProps
                    = uno::makeAny(
                             beans::PropertyValue(
                                 rtl::OUString( "Uri"),
                                 -1,
                                 uno::makeAny(m_xIdentifier->
                                                  getContentIdentifier()),
                                 beans::PropertyState_DIRECT_VALUE));
                ucbhelper::cancelCommandExecution(
                    ucb::IOErrorCode_CANT_READ,
                    uno::Sequence< uno::Any >(&aProps, 1),
                    m_eState == PERSISTENT
                        ? xEnv
                        : uno::Reference< ucb::XCommandEnvironment >(),
                    rtl::OUString( "Got no data stream!" ),
                    this );
                // Unreachable
            }

            // Done.
            xDataStreamer->setStream( xStream );
        }
        else
        {
            uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
            if ( xOut.is() )
            {
                // PUSH: write data into xOut

                // May throw CommandFailedException, DocumentPasswordRequest!
                uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
                if ( !xIn.is() )
                {
                    // No interaction if we are not persistent!
                    uno::Any aProps
                        = uno::makeAny(
                                 beans::PropertyValue(
                                     rtl::OUString( "Uri"),
                                     -1,
                                     uno::makeAny(m_xIdentifier->
                                                      getContentIdentifier()),
                                     beans::PropertyState_DIRECT_VALUE));
                    ucbhelper::cancelCommandExecution(
                        ucb::IOErrorCode_CANT_READ,
                        uno::Sequence< uno::Any >(&aProps, 1),
                        m_eState == PERSISTENT
                            ? xEnv
                            : uno::Reference< ucb::XCommandEnvironment >(),
                        rtl::OUString("Got no data stream!"),
                        this );
                    // Unreachable
                }

                try
                {
                    uno::Sequence< sal_Int8 > aBuffer;
                    sal_Int32  nRead = xIn->readSomeBytes( aBuffer, 65536 );

                    while ( nRead > 0 )
                    {
                        aBuffer.realloc( nRead );
                        xOut->writeBytes( aBuffer );
                        aBuffer.realloc( 0 );
                        nRead = xIn->readSomeBytes( aBuffer, 65536 );
                    }

                    xOut->closeOutput();
                }
                catch ( io::NotConnectedException const & )
                {
                    // closeOutput, readSomeBytes, writeBytes
                }
                catch ( io::BufferSizeExceededException const & )
                {
                    // closeOutput, readSomeBytes, writeBytes
                }
                catch ( io::IOException const & )
                {
                    // closeOutput, readSomeBytes, writeBytes
                }
            }
            else
            {
                uno::Reference< io::XActiveDataSink > xDataSink(
                                                rArg.Sink, uno::UNO_QUERY );
                if ( xDataSink.is() )
                {
                    // PULL: wait for client read

                    // May throw CommandFailedException, DocumentPasswordRequest!
                    uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
                    if ( !xIn.is() )
                    {
                        // No interaction if we are not persistent!
                        uno::Any aProps
                            = uno::makeAny(
                                     beans::PropertyValue(
                                         rtl::OUString( "Uri"),
                                         -1,
                                         uno::makeAny(m_xIdentifier->
                                                          getContentIdentifier()),
                                         beans::PropertyState_DIRECT_VALUE));
                        ucbhelper::cancelCommandExecution(
                            ucb::IOErrorCode_CANT_READ,
                            uno::Sequence< uno::Any >(&aProps, 1),
                            m_eState == PERSISTENT
                                ? xEnv
                                : uno::Reference<
                                      ucb::XCommandEnvironment >(),
                            rtl::OUString( "Got no data stream!" ),
                            this );
                        // Unreachable
                    }

                    // Done.
                    xDataSink->setInputStream( xIn );
                }
                else
                {
                    ucbhelper::cancelCommandExecution(
                        uno::makeAny(
                            ucb::UnsupportedDataSinkException(
                                    rtl::OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    rArg.Sink ) ),
                        xEnv );
                    // Unreachable
                }
            }
        }
    }

    return uno::Any();
}

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

    ContentType eType = m_aProps.getType();

    OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
                "insert command only supported by streams and folders!" );

    Uri aUri( m_xIdentifier->getContentIdentifier() );

#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
#if OSL_DEBUG_LEVEL > 0
    if ( eType == STREAM )
    {
        Uri aParentUri( aUri.getParentUri() );
        OSL_ENSURE( !aParentUri.isDocument(),
                    "insert command not supported by streams that are direct "
                    "children of document root!" );
    }
#endif
#endif

    // Check, if all required properties were set.
    if ( eType == FOLDER )
    {
        // Required: Title

        if ( m_aProps.getTitle().isEmpty() )
            m_aProps.setTitle( aUri.getDecodedName() );
    }
    else // stream
    {
        // Required: data

        if ( !xData.is() )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::MissingInputStreamException(
                                rtl::OUString(),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
                xEnv );
            // Unreachable
        }

        // Required: Title

        if ( m_aProps.getTitle().isEmpty() )
            m_aProps.setTitle( aUri.getDecodedName() );
    }

    rtl::OUStringBuffer aNewURL = aUri.getParentUri();
    aNewURL.append( m_aProps.getTitle() );
    Uri aNewUri( aNewURL.makeStringAndClear() );

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

        // replace (possibly) existing object.
        case ucb::NameClash::OVERWRITE:
            break;

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

                do
                {
                    rtl::OUStringBuffer aNew = aNewUri.getUri();
                    aNew.appendAscii( "_" );
                    aNew.append( rtl::OUString::valueOf( ++nTry ) );
                    aNewUri.setUri( aNew.makeStringAndClear() );
                }
                while ( hasData( aNewUri ) && ( nTry < 1000 ) );

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

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

    // Identifier changed?
    sal_Bool bNewId = ( aUri != aNewUri );

    if ( bNewId )
    {
        m_xIdentifier
            = new ::ucbhelper::ContentIdentifier( aNewUri.getUri() );
    }

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

    m_eState = PERSISTENT;

    if ( bNewId )
    {
        //loadData( m_pProvider, m_aUri, m_aProps );

        aGuard.clear();
        inserted();
    }
}

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

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

    ContentType eType = m_aProps.getType();

    OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
                "delete command only supported by streams and folders!" );

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

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

    m_eState = DEAD;

    aGuard.clear();
    deleted();

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

        ContentRefList aChildren;
        queryChildren( aChildren );

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

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

//=========================================================================
void Content::notifyDocumentClosed()
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    m_eState = DEAD;

    // @@@ anything else to reset or such?

    // callback follows!
    aGuard.clear();

    // Propagate destruction to content event listeners
    // Remove this from provider's content list.
    deleted();
}

//=========================================================================
uno::Reference< ucb::XContent >
Content::queryChildContent( const rtl::OUString & rRelativeChildUri )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    const rtl::OUString aMyId = getIdentifier()->getContentIdentifier();
    rtl::OUStringBuffer aBuf( aMyId );
    if ( aMyId.getStr()[ aMyId.getLength() - 1 ] != sal_Unicode( '/' ) )
        aBuf.appendAscii( "/" );
    if ( rRelativeChildUri.getStr()[ 0 ] != sal_Unicode( '/' ) )
        aBuf.append( rRelativeChildUri );
    else
        aBuf.append( rRelativeChildUri.copy( 1 ) );

    uno::Reference< ucb::XContentIdentifier > xChildId
        = new ::ucbhelper::ContentIdentifier( aBuf.makeStringAndClear() );

    uno::Reference< ucb::XContent > xChild;
    try
    {
        xChild = m_pProvider->queryContent( xChildId );
    }
    catch ( ucb::IllegalIdentifierException const & )
    {
        // handled below.
    }

    OSL_ENSURE( xChild.is(),
                "Content::queryChildContent - unable to create child content!" );
    return xChild;
}

//=========================================================================
void Content::notifyChildRemoved( const rtl::OUString & rRelativeChildUri )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    // Ugly! Need to create child content object, just to fill event properly.
    uno::Reference< ucb::XContent > xChild
        = queryChildContent( rRelativeChildUri );

    if ( xChild.is() )
    {
        // callback follows!
        aGuard.clear();

        // Notify "REMOVED" event.
        ucb::ContentEvent aEvt(
            static_cast< cppu::OWeakObject * >( this ),
            ucb::ContentAction::REMOVED,
            xChild,
            getIdentifier() );
        notifyContentEvent( aEvt );
    }
}

//=========================================================================
void Content::notifyChildInserted( const rtl::OUString & rRelativeChildUri )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    // Ugly! Need to create child content object, just to fill event properly.
    uno::Reference< ucb::XContent > xChild
        = queryChildContent( rRelativeChildUri );

    if ( xChild.is() )
    {
        // callback follows!
        aGuard.clear();

        // Notify "INSERTED" event.
        ucb::ContentEvent aEvt(
            static_cast< cppu::OWeakObject * >( this ),
            ucb::ContentAction::INSERTED,
            xChild,
            getIdentifier() );
        notifyContentEvent( aEvt );
    }
}

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

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

    // Does source URI scheme match? Only vnd.sun.star.tdoc is supported.

    if ( ( rInfo.SourceURL.getLength() < TDOC_URL_SCHEME_LENGTH + 2 ) )
    {
        // Invaild length (to short).
        ucbhelper::cancelCommandExecution(
            uno::makeAny( ucb::InteractiveBadTransferURLException(
                            rtl::OUString(),
                            static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    rtl::OUString aScheme
        = rInfo.SourceURL.copy( 0, TDOC_URL_SCHEME_LENGTH + 2 )
            .toAsciiLowerCase();
    if ( aScheme != TDOC_URL_SCHEME ":/" )
    {
        // Invalid scheme.
        ucbhelper::cancelCommandExecution(
            uno::makeAny( ucb::InteractiveBadTransferURLException(
                            rtl::OUString(),
                            static_cast< cppu::OWeakObject * >( this ) ) ),
            xEnv );
        // Unreachable
    }

    // Does source URI describe a tdoc folder or stream?
    Uri aSourceUri( rInfo.SourceURL );
    if ( !aSourceUri.isValid() )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( lang::IllegalArgumentException(
                                rtl::OUString( "Invalid source URI! Syntax!" ),
                                static_cast< cppu::OWeakObject * >( this ),
                                -1 ) ),
            xEnv );
        // Unreachable
    }

    if ( aSourceUri.isRoot() || aSourceUri.isDocument() )
    {
        ucbhelper::cancelCommandExecution(
            uno::makeAny( lang::IllegalArgumentException(
                                rtl::OUString( "Invalid source URI! "
                                    "Must describe a folder or stream!" ),
                                static_cast< cppu::OWeakObject * >( this ),
                                -1 ) ),
            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("/");
    }

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

#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
    if ( m_aProps.getType() == DOCUMENT )
    {
        bool bOK = false;

        uno::Reference< embed::XStorage > xStorage
            = m_pProvider->queryStorage(
                aSourceUri.getParentUri(), READ_WRITE_NOCREATE );
        if ( xStorage.is() )
        {
            try
            {
                if ( xStorage->isStreamElement( aSourceUri.getDecodedName() ) )
                {
                    ucbhelper::cancelCommandExecution(
                        uno::makeAny( lang::IllegalArgumentException(
                                        rtl::OUString( "Invalid source URI! "
                                            "Streams cannot be created as "
                                            "children of document root!" ),
                                        static_cast< cppu::OWeakObject * >(
                                            this ),
                                        -1 ) ),
                        xEnv );
                    // Unreachable
                }
                bOK = true;
            }
            catch ( container::NoSuchElementException const & )
            {
                // handled below.
            }
            catch ( lang::IllegalArgumentException const & )
            {
                // handled below.
            }
            catch ( embed::InvalidStorageException const & )
            {
                // handled below.
            }
        }

        if ( !bOK )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( lang::IllegalArgumentException(
                                    rtl::OUString( "Invalid source URI! "
                                        "Unabale to determine source type!" ),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                xEnv );
            // Unreachable
        }
    }
#endif

    /////////////////////////////////////////////////////////////////////////
    // Copy data.
    /////////////////////////////////////////////////////////////////////////

    rtl::OUString aNewName( !rInfo.NewTitle.isEmpty()
                                ? rInfo.NewTitle
                                : aSourceUri.getDecodedName() );

    if ( !copyData( aSourceUri, aNewName ) )
    {
        uno::Any aProps
            = uno::makeAny(
                     beans::PropertyValue(
                         rtl::OUString( "Uri"),
                         -1,
                         uno::makeAny( rInfo.SourceURL ),
                         beans::PropertyState_DIRECT_VALUE));
        ucbhelper::cancelCommandExecution(
            ucb::IOErrorCode_CANT_WRITE,
            uno::Sequence< uno::Any >(&aProps, 1),
            xEnv,
            rtl::OUString( "Cannot copy data!"  ),
            this );
        // Unreachable
    }

    /////////////////////////////////////////////////////////////////////////
    // Copy own and all children's Additional Core Properties.
    /////////////////////////////////////////////////////////////////////////

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

    if ( !rInfo.NewTitle.isEmpty() )
        aTargetUri += ::ucb_impl::urihelper::encodeSegment( rInfo.NewTitle );
    else
        aTargetUri += aSourceUri.getName();

    if ( !copyAdditionalPropertySet(
            aSourceUri.getUri(), aTargetUri, sal_True ) )
    {
        uno::Any aProps
            = uno::makeAny(
                     beans::PropertyValue(
                         rtl::OUString( "Uri"),
                         -1,
                         uno::makeAny( rInfo.SourceURL ),
                         beans::PropertyState_DIRECT_VALUE));
        ucbhelper::cancelCommandExecution(
            ucb::IOErrorCode_CANT_WRITE,
            uno::Sequence< uno::Any >(&aProps, 1),
            xEnv,
            rtl::OUString( "Cannot copy additional properties!"  ),
            this );
        // Unreachable
    }

    /////////////////////////////////////////////////////////////////////////
    // Propagate new content.
    /////////////////////////////////////////////////////////////////////////

    rtl::Reference< Content > xTarget;
    try
    {
        uno::Reference< ucb::XContentIdentifier > xTargetId
            = new ::ucbhelper::ContentIdentifier( aTargetUri );

        // Note: The static cast is okay here, because its sure that
        //       m_xProvider is always the WebDAVContentProvider.
        xTarget = static_cast< Content * >(
            m_pProvider->queryContent( xTargetId ).get() );

    }
    catch ( ucb::IllegalIdentifierException const & )
    {
        // queryContent
    }

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

    // Announce transfered content in its new folder.
    xTarget->inserted();

    /////////////////////////////////////////////////////////////////////////
    // Remove source, if requested
    /////////////////////////////////////////////////////////////////////////

    if ( rInfo.MoveData )
    {
        rtl::Reference< Content > xSource;
        try
        {
            uno::Reference< ucb::XContentIdentifier >
                xSourceId = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );

            // Note: The static cast is okay here, because its sure
            //       that m_xProvider is always the ContentProvider.
            xSource = static_cast< Content * >(
                m_xProvider->queryContent( xSourceId ).get() );
        }
        catch ( ucb::IllegalIdentifierException const & )
        {
            // queryContent
        }

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

        // Propagate destruction (recursively).
        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( "Uri"),
                             -1,
                             uno::makeAny( rInfo.SourceURL ),
                             beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                ucb::IOErrorCode_CANT_WRITE,
                uno::Sequence< uno::Any >(&aProps, 1),
                xEnv,
                rtl::OUString( "Cannot remove persistent data of source object!" ),
                this );
            // Unreachable
        }

        // Remove own and all children's Additional Core Properties.
        if ( !xSource->removeAdditionalPropertySet( sal_True ) )
        {
            uno::Any aProps
                = uno::makeAny(
                         beans::PropertyValue(
                             rtl::OUString( "Uri"),
                             -1,
                             uno::makeAny( rInfo.SourceURL ),
                             beans::PropertyState_DIRECT_VALUE));
            ucbhelper::cancelCommandExecution(
                ucb::IOErrorCode_CANT_WRITE,
                uno::Sequence< uno::Any >(&aProps, 1),
                xEnv,
                rtl::OUString( "Cannot remove additional properties of source object!" ),
                this );
            // Unreachable
        }

    } // rInfo.MoveData
}

//=========================================================================
//static
bool Content::hasData( ContentProvider* pProvider, const Uri & rUri )
{
    if ( rUri.isRoot() )
    {
        return true; // root has no storage
    }
    else if ( rUri.isDocument() )
    {
        uno::Reference< embed::XStorage > xStorage
            = pProvider->queryStorage( rUri.getUri(), READ );
        return xStorage.is();
    }
    else
    {
        // folder or stream

        // Ask parent storage. In case that rUri describes a stream,
        // ContentProvider::queryStorage( rUri ) would return null.

        uno::Reference< embed::XStorage > xStorage
            = pProvider->queryStorage( rUri.getParentUri(), READ );

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

        uno::Reference< container::XNameAccess > xParentNA(
            xStorage, uno::UNO_QUERY );

        OSL_ENSURE( xParentNA.is(), "Got no css.container.XNameAccess!" );

        return xParentNA->hasByName( rUri.getDecodedName() );
    }
}

//=========================================================================
//static
bool Content::loadData( ContentProvider* pProvider,
                        const Uri & rUri,
                        ContentProperties& rProps )
{
    if ( rUri.isRoot() ) // root has no storage, but can always be created
    {
        rProps
            = ContentProperties(
                ROOT, pProvider->queryStorageTitle( rUri.getUri() ) );
    }
    else if ( rUri.isDocument() ) // document must have storage
    {
        uno::Reference< embed::XStorage > xStorage
            = pProvider->queryStorage( rUri.getUri(), READ );

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

        rProps
            = ContentProperties(
                DOCUMENT, pProvider->queryStorageTitle( rUri.getUri() ) );
    }
    else // stream or folder; stream has no storage; folder has storage
    {
        uno::Reference< embed::XStorage > xStorage
            = pProvider->queryStorage( rUri.getParentUri(), READ );

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

        // Check whether exists at all, is stream or folder
        try
        {
            // return: true  -> folder
            // return: false -> stream
            // NoSuchElementException -> neither folder nor stream
            bool bIsFolder
                = xStorage->isStorageElement( rUri.getDecodedName() );

            rProps
                = ContentProperties(
                    bIsFolder ? FOLDER : STREAM,
                    pProvider->queryStorageTitle( rUri.getUri() ) );
        }
        catch ( container::NoSuchElementException const & )
        {
            // there is no element with such name
            //OSL_ENSURE( false, "Caught NoSuchElementException!" );
            return false;
        }
        catch ( lang::IllegalArgumentException const & )
        {
            // an illegal argument is provided
            OSL_FAIL( "Caught IllegalArgumentException!" );
            return false;
        }
        catch ( embed::InvalidStorageException const & )
        {
            // this storage is in invalid state for any reason
            OSL_FAIL( "Caught InvalidStorageException!" );
            return false;
        }
    }
    return true;
}

//=========================================================================
bool Content::storeData( const uno::Reference< io::XInputStream >& xData,
                         const uno::Reference<
                            ucb::XCommandEnvironment >& xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    ContentType eType = m_aProps.getType();
    if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
    {
        OSL_FAIL( "storeData not supported by root and documents!" );
        return false;
    }

    Uri aUri( m_xIdentifier->getContentIdentifier() );

    if ( eType == FOLDER )
    {
        uno::Reference< embed::XStorage > xStorage
            = m_pProvider->queryStorage( aUri.getUri(), READ_WRITE_CREATE );

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

        uno::Reference< beans::XPropertySet > xPropSet(
            xStorage, uno::UNO_QUERY );
        OSL_ENSURE( xPropSet.is(),
                    "Content::storeData - Got no XPropertySet interface!" );
        if ( !xPropSet.is() )
            return false;

        try
        {
            // According to MBA, if no mediatype is set, folder and all
            // its contents will be lost on save of the document!!!
            xPropSet->setPropertyValue(
                rtl::OUString(  "MediaType"  ),
                uno::makeAny(
                    rtl::OUString(                        // @@@ better mediatype
                        "application/binary"  ) ) );
        }
        catch ( beans::UnknownPropertyException const & )
        {
            OSL_FAIL( "Property MediaType not supported!" );
            return false;
        }
        catch ( beans::PropertyVetoException const & )
        {
            OSL_FAIL( "Caught PropertyVetoException!" );
            return false;
        }
        catch ( lang::IllegalArgumentException const & )
        {
            OSL_FAIL( "Caught IllegalArgumentException!" );
            return false;
        }
        catch ( lang::WrappedTargetException const & )
        {
            OSL_FAIL( "Caught WrappedTargetException!" );
            return false;
        }

        if ( !commitStorage( xStorage ) )
            return false;
    }
    else if ( eType == STREAM )
    {
        // stream

        // Important: Parent storage and output stream must be kept alive until
        //            changes have been committed!
        uno::Reference< embed::XStorage > xStorage
            = m_pProvider->queryStorage(
                aUri.getParentUri(), READ_WRITE_CREATE );
        uno::Reference< io::XOutputStream > xOut;

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

        if ( xData.is() )
        {
            // May throw CommandFailedException, DocumentPasswordRequest!
            xOut = getTruncatedOutputStream( xEnv );

            OSL_ENSURE( xOut.is(), "No target data stream!" );

            try
            {
                uno::Sequence< sal_Int8 > aBuffer;
                sal_Int32 nRead = xData->readSomeBytes( aBuffer, 65536 );

                while ( nRead > 0 )
                {
                    aBuffer.realloc( nRead );
                    xOut->writeBytes( aBuffer );
                    aBuffer.realloc( 0 );
                    nRead = xData->readSomeBytes( aBuffer, 65536 );
                }

                closeOutputStream( xOut );
            }
            catch ( io::NotConnectedException const & )
            {
                // readSomeBytes, writeBytes
                OSL_FAIL( "Caught NotConnectedException!" );
                closeOutputStream( xOut );
                return false;
            }
            catch ( io::BufferSizeExceededException const & )
            {
                // readSomeBytes, writeBytes
                OSL_FAIL( "Caught BufferSizeExceededException!" );
                closeOutputStream( xOut );
                return false;
            }
            catch ( io::IOException const & )
            {
                // readSomeBytes, writeBytes
                OSL_FAIL( "Caught IOException!" );
                closeOutputStream( xOut );
                return false;
            }
            catch ( ... )
            {
                closeOutputStream( xOut );
                throw;
            }
        }

        // Commit changes.
        if ( !commitStorage( xStorage ) )
            return false;
    }
    else
    {
        OSL_FAIL( "Unknown content type!" );
        return false;
    }
    return true;
}

//=========================================================================
bool Content::renameData(
            const uno::Reference< ucb::XContentIdentifier >& xOldId,
            const uno::Reference< ucb::XContentIdentifier >& xNewId )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    ContentType eType = m_aProps.getType();
    if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
    {
        OSL_FAIL( "renameData not supported by root and documents!" );
        return false;
    }

    Uri aOldUri( xOldId->getContentIdentifier() );
    uno::Reference< embed::XStorage > xStorage
        = m_pProvider->queryStorage(
            aOldUri.getParentUri(), READ_WRITE_NOCREATE );

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

    try
    {
        Uri aNewUri( xNewId->getContentIdentifier() );
        xStorage->renameElement(
            aOldUri.getDecodedName(), aNewUri.getDecodedName() );
    }
    catch ( embed::InvalidStorageException const & )
    {
        // this storage is in invalid state for eny reason
        OSL_FAIL( "Caught InvalidStorageException!" );
        return false;
    }
    catch ( lang::IllegalArgumentException const & )
    {
        // an illegal argument is provided
        OSL_FAIL( "Caught IllegalArgumentException!" );
        return false;
    }
    catch ( container::NoSuchElementException const & )
    {
        // there is no element with old name in this storage
        OSL_FAIL( "Caught NoSuchElementException!" );
        return false;
    }
    catch ( container::ElementExistException const & )
    {
        // an element with new name already exists in this storage
        OSL_FAIL( "Caught ElementExistException!" );
        return false;
    }
    catch ( io::IOException const & )
    {
        // in case of io errors during renaming
        OSL_FAIL( "Caught IOException!" );
        return false;
    }
    catch ( embed::StorageWrappedTargetException const & )
    {
        // wraps other exceptions
        OSL_FAIL( "Caught StorageWrappedTargetException!" );
        return false;
    }

    return commitStorage( xStorage );
}

//=========================================================================
bool Content::removeData()
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    ContentType eType = m_aProps.getType();
    if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
    {
        OSL_FAIL( "removeData not supported by root and documents!" );
        return false;
    }

    Uri aUri( m_xIdentifier->getContentIdentifier() );
    uno::Reference< embed::XStorage > xStorage
        = m_pProvider->queryStorage(
            aUri.getParentUri(), READ_WRITE_NOCREATE );

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

    try
    {
        xStorage->removeElement( aUri.getDecodedName() );
    }
    catch ( embed::InvalidStorageException const & )
    {
        // this storage is in invalid state for eny reason
        OSL_FAIL( "Caught InvalidStorageException!" );
        return false;
    }
    catch ( lang::IllegalArgumentException const & )
    {
        // an illegal argument is provided
        OSL_FAIL( "Caught IllegalArgumentException!" );
        return false;
    }
    catch ( container::NoSuchElementException const & )
    {
        // there is no element with this name in this storage
        OSL_FAIL( "Caught NoSuchElementException!" );
        return false;
    }
    catch ( io::IOException const & )
    {
        // in case of io errors during renaming
        OSL_FAIL( "Caught IOException!" );
        return false;
    }
    catch ( embed::StorageWrappedTargetException const & )
    {
        // wraps other exceptions
        OSL_FAIL( "Caught StorageWrappedTargetException!" );
        return false;
    }

    return commitStorage( xStorage );
}

//=========================================================================
bool Content::copyData( const Uri & rSourceUri, const rtl::OUString & rNewName )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    ContentType eType = m_aProps.getType();
    if ( ( eType == ROOT ) || ( eType == STREAM ) )
    {
        OSL_FAIL( "copyData not supported by root and streams!" );
        return false;
    }

    Uri aDestUri( m_xIdentifier->getContentIdentifier() );
    uno::Reference< embed::XStorage > xDestStorage
        = m_pProvider->queryStorage( aDestUri.getUri(), READ_WRITE_NOCREATE );

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

    uno::Reference< embed::XStorage > xSourceStorage
        = m_pProvider->queryStorage( rSourceUri.getParentUri(), READ );

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

    try
    {
        xSourceStorage->copyElementTo( rSourceUri.getDecodedName(),
                                       xDestStorage,
                                       rNewName );
    }
    catch ( embed::InvalidStorageException const & )
    {
        // this storage is in invalid state for eny reason
        OSL_FAIL( "Caught InvalidStorageException!" );
        return false;
    }
    catch ( lang::IllegalArgumentException const & )
    {
        // an illegal argument is provided
        OSL_FAIL( "Caught IllegalArgumentException!" );
        return false;
    }
    catch ( container::NoSuchElementException const & )
    {
        // there is no element with this name in this storage
        OSL_FAIL( "Caught NoSuchElementException!" );
        return false;
    }
    catch ( container::ElementExistException const & )
    {
        // there is no element with this name in this storage
        OSL_FAIL( "Caught ElementExistException!" );
        return false;
    }
    catch ( io::IOException const & )
    {
        // in case of io errors during renaming
        OSL_FAIL( "Caught IOException!" );
        return false;
    }
    catch ( embed::StorageWrappedTargetException const & )
    {
        // wraps other exceptions
        OSL_FAIL( "Caught StorageWrappedTargetException!" );
        return false;
    }

    return commitStorage( xDestStorage );
}

//=========================================================================
// static
bool Content::commitStorage( const uno::Reference< embed::XStorage > & xStorage )
{
    // Commit changes
    uno::Reference< embed::XTransactedObject > xTO( xStorage, uno::UNO_QUERY );

    OSL_ENSURE( xTO.is(),
                "Required interface css.embed.XTransactedObject missing!" );
    try
    {
        xTO->commit();
    }
    catch ( io::IOException const & )
    {
        OSL_FAIL( "Caught IOException!" );
        return false;
    }
    catch ( lang::WrappedTargetException const & )
    {
        OSL_FAIL( "Caught WrappedTargetException!" );
        return false;
    }

    return true;
}

//=========================================================================
// static
bool Content::closeOutputStream(
    const uno::Reference< io::XOutputStream > & xOut )
{
    if ( xOut.is() )
    {
        try
        {
            xOut->closeOutput();
            return true;
        }
        catch ( io::NotConnectedException const & )
        {
            OSL_FAIL( "Caught NotConnectedException!" );
        }
        catch ( io::BufferSizeExceededException const & )
        {
            OSL_FAIL( "Caught BufferSizeExceededException!" );
        }
        catch ( io::IOException const & )
        {
            OSL_FAIL( "Caught IOException!" );
        }
    }
    return false;
}

//=========================================================================
static rtl::OUString obtainPassword(
        const rtl::OUString & rName,
        task::PasswordRequestMode eMode,
        const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    rtl::Reference< DocumentPasswordRequest > xRequest
        = new DocumentPasswordRequest( eMode, rName );

    if ( xEnv.is() )
    {
        uno::Reference< task::XInteractionHandler > xIH
            = xEnv->getInteractionHandler();
        if ( xIH.is() )
        {
            xIH->handle( xRequest.get() );

            rtl::Reference< ucbhelper::InteractionContinuation > xSelection
                = xRequest->getSelection();

            if ( xSelection.is() )
            {
                // Handler handled the request.
                uno::Reference< task::XInteractionAbort > xAbort(
                    xSelection.get(), uno::UNO_QUERY );
                if ( xAbort.is() )
                {
                    throw ucb::CommandFailedException(
                        rtl::OUString( "Abort requested by Interaction Handler."  ),
                        uno::Reference< uno::XInterface >(),
                        xRequest->getRequest() );
                }

                uno::Reference< task::XInteractionPassword > xPassword(
                    xSelection.get(), uno::UNO_QUERY );
                if ( xPassword.is() )
                {
                    return xPassword->getPassword();
                }

                // Unknown selection. Should never happen.
                throw ucb::CommandFailedException(
                    rtl::OUString( "Interaction Handler selected unknown continuation!"  ),
                    uno::Reference< uno::XInterface >(),
                    xRequest->getRequest() );
            }
        }
    }

    // No IH or IH did not handle exception.
    task::DocumentPasswordRequest aRequest;
    xRequest->getRequest() >>= aRequest;
    throw aRequest;
}

//=========================================================================
uno::Reference< io::XInputStream > Content::getInputStream(
        const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    rtl::OUString aUri;
    rtl::OUString aPassword;
    bool bPasswordRequested = false;

    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        OSL_ENSURE( m_aProps.getType() == STREAM,
                    "Content::getInputStream - content is no stream!" );

        aUri = Uri( m_xIdentifier->getContentIdentifier() ).getUri();
    }

    for ( ;; )
    {
        try
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );
            return uno::Reference< io::XInputStream >(
                m_pProvider->queryInputStream( aUri, aPassword ) );
        }
        catch ( packages::WrongPasswordException const & )
        {
            // Obtain (new) password.
            aPassword
                = obtainPassword( aUri, /* @@@ find better title */
                                  bPasswordRequested
                                   ? task::PasswordRequestMode_PASSWORD_REENTER
                                   : task::PasswordRequestMode_PASSWORD_ENTER,
                                   xEnv );
            bPasswordRequested = true;
        }
    }
}

//=========================================================================
static uno::Reference< io::XOutputStream > lcl_getTruncatedOutputStream(
                const rtl::OUString & rUri,
                ContentProvider * pProvider,
                const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    rtl::OUString aPassword;
    bool bPasswordRequested = false;
    for ( ;; )
    {
        try
        {
            return uno::Reference< io::XOutputStream >(
                pProvider->queryOutputStream(
                    rUri, aPassword, true /* truncate */ ) );
        }
        catch ( packages::WrongPasswordException const & )
        {
            // Obtain (new) password.
            aPassword
                = obtainPassword( rUri, /* @@@ find better title */
                                  bPasswordRequested
                                   ? task::PasswordRequestMode_PASSWORD_REENTER
                                   : task::PasswordRequestMode_PASSWORD_ENTER,
                                   xEnv );
            bPasswordRequested = true;
        }
    }
}

//=========================================================================
uno::Reference< io::XOutputStream > Content::getTruncatedOutputStream(
        const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    OSL_ENSURE( m_aProps.getType() == STREAM,
                "Content::getTruncatedOutputStream - content is no stream!" );

    return lcl_getTruncatedOutputStream(
            Uri( m_xIdentifier->getContentIdentifier() ).getUri(),
            m_pProvider,
            xEnv );
}

//=========================================================================
uno::Reference< io::XStream > Content::getStream(
        const uno::Reference< ucb::XCommandEnvironment > & xEnv )
    throw ( ucb::CommandFailedException,
            task::DocumentPasswordRequest )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    OSL_ENSURE( m_aProps.getType() == STREAM,
                "Content::getStream - content is no stream!" );

    rtl::OUString aUri( Uri( m_xIdentifier->getContentIdentifier() ).getUri() );
    rtl::OUString aPassword;
    bool bPasswordRequested = false;
    for ( ;; )
    {
        try
        {
            return uno::Reference< io::XStream >(
                m_pProvider->queryStream(
                    aUri, aPassword, false /* no truncate */ ) );
        }
        catch ( packages::WrongPasswordException const & )
        {
            // Obtain (new) password.
            aPassword
                = obtainPassword( aUri, /* @@@ find better title */
                                  bPasswordRequested
                                   ? task::PasswordRequestMode_PASSWORD_REENTER
                                   : task::PasswordRequestMode_PASSWORD_ENTER,
                                   xEnv );
            bPasswordRequested = true;
        }
    }
}

//=========================================================================
//=========================================================================
//
// ContentProperties Implementation.
//
//=========================================================================
//=========================================================================

uno::Sequence< ucb::ContentInfo >
ContentProperties::getCreatableContentsInfo() const
{
    if ( isContentCreator() )
    {
        uno::Sequence< beans::Property > aProps( 1 );
        aProps.getArray()[ 0 ] = beans::Property(
                    rtl::OUString("Title"),
                    -1,
                    getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
                    beans::PropertyAttribute::BOUND );

#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
        if ( getType() == DOCUMENT )
        {
            // streams cannot be created as direct children of document root
            uno::Sequence< ucb::ContentInfo > aSeq( 1 );

            // Folder.
            aSeq.getArray()[ 0 ].Type
                = rtl::OUString( TDOC_FOLDER_CONTENT_TYPE );
            aSeq.getArray()[ 0 ].Attributes
                = ucb::ContentInfoAttribute::KIND_FOLDER;
            aSeq.getArray()[ 0 ].Properties = aProps;

            return aSeq;
        }
        else
        {
#endif
            uno::Sequence< ucb::ContentInfo > aSeq( 2 );

            // Folder.
            aSeq.getArray()[ 0 ].Type
                = rtl::OUString( TDOC_FOLDER_CONTENT_TYPE );
            aSeq.getArray()[ 0 ].Attributes
                = ucb::ContentInfoAttribute::KIND_FOLDER;
            aSeq.getArray()[ 0 ].Properties = aProps;

            // Stream.
            aSeq.getArray()[ 1 ].Type
                = rtl::OUString( TDOC_STREAM_CONTENT_TYPE );
            aSeq.getArray()[ 1 ].Attributes
                = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
                  | ucb::ContentInfoAttribute::KIND_DOCUMENT;
            aSeq.getArray()[ 1 ].Properties = aProps;

            return aSeq;
#ifdef NO_STREAM_CREATION_WITHIN_DOCUMENT_ROOT
        }
#endif
    }
    else
    {
        OSL_FAIL( "getCreatableContentsInfo called on non-contentcreator "
                    "object!" );

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

//=========================================================================
bool ContentProperties::isContentCreator() const
{
    return ( getType() == FOLDER ) || ( getType() == DOCUMENT );
}

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