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

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

#ifndef _COM_SUN_STAR_BEANS_PROPERTYATTRIBUTE_HPP_
#include <com/sun/star/beans/PropertyAttribute.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYACCESS_HPP_
#include <com/sun/star/beans/XPropertyAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XENUMERATIONACCESS_HPP_
#include <com/sun/star/container/XEnumerationAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XHIERARCHICALNAMEACCESS_HPP_
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMED_HPP_
#include <com/sun/star/container/XNamed.hpp>
#endif
#ifndef _COM_SUN_STAR_IO_XACTIVEDATASINK_HPP_
#include <com/sun/star/io/XActiveDataSink.hpp>
#endif
#ifndef _COM_SUN_STAR_IO_XINPUTSTREAM_HPP_
#include <com/sun/star/io/XInputStream.hpp>
#endif
#ifndef _COM_SUN_STAR_IO_XOUTPUTSTREAM_HPP_
#include <com/sun/star/io/XOutputStream.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XROW_HPP_
#include <com/sun/star/sdbc/XRow.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_CONTENTINFOATTRIBUTE_HPP_
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_INTERACTIVEBADTRANSFRERURLEXCEPTION_HPP_
#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_NAMECLASH_HPP_
#include <com/sun/star/ucb/NameClash.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_OPENCOMMANDARGUMENT2_HPP_
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_OPENMODE_HPP_
#include <com/sun/star/ucb/OpenMode.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_INSERTCOMMANDARGUMENT_HPP_
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_TRANSFERINFO_HPP_
#include <com/sun/star/ucb/TransferInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_XCOMMANDINFO_HPP_
#include <com/sun/star/ucb/XCommandInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_UCB_XPERSISTENTPROPERTYSET_HPP_
#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_UTIL_XCHANGESBATCH_HPP_
#include <com/sun/star/util/XChangesBatch.hpp>
#endif
#ifndef _VOS_DIAGNOSE_HXX_
#include <vos/diagnose.hxx>
#endif
#ifndef _UCBHELPER_CONTENTIDENTIFIER_HXX
#include <ucbhelper/contentidentifier.hxx>
#endif
#ifndef _UCBHELPER_PROPERTYVALUESET_HXX
#include <ucbhelper/propertyvalueset.hxx>
#endif

#ifndef _PKGCONTENT_HXX
#include "pkgcontent.hxx"
#endif
#ifndef _PKGPROVIDER_HXX
#include "pkgprovider.hxx"
#endif
#ifndef _PKGRESULTSET_HXX
#include "pkgresultset.hxx"
#endif

using namespace com::sun::star::container;
using namespace com::sun::star::beans;
using namespace com::sun::star::io;
using namespace com::sun::star::lang;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::ucb;
using namespace com::sun::star::uno;
using namespace com::sun::star::util;
using namespace cppu;
using namespace rtl;

using namespace package_ucp;

#define NONE_MODIFIED           sal_uInt32( 0x00 )
#define MEDIATYPE_MODIFIED      sal_uInt32( 0x01 )
#define COMPRESSED_MODIFIED     sal_uInt32( 0x02 )
#define ENCRYPTED_MODIFIED      sal_uInt32( 0x03 )

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

ContentProperties::ContentProperties( const OUString& rContentType )
: aContentType( rContentType ),
#if SUPD>614
  nSize( 0 ),
  bCompressed( sal_True ),
  bEncrypted( sal_False )
#else
  nSize( 0 )
#endif
{
    bIsFolder = ( rContentType.compareToAscii(
                                    PACKAGE_FOLDER_CONTENT_TYPE ) == 0 );
    bIsDocument = !bIsFolder;

    VOS_ENSURE( bIsFolder || ( rContentType.compareToAscii(
                                PACKAGE_STREAM_CONTENT_TYPE ) == 0 ),
                "ContentProperties::ContentProperties - Unknown type!" );
}

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

// static ( "virtual" ctor )
Content* Content::create( const Reference< XMultiServiceFactory >& rxSMgr,
                          ContentProvider* pProvider,
                          const Reference< XContentIdentifier >& Identifier )
{
    OUString aURL = Identifier->getContentIdentifier();
    PackageUri aURI( aURL );
    ContentProperties aProps;
    Reference< XHierarchicalNameAccess > xPackage;

    if ( loadData( pProvider, aURI, aProps, xPackage ) )
    {
        // resource exists

        sal_Int32 nLastSlash = aURL.lastIndexOf( '/' );
        if ( ( nLastSlash + 1 ) == aURL.getLength() )
        {
            // Client explicitely requested a folder!
            if ( !aProps.bIsFolder )
                return 0;
        }

        Reference< XContentIdentifier > xId
            = new ::ucb::ContentIdentifier( rxSMgr, aURI.getUri() );
        return new Content( rxSMgr, pProvider, xId, xPackage, aURI, aProps );
    }
    else
    {
        // resource doesn't exist

        sal_Bool bFolder = sal_False;

        // Guess type according to URI.
        sal_Int32 nLastSlash = aURL.lastIndexOf( '/' );
        if ( ( nLastSlash + 1 ) == aURL.getLength() )
            bFolder = sal_True;

        Reference< XContentIdentifier > xId
            = new ::ucb::ContentIdentifier( rxSMgr, aURI.getUri() );

        ContentInfo aInfo;
        if ( bFolder )
            aInfo.Type = OUString::createFromAscii(
                            PACKAGE_FOLDER_CONTENT_TYPE );
        else if ( aURI.getPath().compareToAscii( "/" ) == 0 )
            aInfo.Type = OUString::createFromAscii(
                            PACKAGE_FOLDER_CONTENT_TYPE ); // root folder
        else
            aInfo.Type = OUString::createFromAscii(
                            PACKAGE_STREAM_CONTENT_TYPE );

        return new Content( rxSMgr, pProvider, xId, xPackage, aURI, aInfo );
    }
}

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

    if ( ( Info.Type.compareToAscii( PACKAGE_FOLDER_CONTENT_TYPE ) != 0 ) &&
         ( Info.Type.compareToAscii( PACKAGE_STREAM_CONTENT_TYPE ) != 0 ) )
        return 0;

    PackageUri aURI( Identifier->getContentIdentifier() );
    Reference< XHierarchicalNameAccess > xPackage;

#if 0
    // Fail, if content does exist.
    if ( hasData( pProvider, aURI, xPackage ) )
        return 0;
#else
    xPackage = pProvider->createPackage( aURI.getPackage() );
#endif

    Reference< XContentIdentifier > xId
        = new ::ucb::ContentIdentifier( rxSMgr, aURI.getUri() );
    return new Content( rxSMgr, pProvider, xId, xPackage, aURI, Info );
}

//=========================================================================
Content::Content( const Reference< XMultiServiceFactory >& rxSMgr,
                  ContentProvider* pProvider,
                  const Reference< XContentIdentifier >& Identifier,
                   const Reference< XHierarchicalNameAccess > & Package,
                  const PackageUri& rUri,
                  const ContentProperties& rProps )
: ContentImplHelper( rxSMgr, pProvider, Identifier ),
  m_xPackage( Package ),
  m_aUri( rUri ),
  m_aProps( rProps ),
  m_eState( PERSISTENT ),
  m_pProvider( pProvider ),
  m_nModifiedProps( NONE_MODIFIED )
{
}

//=========================================================================
Content::Content( const Reference< XMultiServiceFactory >& rxSMgr,
                  ContentProvider* pProvider,
                  const Reference< XContentIdentifier >& Identifier,
                   const Reference< XHierarchicalNameAccess > & Package,
                  const PackageUri& rUri,
                  const ContentInfo& Info )
: ContentImplHelper( rxSMgr, pProvider, Identifier, sal_False ),
  m_xPackage( Package ),
  m_aUri( rUri ),
  m_aProps( Info.Type ),
  m_eState( TRANSIENT ),
  m_pProvider( pProvider ),
  m_nModifiedProps( NONE_MODIFIED )
{
}

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

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

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

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

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

    if ( isFolder() )
          aRet = cppu::queryInterface( rType,
                                     static_cast< XContentCreator * >( this ) );

     return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
}

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

XTYPEPROVIDER_COMMON_IMPL( Content );

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

    if ( !pCollection )
    {
        osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
          if ( !pCollection )
          {
            if ( isFolder() )
            {
                  static OTypeCollection aCollection(
                    CPPU_TYPE_REF( XTypeProvider ),
                       CPPU_TYPE_REF( XServiceInfo ),
                       CPPU_TYPE_REF( XComponent ),
                       CPPU_TYPE_REF( XContent ),
                       CPPU_TYPE_REF( XCommandProcessor ),
                       CPPU_TYPE_REF( XPropertiesChangeNotifier ),
                       CPPU_TYPE_REF( XCommandInfoChangeNotifier ),
                       CPPU_TYPE_REF( XPropertyContainer ),
                       CPPU_TYPE_REF( XPropertySetInfoChangeNotifier ),
                       CPPU_TYPE_REF( XChild ),
                       CPPU_TYPE_REF( XContentCreator ) );  // !!
                  pCollection = &aCollection;
            }
            else
            {
                  static OTypeCollection aCollection(
                    CPPU_TYPE_REF( XTypeProvider ),
                       CPPU_TYPE_REF( XServiceInfo ),
                       CPPU_TYPE_REF( XComponent ),
                       CPPU_TYPE_REF( XContent ),
                       CPPU_TYPE_REF( XCommandProcessor ),
                       CPPU_TYPE_REF( XPropertiesChangeNotifier ),
                       CPPU_TYPE_REF( XCommandInfoChangeNotifier ),
                       CPPU_TYPE_REF( XPropertyContainer ),
                       CPPU_TYPE_REF( XPropertySetInfoChangeNotifier ),
                       CPPU_TYPE_REF( XChild ) );
                  pCollection = &aCollection;
            }
        }
    }

    return (*pCollection).getTypes();
}

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

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

//=========================================================================
// virtual
Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
    throw( RuntimeException )
{
    Sequence< OUString > aSNS( 1 );
    if ( isFolder() )
        aSNS.getArray()[ 0 ]
            = OUString::createFromAscii( PACKAGE_FOLDER_CONTENT_SERVICE_NAME );
    else
        aSNS.getArray()[ 0 ]
            = OUString::createFromAscii( PACKAGE_STREAM_CONTENT_SERVICE_NAME );

    return aSNS;
}

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

// virtual
OUString SAL_CALL Content::getContentType()
    throw( RuntimeException )
{
    return m_aProps.aContentType;
}

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

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

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

        Sequence< Property > Properties;
        if ( !( aCommand.Argument >>= Properties ) )
        {
            VOS_ENSURE( sal_False, "Wrong argument type!" );
            return Any();
        }

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

        Sequence< PropertyValue > aProperties;
        if ( !( aCommand.Argument >>= aProperties ) )
        {
            VOS_ENSURE( sal_False, "Wrong argument type!" );
            return Any();
        }

        if ( !aProperties.getLength() )
        {
            VOS_ENSURE( sal_False, "No properties!" );
            return Any();
        }

        setPropertyValues( aProperties );
    }
    else if ( aCommand.Name.compareToAscii( "getPropertySetInfo" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // getPropertySetInfo
        //////////////////////////////////////////////////////////////////

        // Note: Implemented by base class.
        aRet <<= getPropertySetInfo( Environment );
    }
    else if ( aCommand.Name.compareToAscii( "getCommandInfo" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // getCommandInfo
        //////////////////////////////////////////////////////////////////

        // Note: Implemented by base class.
        aRet <<= getCommandInfo( Environment );
    }
    else if ( aCommand.Name.compareToAscii( "open" ) == 0 )
    {
        OpenCommandArgument2 aOpenCommand;
        if ( aCommand.Argument >>= aOpenCommand )
        {
            aRet = open( aOpenCommand, Environment );
        }
        else
        {
            VOS_ENSURE( sal_False,
                        "Content::execute - invalid parameter!" );
            throw CommandAbortedException();
        }
    }
    else if ( aCommand.Name.compareToAscii( "insert" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // insert
        //////////////////////////////////////////////////////////////////

        InsertCommandArgument aArg;
        if ( aCommand.Argument >>= aArg )
        {
            sal_Int32 nNameClash = aArg.ReplaceExisting
                                 ? NameClash::OVERWRITE
                                 : NameClash::ERROR;
            insert( aArg.Data, nNameClash );
        }
        else
        {
            VOS_ENSURE( sal_False,
                        "Content::execute - invalid parameter!" );
            throw CommandAbortedException();
        }
    }
    else if ( aCommand.Name.compareToAscii( "delete" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // delete
        //////////////////////////////////////////////////////////////////

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

        // Remove own and all children's persistent data.
        removeData();

        // Remove own and all children's Additional Core Properties.
        removeAdditionalPropertySet( sal_True );
    }
    else if ( aCommand.Name.compareToAscii( "transfer" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // transfer
        //  ( Not available at stream objects )
        //////////////////////////////////////////////////////////////////

        TransferInfo aInfo;
        if ( aCommand.Argument >>= aInfo )
        {
            transfer( aInfo, Environment );
        }
        else
        {
            VOS_ENSURE( sal_False,
                        "Content::execute - invalid parameter!" );
            throw CommandAbortedException();
        }
    }
    else if ( aCommand.Name.compareToAscii( "flush" ) == 0 )
    {
        //////////////////////////////////////////////////////////////////
        // flush
        //  ( Not available at stream objects )
        //////////////////////////////////////////////////////////////////

        flushData();
    }
    else
    {
        //////////////////////////////////////////////////////////////////
        // Unsupported command
        //////////////////////////////////////////////////////////////////

        VOS_ENSURE( sal_False, "Content::execute - unsupported command!" );
        throw CommandAbortedException();
    }

    return aRet;
}

//=========================================================================
// virtual
void SAL_CALL Content::abort( sal_Int32 CommandId )
    throw( RuntimeException )
{
    // @@@ Implement logic to abort running commands, if this makes
    //     sense for your content.
}

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

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

        Sequence< Property > aProps( 1 );
        aProps.getArray()[ 0 ] = Property(
                    OUString::createFromAscii( "Title" ),
                    -1,
                    getCppuType( static_cast< const OUString * >( 0 ) ),
                    PropertyAttribute::BOUND );

        Sequence< ContentInfo > aSeq( 2 );

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

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

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

        return Sequence< ContentInfo >( 0 );
    }
}

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

        if ( !Info.Type.getLength() )
            return Reference< XContent >();

        if ( ( Info.Type.compareToAscii( PACKAGE_FOLDER_CONTENT_TYPE ) != 0 )
              &&
              ( Info.Type.compareToAscii( PACKAGE_STREAM_CONTENT_TYPE ) != 0 ) )
            return Reference< XContent >();

        OUString aURL = m_aUri.getUri();
        aURL += OUString::createFromAscii( "/" );

        if ( Info.Type.compareToAscii( PACKAGE_FOLDER_CONTENT_TYPE ) == 0 )
            aURL += OUString::createFromAscii( "New_Folder" );
        else
            aURL += OUString::createFromAscii( "New_Stream" );

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

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

//=========================================================================
//
// Non-interface methods.
//
//=========================================================================

// virtual
OUString Content::getParentURL()
{
    return m_aUri.getParentUri();
}

//=========================================================================
// static
Reference< XRow > Content::getPropertyValues(
                const Reference< XMultiServiceFactory >& rSMgr,
                const Sequence< Property >& rProperties,
                ContentProvider* pProvider,
                const OUString& rContentId )
{
    ContentProperties aData;
    Reference< XHierarchicalNameAccess > xPackage;
    if ( loadData( pProvider, PackageUri( rContentId ), aData, xPackage ) )
    {
        return getPropertyValues( rSMgr,
                                  rProperties,
                                  aData,
                                  vos::ORef< ucb::ContentProviderImplHelper >(
                                      pProvider ),
                                  rContentId );
    }
    else
    {
        vos::ORef< ::ucb::PropertyValueSet > xRow
                                = new ::ucb::PropertyValueSet( rSMgr );

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

        return Reference< XRow >( xRow.getBodyPtr() );
    }
}

//=========================================================================
// static
Reference< XRow > Content::getPropertyValues(
                const Reference< XMultiServiceFactory >& rSMgr,
                const Sequence< Property >& rProperties,
                const ContentProperties& rData,
                const vos::ORef< ucb::ContentProviderImplHelper >& rProvider,
                const OUString& rContentId )
{
    // Note: Empty sequence means "get values of all supported properties".

    vos::ORef< ::ucb::PropertyValueSet > xRow
                                = new ::ucb::PropertyValueSet( rSMgr );

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

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

            // Process Core properties.

            if ( rProp.Name.compareToAscii( "ContentType" ) == 0 )
            {
                xRow->appendString ( rProp, rData.aContentType );
            }
            else if ( rProp.Name.compareToAscii( "Title" ) == 0 )
            {
                xRow->appendString ( rProp, rData.aTitle );
            }
            else if ( rProp.Name.compareToAscii( "IsDocument" ) == 0 )
            {
                xRow->appendBoolean( rProp, rData.bIsDocument );
            }
            else if ( rProp.Name.compareToAscii( "IsFolder" ) == 0 )
            {
                xRow->appendBoolean( rProp, rData.bIsFolder );
            }
            else if ( rProp.Name.compareToAscii( "MediaType" ) == 0 )
            {
                xRow->appendString ( rProp, rData.aMediaType );
            }
#if SUPD>614
            else if ( rProp.Name.compareToAscii( "Size" ) == 0 )
            {
                // Property only available for streams.
                if ( rData.bIsDocument )
                    xRow->appendLong( rProp, rData.nSize );
                else
                    xRow->appendVoid( rProp );
            }
            else if ( rProp.Name.compareToAscii( "Compressed" ) == 0 )
            {
                // Property only available for streams.
                if ( rData.bIsDocument )
                    xRow->appendBoolean( rProp, rData.bCompressed );
                else
                    xRow->appendVoid( rProp );
            }
            else if ( rProp.Name.compareToAscii( "Encrypted" ) == 0 )
            {
                // Property only available for streams.
                if ( rData.bIsDocument )
                    xRow->appendBoolean( rProp, rData.bEncrypted );
                else
                    xRow->appendVoid( rProp );
            }
#else
            else if ( rProp.Name.compareToAscii( "Size" ) == 0 )
            {
                xRow->appendLong( rProp, rData.nSize );
            }
#endif
            else
            {
                // Not a Core Property! Maybe it's an Additional Core Property?!

                if ( !bTriedToGetAdditonalPropSet && !xAdditionalPropSet.is() )
                {
                    xAdditionalPropSet
                        = Reference< XPropertySet >(
                            rProvider->getAdditionalPropertySet( rContentId,
                                                                 sal_False ),
                            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 (
            Property( OUString::createFromAscii( "ContentType" ),
                      -1,
                      getCppuType( static_cast< const OUString * >( 0 ) ),
                      PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
            rData.aContentType );
        xRow->appendString (
            Property( OUString::createFromAscii( "Title" ),
                      -1,
                      getCppuType( static_cast< const OUString * >( 0 ) ),
                      PropertyAttribute::BOUND ),
            rData.aTitle );
        xRow->appendBoolean(
            Property( OUString::createFromAscii( "IsDocument" ),
                      -1,
                      getCppuBooleanType(),
                      PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
            rData.bIsDocument );
        xRow->appendBoolean(
            Property( OUString::createFromAscii( "IsFolder" ),
                      -1,
                      getCppuBooleanType(),
                      PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
            rData.bIsFolder );
        xRow->appendString (
            Property( OUString::createFromAscii( "MediaType" ),
                      -1,
                      getCppuType( static_cast< const OUString * >( 0 ) ),
                      PropertyAttribute::BOUND ),
            rData.aMediaType );
#if SUPD>614
        // Properties only available for streams.
        if ( rData.bIsDocument )
        {
            xRow->appendLong   (
                Property( OUString::createFromAscii( "Size" ),
                          -1,
                          getCppuType( static_cast< const sal_Int64 * >( 0 ) ),
                          PropertyAttribute::BOUND
                              | PropertyAttribute::READONLY ),
                rData.nSize );

            xRow->appendBoolean(
                Property( OUString::createFromAscii( "Compressed" ),
                          -1,
                          getCppuBooleanType(),
                          PropertyAttribute::BOUND ),
                rData.bCompressed );

            xRow->appendBoolean(
                Property( OUString::createFromAscii( "Encrypted" ),
                          -1,
                          getCppuBooleanType(),
                          PropertyAttribute::BOUND ),
                rData.bEncrypted );
        }
#else
        xRow->appendLong   (
            Property( OUString::createFromAscii( "Size" ),
                      -1,
                      getCppuType( static_cast< const sal_Int64 * >( 0 ) ),
                      PropertyAttribute::BOUND
                          | PropertyAttribute::READONLY ),
            rData.nSize );
#endif

        // Append all Additional Core Properties.

        Reference< XPropertySet > xSet(
            rProvider->getAdditionalPropertySet( rContentId, sal_False ),
            UNO_QUERY );
        xRow->appendPropertySet( xSet );
    }

    return Reference< XRow >( xRow.getBodyPtr() );
}

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

//=========================================================================
void Content::setPropertyValues( const Sequence< PropertyValue >& rValues )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    Sequence< PropertyChangeEvent > aChanges( rValues.getLength() );
    sal_Int32 nChanged = 0;

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

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

    Reference< XPersistentPropertySet > xAdditionalPropSet;
    sal_Bool bTriedToGetAdditonalPropSet = sal_False;
    sal_Bool bExchange = sal_False;
    sal_Bool bStore    = sal_False;
    OUString aNewTitle;

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

        if ( rValue.Name.compareToAscii( "ContentType" ) == 0 )
        {
            // Read-only property!
        }
        else if ( rValue.Name.compareToAscii( "IsDocument" ) == 0 )
        {
            // Read-only property!
        }
        else if ( rValue.Name.compareToAscii( "IsFolder" ) == 0 )
        {
            // Read-only property!
        }
        else if ( rValue.Name.compareToAscii( "Title" ) == 0 )
        {
            OUString aNewValue;
            if ( rValue.Value >>= aNewValue )
            {
                if ( aNewValue != m_aProps.aTitle )
                {
                    // modified title -> modified URL -> exchange !
                    if ( m_eState == PERSISTENT )
                        bExchange = sal_True;

                    // new value will be set later...
                    aNewTitle = aNewValue;
                }
            }
        }
        else if ( rValue.Name.compareToAscii( "MediaType" ) == 0 )
        {
            OUString aNewValue;
            if ( rValue.Value >>= aNewValue )
            {
                if ( aNewValue != m_aProps.aMediaType )
                {
                    aEvent.PropertyName = rValue.Name;
                    aEvent.OldValue     = makeAny( m_aProps.aMediaType );
                    aEvent.NewValue     = makeAny( aNewValue );

                    m_aProps.aMediaType = aNewValue;
                      nChanged++;
                    bStore = sal_True;
                    m_nModifiedProps |= MEDIATYPE_MODIFIED;
                }
            }
        }
        else if ( rValue.Name.compareToAscii( "Size" ) == 0 )
        {
            // Read-only property!
        }
#if SUPD>614
        else if ( rValue.Name.compareToAscii( "Compressed" ) == 0 )
        {
            // Property only available for streams.
            if ( m_aProps.bIsDocument )
            {
                sal_Bool bNewValue;
                if ( rValue.Value >>= bNewValue )
                {
                    if ( bNewValue != m_aProps.bCompressed )
                    {
                        aEvent.PropertyName = rValue.Name;
                        aEvent.OldValue     = makeAny( m_aProps.bCompressed );
                        aEvent.NewValue     = makeAny( bNewValue );

                        m_aProps.bCompressed = bNewValue;
                          nChanged++;
                        bStore = sal_True;
                        m_nModifiedProps |= COMPRESSED_MODIFIED;
                    }
                }
            }
        }
        else if ( rValue.Name.compareToAscii( "Encrypted" ) == 0 )
        {
            // Property only available for streams.
            if ( m_aProps.bIsDocument )
            {
                sal_Bool bNewValue;
                if ( rValue.Value >>= bNewValue )
                {
                    if ( bNewValue != m_aProps.bEncrypted )
                    {
                        aEvent.PropertyName = rValue.Name;
                        aEvent.OldValue     = makeAny( m_aProps.bEncrypted );
                        aEvent.NewValue     = makeAny( bNewValue );

                        m_aProps.bEncrypted = bNewValue;
                          nChanged++;
                        bStore = sal_True;
                        m_nModifiedProps |= ENCRYPTED_MODIFIED;
                    }
                }
            }
        }
        else if ( rValue.Name.compareToAscii( "EncryptionKey" ) == 0 )
        {
            // @@@ This is a temporary solution. In the future submitting
            //     the key should be done using an interaction handler!

            // Write-Only property. Only supported by root folder ( all
            // streams of a package have the same encryption key ).
            if ( m_aUri.getPath().compareToAscii( "/" ) == 0 )
            {
                Reference< XPropertySet > xPropSet( getPackage(), UNO_QUERY );

                OSL_ENSURE( xPropSet.is(),
                            "Content::setPropertyValues - "
                            "Got no XPropertySet interface from package!" );

                if ( xPropSet.is() )
                {
                    try
                    {
                        xPropSet->setPropertyValue(
                                OUString::createFromAscii( "EncryptionKey" ),
                                rValue.Value );
                    }
                    catch ( UnknownPropertyException & )
                    {
                        // setPropertyValue
                    }
                    catch ( PropertyVetoException & )
                    {
                        // setPropertyValue
                    }
                    catch ( IllegalArgumentException & )
                    {
                        // setPropertyValue
                    }
                    catch ( WrappedTargetException & )
                    {
                        // setPropertyValue
                    }
                }
            }
        }
#endif
        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
                {
                    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 ( UnknownPropertyException )
                {
                }
                catch ( WrappedTargetException )
                {
                }
                catch ( PropertyVetoException )
                {
                }
                catch ( IllegalArgumentException )
                {
                }
            }
        }
    }

    if ( bExchange )
    {
        Reference< XContentIdentifier > xOldId = m_xIdentifier;

        // Assemble new content identifier...
        OUString aNewURL = m_aUri.getParentUri();
        aNewURL += OUString::createFromAscii( "/" );
        aNewURL += aNewTitle;
        Reference< XContentIdentifier > xNewId
                        = new ::ucb::ContentIdentifier( m_xSMgr, aNewURL );

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

            // Adapt Additional Core Properties.
            renameAdditionalPropertySet(
                            xOldId->getContentIdentifier(),
                            xNewId->getContentIdentifier(),
                            sal_True );
        }
        else
        {
            // Do not set new title!
            aNewTitle = OUString();
        }
    }

    if ( aNewTitle.getLength() )
    {
        aEvent.PropertyName = OUString::createFromAscii( "Title" );
        aEvent.OldValue     = makeAny( m_aProps.aTitle );
        aEvent.NewValue     = makeAny( aNewTitle );

        m_aProps.aTitle = aNewTitle;

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

    if ( nChanged > 0 )
    {
        // Save changes, if content was already made persistent.
        if ( bStore && ( m_eState == PERSISTENT  ) )
            storeData( Reference< XInputStream >() );

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

//=========================================================================
Any Content::open( const OpenCommandArgument2& rArg,
                   const Reference< XCommandEnvironment >& xEnv )
    throw( CommandAbortedException )
{
      if ( isFolder() )
    {
        //////////////////////////////////////////////////////////////////
        // open command for a folder content
        //////////////////////////////////////////////////////////////////

        Reference< XDynamicResultSet > xSet
                    = new DynamicResultSet( m_xSMgr, this, rArg, xEnv );
        Any aRet;
        aRet <<= xSet;
        return aRet;
      }
      else
    {
        //////////////////////////////////////////////////////////////////
        // open command for a document content
        //////////////////////////////////////////////////////////////////

        if ( ( rArg.Mode == OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
             ( rArg.Mode == OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
        {
            // Currently(?) unsupported.
              throw CommandAbortedException();
        }

        OUString aURL = m_xIdentifier->getContentIdentifier();
        Reference< XOutputStream > xOut
              = Reference< XOutputStream >( rArg.Sink, UNO_QUERY );
        if ( xOut.is() )
          {
            // PUSH: write data into xOut

               Reference< XInputStream > xIn = getInputStream();
            if ( !xIn.is() )
                  throw CommandAbortedException();

            try
            {
                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 ( NotConnectedException & )
            {
                // closeOutput, readSomeBytes, writeBytes
            }
            catch ( BufferSizeExceededException & )
            {
                // closeOutput, readSomeBytes, writeBytes
            }
            catch ( IOException & )
            {
                // closeOutput, readSomeBytes, writeBytes
            }
          }
        else
          {
            Reference< XActiveDataSink > xDataSink
                  = Reference< XActiveDataSink >(   rArg.Sink, UNO_QUERY );
              if ( xDataSink.is() )
            {
                  // PULL: wait for client read

                Reference< XInputStream > xIn = getInputStream();
                if ( !xIn.is() )
                      throw CommandAbortedException();

                // Done.
                xDataSink->setInputStream( xIn );
            }
              else
            {
                  VOS_ENSURE( sal_False,
                              "Content::execute - invalid parameter!" );
                  throw CommandAbortedException();
            }
          }
    }

    return Any();
}

//=========================================================================
void Content::insert(
        const Reference< XInputStream >& xStream, sal_Int32 nNameClashResolve )
    throw( CommandAbortedException )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

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

        if ( !m_aProps.aTitle.getLength() )
            m_aProps.aTitle = m_aUri.getName();
    }
    else
    {
        // Required: rArg.Data

        if ( !xStream.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::insert - input stream missing!" );
            throw CommandAbortedException();
        }

        // Required: Title

        if ( !m_aProps.aTitle.getLength() )
            m_aProps.aTitle = m_aUri.getName();
    }

    OUString aNewURL = m_aUri.getParentUri();
    aNewURL += OUString::createFromAscii( "/" );
    aNewURL += m_aProps.aTitle;
    PackageUri aNewUri( aNewURL );

    // Handle possible name clash...
    switch ( nNameClashResolve )
    {
        // fail.
        case NameClash::ERROR:
            if ( hasData( aNewUri ) )
                throw CommandAbortedException();

            break;

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

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

                do
                {
                    OUString aNew = aNewUri.getUri();
                    aNew += OUString::createFromAscii( "_" );
                    aNew += OUString::valueOf( ++nTry );
                    aNewUri.setUri( aNew );
                }
                while ( hasData( aNewUri ) && ( nTry < 100000 ) );

                if ( nTry == 100000 )
                {
                    VOS_ENSURE( sal_False,
                                "Content::insert - "
                                "Unable to resolve name clash" );
                    throw CommandAbortedException();
                }
                else
                {
                    m_aProps.aTitle += OUString::createFromAscii( "_" );
                    m_aProps.aTitle += OUString::valueOf( nTry );
                }
            }
            break;

        // keep existing sub-objects, transfer non-clashing sub-objects.
        case NameClash::KEEP:
            // @@@

        default:
            throw CommandAbortedException();
    }

    m_xIdentifier = new ::ucb::ContentIdentifier( m_xSMgr, aNewURL );
    m_aUri = aNewUri;

    storeData( xStream );

    if ( m_eState == TRANSIENT )
    {
        m_eState = PERSISTENT;

        // Take over correct default values from underlying packager...
        loadData( m_pProvider,
                  m_aUri,
                  m_aProps,
                  Reference< XHierarchicalNameAccess >() );

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

//=========================================================================
void Content::destroy( sal_Bool bDeletePhysical )
    throw( CommandAbortedException )
{
    // @@@ take care about bDeletePhysical -> trashcan support

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

    Reference< XContent > xThis = this;

    // Persistent?
    if ( m_eState != PERSISTENT )
    {
        VOS_ENSURE( sal_False, "Content::destroy - Not persistent!" );
        throw CommandAbortedException();
    }

    m_eState = DEAD;

    aGuard.clear();
    deleted();

    if ( isFolder() )
    {
        // 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 );
            ++it;
        }
    }
}

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

    // Persistent?
    if ( m_eState != PERSISTENT )
    {
        VOS_ENSURE( sal_False, "Content::transfer - Not persistent!" );
        throw CommandAbortedException();
    }

    if ( rInfo.SourceURL.getLength() == 0 )
        throw CommandAbortedException();

    // Is source a hierarchy content?
    if ( rInfo.SourceURL.compareToAscii(
            PACKAGE_URL_SCHEME "://", PACKAGE_URL_SCHEME_LENGTH + 3 ) != 0 )
        throw InteractiveBadTransferURLException();

    // Is source not a parent of me / not me?
    OUString aId = m_aUri.getParentUri();
    aId += OUString::createFromAscii( "/" );

    if ( rInfo.SourceURL.getLength() <= aId.getLength() )
    {
        if ( aId.compareTo(
                rInfo.SourceURL, rInfo.SourceURL.getLength() ) == 0 )
            throw CommandAbortedException();
    }

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

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

        // Note: The static cast is okay here, because its sure that
        //       m_xProvider is always the PackageContentProvider.
        vos::ORef< Content > xSource
            = static_cast< Content * >(
                m_xProvider->queryContent( xId ).get() );
        if ( !xSource.isValid() )
            throw CommandAbortedException();

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

        OUString aType = xSource->isFolder()
                       ? OUString::createFromAscii(
                               PACKAGE_FOLDER_CONTENT_TYPE )
                       : OUString::createFromAscii(
                               PACKAGE_STREAM_CONTENT_TYPE );
        ContentInfo aInfo;
        aInfo.Type = aType;
        aInfo.Attributes = 0;

        // Note: The static cast is okay here, because its sure that
        //       createNewContent always creates a Content.
        vos::ORef< Content > xTarget = static_cast< Content * >(
                                            createNewContent( aInfo ).get() );
        if ( !xTarget.isValid() )
            throw CommandAbortedException();

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

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

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

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

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

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

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

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

                rValue.State = PropertyState_DIRECT_VALUE;

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

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

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

        xTarget->insert( xSource->getInputStream(), rInfo.NameClash );

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

        if ( xSource->isFolder() )
        {
            Reference< XEnumeration > xIter = xSource->getIterator();
            if ( xIter.is() )
            {
                while ( xIter->hasMoreElements() )
                {
                    try
                    {
                        Reference< XNamed > xNamed;
                        xIter->nextElement() >>= xNamed;

                        if ( !xNamed.is() )
                        {
                            VOS_ENSURE( sal_False,
                                        "Content::transfer - Got no XNamed!" );
                            break;
                        }

                        OUString aName = xNamed->getName();

                        if ( !aName.getLength() )
                        {
                            VOS_ENSURE( sal_False,
                                        "Content::transfer - Empty name!" );
                            break;
                        }

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

                        aChildId += aName;

                        Reference< XContentIdentifier > xChildId
                            = new ::ucb::ContentIdentifier( m_xSMgr, aChildId );

                        vos::ORef< Content > xChild
                            = static_cast< Content * >(
                                m_xProvider->queryContent( xChildId ).get() );

                        TransferInfo aInfo;
                        aInfo.MoveData  = sal_False;
                        aInfo.NewTitle  = OUString();
                        aInfo.SourceURL = aChildId;
                        aInfo.NameClash = rInfo.NameClash;

                        // Transfer child to target.
                        xTarget->transfer( aInfo, xEnv );
                    }
                    catch ( NoSuchElementException & )
                    {
                    }
                    catch ( WrappedTargetException & )
                    {
                    }
                }
            }
        }

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

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

            // Remove all persistent data of source and its children.
            xSource->removeData();

            // Remove own and all children's Additional Core Properties.
            xSource->removeAdditionalPropertySet( sal_True );
        }
    }
    catch ( IllegalIdentifierException & )
    {
        // queryContent
        VOS_ENSURE( sal_False, "Content::transfer - "
                               "Caught IllegalIdentifierException!" );
        throw CommandAbortedException();
    }
}

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

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

    Reference< XContent > xThis = this;

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

    // Exchange own identitity.

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

        aGuard.clear();
        if ( exchange( xNewId ) )
        {
            m_aUri = aNewUri;
            if ( isFolder() )
            {
                // 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...
                    Reference< XContentIdentifier > xOldChildId
                                                    = xChild->getIdentifier();
                    OUString aOldChildURL = xOldChildId->getContentIdentifier();
                    OUString aNewChildURL
                        = aOldChildURL.replaceAt(
                                        0,
                                        aOldURL.getLength(),
                                        xNewId->getContentIdentifier() );
                    Reference< XContentIdentifier > xNewChildId
                        = new ::ucb::ContentIdentifier( m_xSMgr, aNewChildURL );

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

                    ++it;
                }
            }
            return sal_True;
        }
    }

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

//=========================================================================
void Content::queryChildren( ContentRefList& rChildren )
{
    // Obtain a list with a snapshot of all currently instanciated contents
    // from provider and extract the contents which are direct children
    // of this content.

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

    OUString aURL = m_xIdentifier->getContentIdentifier();

    VOS_ENSURE( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ),
                "Content::queryChildren - Invalid URL!" );

    aURL += OUString::createFromAscii( "/" );

    sal_Int32 nLen = aURL.getLength();

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

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

        // Is aURL a prefix of aChildURL?
        if ( ( aChildURL.getLength() > nLen ) &&
             ( aChildURL.compareTo( aURL, nLen ) == 0 ) )
        {
            if ( aChildURL.indexOf( '/', nLen ) == -1 )
            {
                // No further slashes. It's a child!
                rChildren.push_back(
                    ContentRef(
                        static_cast< Content * >( xChild.getBodyPtr() ) ) );
            }
        }
        ++it;
    }
}

//=========================================================================
Reference< XHierarchicalNameAccess > Content::getPackage(
                                                const PackageUri& rURI )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    Reference< XHierarchicalNameAccess > xPackage;
    if ( rURI.getPackage() == m_aUri.getPackage() )
    {
        if ( !m_xPackage.is() )
            m_xPackage = m_pProvider->createPackage( m_aUri.getPackage() );

        return m_xPackage;
    }

    return m_pProvider->createPackage( rURI.getPackage() );
}

//=========================================================================
Reference< XHierarchicalNameAccess > Content::getPackage()
{
    return getPackage( m_aUri );
}

//=========================================================================
// static
sal_Bool Content::hasData( ContentProvider* pProvider,
                           const PackageUri& rURI,
                           Reference< XHierarchicalNameAccess > & rxPackage )
{
    rxPackage = pProvider->createPackage( rURI.getPackage() );
    if ( !rxPackage.is() )
        return sal_False;

    return rxPackage->hasByHierarchicalName( rURI.getPath() );
}

//=========================================================================
sal_Bool Content::hasData( const PackageUri& rURI )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    Reference< XHierarchicalNameAccess > xPackage;
    if ( rURI.getPackage() == m_aUri.getPackage() )
    {
        xPackage = getPackage();
        if ( !xPackage.is() )
            return sal_False;

        return xPackage->hasByHierarchicalName( rURI.getPath() );
    }

    return hasData( m_pProvider, rURI, xPackage );
}

//=========================================================================
//static
sal_Bool Content::loadData( ContentProvider* pProvider,
                            const PackageUri& rURI,
                            ContentProperties& rProps,
                            Reference< XHierarchicalNameAccess > & rxPackage )
{
    rxPackage = pProvider->createPackage( rURI.getPackage() );
    if ( !rxPackage.is() )
        return sal_False;

    if ( !rxPackage->hasByHierarchicalName( rURI.getPath() ) )
        return sal_False;

    try
    {
        Any aEntry = rxPackage->getByHierarchicalName( rURI.getPath() );
        if ( aEntry.hasValue() )
        {
            Reference< XPropertySet > xPropSet;
            aEntry >>= xPropSet;

            if ( !xPropSet.is() )
            {
                VOS_ENSURE( sal_False,
                        "Content::loadData - Got no XPropertySet interface!" );
                return sal_False;
            }

            // Title
            rProps.aTitle = rURI.getName();

            // MediaType
            try
            {
                Any aMediaType
                    = xPropSet->getPropertyValue(
                        OUString::createFromAscii( "MediaType" ) );
                if ( !( aMediaType >>= rProps.aMediaType ) )
                {
                    VOS_ENSURE( sal_False,
                                "Content::loadData - Got no MediaType value!" );
                    return sal_False;
                }
            }
            catch ( UnknownPropertyException & )
            {
                VOS_ENSURE( sal_False,
                            "Content::loadData - Got no MediaType value!" );
                return sal_False;
            }
            catch ( WrappedTargetException & )
            {
                VOS_ENSURE( sal_False,
                            "Content::loadData - Got no MediaType value!" );
                return sal_False;
            }

            Reference< XEnumerationAccess > xEnumAccess;
            aEntry >>= xEnumAccess;

            // ContentType / IsFolder / IsDocument
            if ( xEnumAccess.is() )
            {
                // folder
                rProps.aContentType
                    = OUString::createFromAscii( PACKAGE_FOLDER_CONTENT_TYPE );
                rProps.bIsDocument = sal_False;
                rProps.bIsFolder = sal_True;
            }
            else
            {
                // stream
                rProps.aContentType
                    = OUString::createFromAscii( PACKAGE_STREAM_CONTENT_TYPE );
                rProps.bIsDocument = sal_True;
                rProps.bIsFolder = sal_False;
            }

#if SUPD>614
            if ( rProps.bIsDocument )
            {
                // Size ( only available for streams )
                try
                {
                    Any aSize
                        = xPropSet->getPropertyValue(
                            OUString::createFromAscii( "Size" ) );
                    if ( !( aSize >>= rProps.nSize ) )
                    {
                        VOS_ENSURE( sal_False,
                                    "Content::loadData - Got no Size value!" );
                        return sal_False;
                    }
                }
                catch ( UnknownPropertyException & )
                {
                    VOS_ENSURE( sal_False,
                                "Content::loadData - Got no Size value!" );
                    return sal_False;
                }
                catch ( WrappedTargetException & )
                {
                    VOS_ENSURE( sal_False,
                                "Content::loadData - Got no Size value!" );
                    return sal_False;
                }

                // Compressed ( only available for streams )
                try
                {
                    Any aCompressed
                        = xPropSet->getPropertyValue(
                            OUString::createFromAscii( "Compressed" ) );
                    if ( !( aCompressed >>= rProps.bCompressed ) )
                    {
                        VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Compressed value!" );
                        return sal_False;
                    }
                }
                catch ( UnknownPropertyException & )
                {
                    VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Compressed value!" );
                    return sal_False;
                }
                catch ( WrappedTargetException & )
                {
                    VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Compressed value!" );
                    return sal_False;
                }

                // Encrypted ( only available for streams )
                try
                {
                    Any aEncrypted
                        = xPropSet->getPropertyValue(
                            OUString::createFromAscii( "Encrypted" ) );
                    if ( !( aEncrypted >>= rProps.bEncrypted ) )
                    {
                        VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Encrypted value!" );
                        return sal_False;
                    }
                }
                catch ( UnknownPropertyException & )
                {
                    VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Encrypted value!" );
                    return sal_False;
                }
                catch ( WrappedTargetException & )
                {
                    VOS_ENSURE( sal_False,
                            "Content::loadData - Got no Encrypted value!" );
                    return sal_False;
                }
            }
#else
            // Size
              try
              {
                  Any aSize
                      = xPropSet->getPropertyValue(
                          OUString::createFromAscii( "Size" ) );
                  if ( !( aSize >>= rProps.nSize ) )
                  {
                      VOS_ENSURE( sal_False,
                                  "Content::loadData - Got no Size value!" );
                      return sal_False;
                  }
              }
              catch ( UnknownPropertyException & )
              {
                  VOS_ENSURE( sal_False,
                              "Content::loadData - Got no Size value!" );
                  return sal_False;
              }
              catch ( WrappedTargetException & )
              {
                  VOS_ENSURE( sal_False,
                              "Content::loadData - Got no Size value!" );
                  return sal_False;
              }
#endif
            return sal_True;
        }
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName
    }

    return sal_False;
}

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

    PackageUri aURI( xOldId->getContentIdentifier() );
    Reference< XHierarchicalNameAccess > xNA = getPackage( aURI );
    if ( !xNA.is() )
        return sal_False;

    if ( !xNA->hasByHierarchicalName( aURI.getPath() ) )
        return sal_False;

    try
    {
        Any aEntry = xNA->getByHierarchicalName( aURI.getPath() );
        Reference< XNamed > xNamed;
        aEntry >>= xNamed;

        if ( !xNamed.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::renameData - Got no XNamed interface!" );
            return sal_False;
        }

        PackageUri aNewURI( xNewId->getContentIdentifier() );

        // No success indicator!? No return value / exceptions specified.
        xNamed->setName( aNewURI.getName() );

        return sal_True;
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName
    }

    return sal_False;
}

//=========================================================================
sal_Bool Content::storeData( const Reference< XInputStream >& xStream )
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    Reference< XHierarchicalNameAccess > xNA = getPackage();
    if ( !xNA.is() )
        return sal_False;

    if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) )
    {
//      if ( !bCreate )
//          return sal_True;

        try
        {
            // Create new resource...
            Reference< XSingleServiceFactory > xFac( xNA, UNO_QUERY );
            if ( !xFac.is() )
            {
                VOS_ENSURE( sal_False,
                            "Content::storeData - "
                            "Got no XSingleServiceFactory interface!" );
                return sal_False;
            }

            Sequence< Any > aArgs( 1 );
            aArgs[ 0 ] <<= isFolder();

            Reference< XInterface > xNew
                = xFac->createInstanceWithArguments( aArgs );

            if ( !xNew.is() )
            {
                VOS_ENSURE( sal_False,
                            "Content::storeData - createInstance failed!" );
                return sal_False;
            }

            PackageUri aParentUri( getParentURL() );
            Any aEntry = xNA->getByHierarchicalName( aParentUri.getPath() );
            Reference< XNameContainer > xParentContainer;
            aEntry >>= xParentContainer;

            if ( !xParentContainer.is() )
            {
                VOS_ENSURE( sal_False,
                            "Content::storeData - "
                            "Got no XNameContainer interface!" );
                return sal_False;
            }

            xParentContainer->insertByName( m_aProps.aTitle, makeAny( xNew ) );
        }
        catch ( RuntimeException & )
        {
            throw;
        }
        catch ( IllegalArgumentException & )
        {
            // insertByName
            VOS_ENSURE( sal_False,
                        "Content::storeData - insertByName failed!" );
            return sal_False;
        }
        catch ( ElementExistException & )
        {
            // insertByName
            VOS_ENSURE( sal_False,
                        "Content::storeData - insertByName failed!" );
            return sal_False;
        }
        catch ( WrappedTargetException & )
        {
            // insertByName
            VOS_ENSURE( sal_False,
                        "Content::storeData - insertByName failed!" );
            return sal_False;
        }
        catch ( NoSuchElementException & )
        {
            // getByHierarchicalName
            VOS_ENSURE( sal_False,
                        "Content::storeData - getByHierarchicalName failed!" );
            return sal_False;
        }
        catch ( Exception & )
        {
            // createInstanceWithArguments
            VOS_ENSURE( sal_False, "Content::storeData - Error!" );
            return sal_False;
        }
    }

    if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) )
        return sal_False;

    try
    {
        Reference< XPropertySet > xPropSet;
        xNA->getByHierarchicalName( m_aUri.getPath() ) >>= xPropSet;

        if ( !xPropSet.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::storeData - Got no XPropertySet interface!" );
            return sal_False;
        }

        //////////////////////////////////////////////////////////////////
        // Store property values...
        //////////////////////////////////////////////////////////////////

        if ( m_nModifiedProps & MEDIATYPE_MODIFIED )
        {
            xPropSet->setPropertyValue(
                                OUString::createFromAscii( "MediaType" ),
                                makeAny( m_aProps.aMediaType ) );
            m_nModifiedProps &= ~MEDIATYPE_MODIFIED;
        }

#if SUPD>614
        if ( m_nModifiedProps & COMPRESSED_MODIFIED )
        {
            if ( !isFolder() )
                xPropSet->setPropertyValue(
                                OUString::createFromAscii( "Compressed" ),
                                makeAny( m_aProps.bCompressed ) );

            m_nModifiedProps &= ~COMPRESSED_MODIFIED;
        }

        if ( m_nModifiedProps & ENCRYPTED_MODIFIED )
        {
            if ( !isFolder() )
                xPropSet->setPropertyValue(
                                OUString::createFromAscii( "Encrypted" ),
                                makeAny( m_aProps.bEncrypted ) );

            m_nModifiedProps &= ~ENCRYPTED_MODIFIED;
        }
#endif

        //////////////////////////////////////////////////////////////////
        // Store data stream...
        //////////////////////////////////////////////////////////////////

        if ( xStream.is() && !isFolder() )
        {
            Reference< XActiveDataSink > xSink( xPropSet, UNO_QUERY );

            if ( !xSink.is() )
            {
                VOS_ENSURE( sal_False,
                            "Content::storeData - "
                            "Got no XActiveDataSink interface!" );
                return sal_False;
            }

            xSink->setInputStream( xStream );
        }

        return sal_True;
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName
    }
    catch ( UnknownPropertyException & )
    {
        // setPropertyValue
    }
    catch ( PropertyVetoException & )
    {
        // setPropertyValue
    }
    catch ( IllegalArgumentException & )
    {
        // setPropertyValue
    }
    catch ( WrappedTargetException & )
    {
        // setPropertyValue
    }

    VOS_ENSURE( sal_False, "Content::storeData - Error!" );
    return sal_False;
}

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

    Reference< XHierarchicalNameAccess > xNA = getPackage();
    if ( !xNA.is() )
        return sal_False;

    PackageUri aParentUri( getParentURL() );
    if ( !xNA->hasByHierarchicalName( aParentUri.getPath() ) )
        return sal_False;

    try
    {
        Any aEntry = xNA->getByHierarchicalName( aParentUri.getPath() );
        Reference< XNameContainer > xContainer;
        aEntry >>= xContainer;

        if ( !xContainer.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::removeData - "
                        "Got no XNameContainer interface!" );
            return sal_False;
        }

        xContainer->removeByName( m_aUri.getName() );
        return sal_True;
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName, removeByName
    }
    catch ( WrappedTargetException & )
    {
        // removeByName
    }

    VOS_ENSURE( sal_False, "Content::removeData - Error!" );
    return sal_False;
}

//=========================================================================
sal_Bool Content::flushData()
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    // Note: XChangesBatch is only implemented by the package itself, not
    //       by the single entries. Maybe this has to change...

    Reference< XHierarchicalNameAccess > xNA = getPackage();
    if ( !xNA.is() )
        return sal_False;

    Reference< XChangesBatch > xBatch( xNA, UNO_QUERY );
    if ( !xBatch.is() )
    {
        VOS_ENSURE( sal_False,
                    "Content::flushData - Got no XChangesBatch interface!" );
        return sal_False;
    }

    try
    {
        xBatch->commitChanges();
        return sal_True;
    }
    catch ( WrappedTargetException & )
    {
    }

    VOS_ENSURE( sal_False, "Content::flushData - Error!" );
    return sal_False;
}

//=========================================================================
Reference< XInputStream > Content::getInputStream()
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    Reference< XInputStream > xStream;
    Reference< XHierarchicalNameAccess > xNA = getPackage();
    if ( !xNA.is() )
        return xStream;

    if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) )
        return xStream;

    try
    {
        Any aEntry = xNA->getByHierarchicalName( m_aUri.getPath() );
        Reference< XActiveDataSink > xSink;
        aEntry >>= xSink;

        if ( !xSink.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::getInputStream - "
                        "Got no XActiveDataSink interface!" );
            return xStream;
        }

        xStream = xSink->getInputStream();

        VOS_ENSURE( xStream.is(),
                    "Content::getInputStream - Got no stream!" );
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName
    }

    return xStream;
}

//=========================================================================
Reference< XEnumeration > Content::getIterator()
{
    osl::Guard< osl::Mutex > aGuard( m_aMutex );

    Reference< XEnumeration > xIter;
    Reference< XHierarchicalNameAccess > xNA = getPackage();
    if ( !xNA.is() )
        return xIter;

    if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) )
        return xIter;

    try
    {
        Any aEntry = xNA->getByHierarchicalName( m_aUri.getPath() );
        Reference< XEnumerationAccess > xIterFac;
        aEntry >>= xIterFac;

        if ( !xIterFac.is() )
        {
            VOS_ENSURE( sal_False,
                        "Content::getIterator - "
                        "Got no XEnumerationAccess interface!" );
            return xIter;
        }

        xIter = xIterFac->createEnumeration();

        VOS_ENSURE( xIter.is(),
                    "Content::getIterator - Got no iterator!" );
    }
    catch ( NoSuchElementException & )
    {
        // getByHierarchicalName
    }

    return xIter;
}