diff options
Diffstat (limited to 'ucb/source/core/ucbcmds.cxx')
-rw-r--r-- | ucb/source/core/ucbcmds.cxx | 2090 |
1 files changed, 2090 insertions, 0 deletions
diff --git a/ucb/source/core/ucbcmds.cxx b/ucb/source/core/ucbcmds.cxx new file mode 100644 index 000000000000..fd3f0f73c772 --- /dev/null +++ b/ucb/source/core/ucbcmds.cxx @@ -0,0 +1,2090 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_ucb.hxx" + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ +#include <osl/diagnose.h> +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/CommandEnvironment.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/ContentInfoAttribute.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument.hpp> +#include <com/sun/star/ucb/InsertCommandArgument.hpp> +#include <com/sun/star/ucb/InteractiveBadTransferURLException.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/UnsupportedNameClashException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <ucbhelper/simplenameclashresolverequest.hxx> +#include "ucbcmds.hxx" +#include "ucb.hxx" + +using namespace com::sun::star; + +namespace +{ + +//========================================================================= +// +// struct TransferCommandContext. +// +//========================================================================= + +struct TransferCommandContext +{ + uno::Reference< lang::XMultiServiceFactory > xSMgr; + uno::Reference< ucb::XCommandProcessor > xProcessor; + uno::Reference< ucb::XCommandEnvironment > xEnv; + uno::Reference< ucb::XCommandEnvironment > xOrigEnv; + ucb::GlobalTransferCommandArgument aArg; + + TransferCommandContext( + const uno::Reference< lang::XMultiServiceFactory > & rxSMgr, + const uno::Reference< ucb::XCommandProcessor > & rxProcessor, + const uno::Reference< ucb::XCommandEnvironment > & rxEnv, + const uno::Reference< ucb::XCommandEnvironment > & rxOrigEnv, + const ucb::GlobalTransferCommandArgument & rArg ) + : xSMgr( rxSMgr ), xProcessor( rxProcessor ), xEnv( rxEnv ), + xOrigEnv( rxOrigEnv ), aArg( rArg ) {} +}; + +//========================================================================= +// +// class InteractionHandlerProxy. +// +//========================================================================= + +class InteractionHandlerProxy : + public cppu::WeakImplHelper1< task::XInteractionHandler > +{ + uno::Reference< task::XInteractionHandler > m_xOrig; + +public: + InteractionHandlerProxy( + const uno::Reference< task::XInteractionHandler > & xOrig ) + : m_xOrig( xOrig ) {} + + // XInteractionHandler methods. + virtual void SAL_CALL handle( + const uno::Reference< task::XInteractionRequest >& Request ) + throw ( uno::RuntimeException ); +}; + +//========================================================================= +// virtual +void SAL_CALL InteractionHandlerProxy::handle( + const uno::Reference< task::XInteractionRequest >& Request ) + throw ( uno::RuntimeException ) +{ + if ( !m_xOrig.is() ) + return; + + // Filter unwanted requests by just not handling them. + uno::Any aRequest = Request->getRequest(); + + // "transfer" + ucb::InteractiveBadTransferURLException aBadTransferURLEx; + if ( aRequest >>= aBadTransferURLEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedNameClashException aUnsupportedNameClashEx; + if ( aRequest >>= aUnsupportedNameClashEx ) + { + if ( aUnsupportedNameClashEx.NameClash + != ucb::NameClash::ERROR ) + return; + } + else + { + // "insert" + ucb::NameClashException aNameClashEx; + if ( aRequest >>= aNameClashEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedCommandException aUnsupportedCommandEx; + if ( aRequest >>= aUnsupportedCommandEx ) + { + return; + } + } + } + } + + // not filtered; let the original handler do the work. + m_xOrig->handle( Request ); +} + +//========================================================================= +// +// class ActiveDataSink. +// +//========================================================================= + +class ActiveDataSink : public cppu::WeakImplHelper1< io::XActiveDataSink > +{ + uno::Reference< io::XInputStream > m_xStream; + +public: + // XActiveDataSink methods. + virtual void SAL_CALL setInputStream( + const uno::Reference< io::XInputStream >& aStream ) + throw( uno::RuntimeException ); + virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() + throw( uno::RuntimeException ); +}; + +//========================================================================= +// virtual +void SAL_CALL ActiveDataSink::setInputStream( + const uno::Reference< io::XInputStream >& aStream ) + throw( uno::RuntimeException ) +{ + m_xStream = aStream; +} + +//========================================================================= +// virtual +uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream() + throw( uno::RuntimeException ) +{ + return m_xStream; +} + +//========================================================================= +// +// class CommandProcessorInfo. +// +//========================================================================= + +class CommandProcessorInfo : + public cppu::WeakImplHelper1< ucb::XCommandInfo > +{ + uno::Sequence< ucb::CommandInfo > * m_pInfo; + +public: + CommandProcessorInfo(); + virtual ~CommandProcessorInfo(); + + // XCommandInfo methods + virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() + throw( uno::RuntimeException ); + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByName( const rtl::OUString& Name ) + throw( ucb::UnsupportedCommandException, uno::RuntimeException ); + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByHandle( sal_Int32 Handle ) + throw( ucb::UnsupportedCommandException, uno::RuntimeException ); + virtual sal_Bool SAL_CALL hasCommandByName( const rtl::OUString& Name ) + throw( uno::RuntimeException ); + virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) + throw( uno::RuntimeException ); +}; + +//========================================================================= +CommandProcessorInfo::CommandProcessorInfo() +{ + m_pInfo = new uno::Sequence< ucb::CommandInfo >( 2 ); + + (*m_pInfo)[ 0 ] + = ucb::CommandInfo( + rtl::OUString::createFromAscii( GETCOMMANDINFO_NAME ), // Name + GETCOMMANDINFO_HANDLE, // Handle + getCppuVoidType() ); // ArgType + (*m_pInfo)[ 1 ] + = ucb::CommandInfo( + rtl::OUString::createFromAscii( GLOBALTRANSFER_NAME ), // Name + GLOBALTRANSFER_HANDLE, // Handle + getCppuType( + static_cast< + ucb::GlobalTransferCommandArgument * >( 0 ) ) ); // ArgType +} + +//========================================================================= +// virtual +CommandProcessorInfo::~CommandProcessorInfo() +{ + delete m_pInfo; +} + +//========================================================================= +// virtual +uno::Sequence< ucb::CommandInfo > SAL_CALL +CommandProcessorInfo::getCommands() + throw( uno::RuntimeException ) +{ + return uno::Sequence< ucb::CommandInfo >( *m_pInfo ); +} + +//========================================================================= +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByName( const rtl::OUString& Name ) + throw( ucb::UnsupportedCommandException, uno::RuntimeException ) +{ + for ( sal_Int32 n = 0; n < m_pInfo->getLength(); ++n ) + { + if ( (*m_pInfo)[ n ].Name == Name ) + return ucb::CommandInfo( (*m_pInfo)[ n ] ); + } + + throw ucb::UnsupportedCommandException(); +} + +//========================================================================= +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle ) + throw( ucb::UnsupportedCommandException, uno::RuntimeException ) +{ + for ( sal_Int32 n = 0; n < m_pInfo->getLength(); ++n ) + { + if ( (*m_pInfo)[ n ].Handle == Handle ) + return ucb::CommandInfo( (*m_pInfo)[ n ] ); + } + + throw ucb::UnsupportedCommandException(); +} + +//========================================================================= +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName( + const rtl::OUString& Name ) + throw( uno::RuntimeException ) +{ + for ( sal_Int32 n = 0; n < m_pInfo->getLength(); ++n ) + { + if ( (*m_pInfo)[ n ].Name == Name ) + return sal_True; + } + + return sal_False; +} + +//========================================================================= +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle ) + throw( uno::RuntimeException ) +{ + for ( sal_Int32 n = 0; n < m_pInfo->getLength(); ++n ) + { + if ( (*m_pInfo)[ n ].Handle == Handle ) + return sal_True; + } + + return sal_False; +} + +//========================================================================= +//========================================================================= +//========================================================================= + +rtl::OUString createDesiredName( + const rtl::OUString & rSourceURL, const rtl::OUString & rNewTitle ) +{ + rtl::OUString aName( rNewTitle ); + if ( aName.getLength() == 0 ) + { + // calculate name using source URL + + // @@@ It's not guaranteed that slashes contained in the URL are + // actually path separators. This depends on the fact whether the + // URL is hierarchical. Only then the slashes are path separators. + // Therefore this algorithm is not guaranteed to work! But, ATM + // I don't know a better solution. It would have been better to + // have a member for the clashing name in + // UnsupportedNameClashException... + + sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' ); + bool bTrailingSlash = false; + if ( nLastSlash == rSourceURL.getLength() - 1 ) + { + nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash ); + bTrailingSlash = true; + } + + if ( nLastSlash != -1 ) + { + if ( bTrailingSlash ) + aName = rSourceURL.copy( + nLastSlash + 1, + rSourceURL.getLength() - nLastSlash - 2 ); + else + aName = rSourceURL.copy( nLastSlash + 1 ); + } + else + { + aName = rSourceURL; + } + + // query, fragment present? + sal_Int32 nPos = aName.indexOf( '?' ); + if ( nPos == -1 ) + nPos = aName.indexOf( '#' ); + + if ( nPos != -1 ) + aName = aName.copy( 0, nPos ); + } + return rtl::OUString( aName ); +} + +rtl::OUString createDesiredName( + const ucb::GlobalTransferCommandArgument & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + +rtl::OUString createDesiredName( + const ucb::TransferInfo & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + +//========================================================================= +enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN }; + +NameClashContinuation interactiveNameClashResolve( + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const rtl::OUString & rTargetURL, + const rtl::OUString & rClashingName, + /* [out] */ uno::Any & rException, + /* [out] */ rtl::OUString & rNewName ) +{ + rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest( + new ucbhelper::SimpleNameClashResolveRequest( + rTargetURL, // target folder URL + rClashingName, // clashing name + rtl::OUString(), // no proposal for new name + sal_True /* bSupportsOverwriteData */ ) ); + + rException = xRequest->getRequest(); + 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() ) + { + // Abort. + return ABORT; + } + else + { + uno::Reference< + ucb::XInteractionReplaceExistingData > + xReplace( + xSelection.get(), uno::UNO_QUERY ); + if ( xReplace.is() ) + { + // Try again: Replace existing data. + return OVERWRITE; + } + else + { + uno::Reference< + ucb::XInteractionSupplyName > + xSupplyName( + xSelection.get(), uno::UNO_QUERY ); + if ( xSupplyName.is() ) + { + // Try again: Use new name. + rNewName = xRequest->getNewName(); + return NEW_NAME; + } + else + { + OSL_ENSURE( sal_False, + "Unknown interaction continuation!" ); + return UNKNOWN; + } + } + } + } + } + } + return NOT_HANDLED; +} + +//========================================================================= +bool setTitle( + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor, + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const rtl::OUString & rNewTitle ) + throw( uno::RuntimeException ) +{ + try + { + uno::Sequence< beans::PropertyValue > aPropValues( 1 ); + aPropValues[ 0 ].Name + = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ); + aPropValues[ 0 ].Handle = -1; + aPropValues[ 0 ].Value = uno::makeAny( rNewTitle ); + + ucb::Command aSetPropsCommand( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "setPropertyValues" ) ), + -1, + uno::makeAny( aPropValues ) ); + + uno::Any aResult + = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv ); + + uno::Sequence< uno::Any > aErrors; + aResult >>= aErrors; + + OSL_ENSURE( aErrors.getLength() == 1, + "getPropertyValues return value invalid!" ); + + if ( aErrors[ 0 ].hasValue() ) + { + // error occured. + OSL_ENSURE( sal_False, "error setting Title property!" ); + return false; + } + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + return false; + } + + return true; +} + +//========================================================================= +uno::Reference< ucb::XContent > createNew( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xTarget, + sal_Bool bSourceIsFolder, + sal_Bool bSourceIsDocument, + sal_Bool bSourceIsLink ) + throw( uno::Exception ) +{ + ////////////////////////////////////////////////////////////////////// + // + // (1) Obtain creatable types from target. + // + ////////////////////////////////////////////////////////////////////// + + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorT( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessorT.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Folder")), + -1, + uno::makeAny(rContext.aArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( "Target is no XCommandProcessor!" ), + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aPropsToObtain( 1 ); + aPropsToObtain[ 0 ].Name + = rtl::OUString::createFromAscii( "CreatableContentsInfo" ); + aPropsToObtain[ 0 ].Handle + = -1; + + ucb::Command aGetPropsCommand( + rtl::OUString::createFromAscii( "getPropertyValues" ), + -1, + uno::makeAny( aPropsToObtain ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + uno::Sequence< ucb::ContentInfo > aTypesInfo; + bool bGotTypesInfo = false; + + if ( xRow.is() ) + { + uno::Any aValue = xRow->getObject( + 1, uno::Reference< container::XNameAccess >() ); + if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) ) + { + bGotTypesInfo = true; + } + } + + uno::Reference< ucb::XContentCreator > xCreator; + + if ( !bGotTypesInfo ) + { + // Second, try it using XContentCreator interface -> the "old" way (not + // providing the chance to supply an XCommandEnvironment. + + xCreator.set( xTarget, uno::UNO_QUERY ); + + if ( !xCreator.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Folder")), + -1, + uno::makeAny(rContext.aArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( "Target is no XContentCreator!" ), + rContext.xProcessor ); + // Unreachable + } + + aTypesInfo = xCreator->queryCreatableContentsInfo(); + } + + sal_Int32 nCount = aTypesInfo.getLength(); + if ( !nCount ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Folder")), + -1, + uno::makeAny(rContext.aArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( "No types creatable!" ), + rContext.xProcessor ); + // Unreachable + } + + ////////////////////////////////////////////////////////////////////// + // + // (2) Try to find a matching target type for the source object. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< ucb::XContent > xNew; + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + sal_Int32 nAttribs = aTypesInfo[ n ].Attributes; + sal_Bool bMatch = sal_False; + + if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK ) + { + // Create link + + if ( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ) + { + // Match! + bMatch = sal_True; + } + } + else if ( ( rContext.aArg.Operation + == ucb::TransferCommandOperation_COPY ) || + ( rContext.aArg.Operation + == ucb::TransferCommandOperation_MOVE ) ) + { + // Copy / Move + + // Is source a link? Create link in target folder then. + if ( bSourceIsLink ) + { + if ( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ) + { + // Match! + bMatch = sal_True; + } + } + else + { + // (not a and not b) or (a and b) + // not( a or b) or (a and b) + // + if ( ( !!bSourceIsFolder == + !!( nAttribs + & ucb::ContentInfoAttribute::KIND_FOLDER ) ) + && + ( !!bSourceIsDocument == + !!( nAttribs + & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) + ) + { + // Match! + bMatch = sal_True; + } + } + } + else + { + ucbhelper::cancelCommandExecution( + uno::makeAny( lang::IllegalArgumentException( + rtl::OUString::createFromAscii( + "Unknown transfer operation!" ), + rContext.xProcessor, + -1 ) ), + rContext.xOrigEnv ); + // Unreachable + } + + if ( bMatch ) + { + ////////////////////////////////////////////////////////////// + // + // (3) Create a new, empty object of matched type. + // + ////////////////////////////////////////////////////////////// + + if ( !xCreator.is() ) + { + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + ucb::Command aCreateNewCommand( + rtl::OUString::createFromAscii( "createNewContent" ), + -1, + uno::makeAny( aTypesInfo[ n ] ) ); + + xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv ) + >>= xNew; + } + else + { + // Second, try it using XContentCreator interface -> the "old" + // way (not providing the chance to supply an XCommandEnvironment. + + xNew = xCreator->createNewContent( aTypesInfo[ n ] ); + } + + if ( !xNew.is() ) + { + uno::Any aProps + = uno::makeAny( + beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Folder")), + -1, + uno::makeAny(rContext.aArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "createNewContent failed!" ), + rContext.xProcessor ); + // Unreachable + } + break; // escape from 'for' loop + } + } // for + + return xNew; +} + +//========================================================================= +void transferProperties( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN ) + throw( uno::Exception ) +{ + ucb::Command aGetPropertySetInfoCommand( + rtl::OUString::createFromAscii( "getPropertySetInfo" ), + -1, + uno::Any() ); + + uno::Reference< beans::XPropertySetInfo > xInfo; + xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv ) + >>= xInfo; + + if ( !xInfo.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Unable to get propertyset info from source object!" ), + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aAllProps = xInfo->getProperties(); + + ucb::Command aGetPropsCommand1( + rtl::OUString::createFromAscii( "getPropertyValues" ), + -1, + uno::makeAny( aAllProps ) ); + + uno::Reference< sdbc::XRow > xRow1; + xCommandProcessorS->execute( + aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1; + + if ( !xRow1.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Unable to get properties from source object!" ), + rContext.xProcessor ); + // Unreachable + } + + // Assemble data structure for setPropertyValues command. + + // Note: Make room for additional Title and TargetURL too. -> + 2 + uno::Sequence< beans::PropertyValue > aPropValues( + aAllProps.getLength() + 2 ); + + sal_Bool bHasTitle = ( rContext.aArg.NewTitle.getLength() == 0 ); + sal_Bool bHasTargetURL = ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ); + + sal_Int32 nWritePos = 0; + for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m ) + { + const beans::Property & rCurrProp = aAllProps[ m ]; + beans::PropertyValue & rCurrValue = aPropValues[ nWritePos ]; + + uno::Any aValue; + + if ( rCurrProp.Name.compareToAscii( "Title" ) == 0 ) + { + // Supply new title, if given. + if ( !bHasTitle ) + { + bHasTitle = sal_True; + aValue <<= rContext.aArg.NewTitle; + } + } + else if ( rCurrProp.Name.compareToAscii( "TargetURL" ) == 0 ) + { + // Supply source URL as link target for the new link to create. + if ( !bHasTargetURL ) + { + bHasTargetURL = sal_True; + aValue <<= rContext.aArg.SourceURL; + } + } + + if ( !aValue.hasValue() ) + { + try + { + aValue = xRow1->getObject( + m + 1, uno::Reference< container::XNameAccess >() ); + } + catch ( sdbc::SQLException const & ) + { + // Argh! But try to bring things to an end. Perhaps the + // mad property is not really important... + } + } + + if ( aValue.hasValue() ) + { + rCurrValue.Name = rCurrProp.Name; + rCurrValue.Handle = rCurrProp.Handle; + rCurrValue.Value = aValue; +// rCurrValue.State = + + nWritePos++; + } + } + + // Title needed, but not set yet? + if ( !bHasTitle && ( rContext.aArg.NewTitle.getLength() > 0 ) ) + { + aPropValues[ nWritePos ].Name + = rtl::OUString::createFromAscii( "Title" ); + aPropValues[ nWritePos ].Handle = -1; + aPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle; + + nWritePos++; + } + + // TargetURL needed, but not set yet? + if ( !bHasTargetURL && ( rContext.aArg.Operation + == ucb::TransferCommandOperation_LINK ) ) + { + aPropValues[ nWritePos ].Name + = rtl::OUString::createFromAscii( "TargetURL" ); + aPropValues[ nWritePos ].Handle = -1; + aPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL; + + nWritePos++; + } + + aPropValues.realloc( nWritePos ); + + // Set properties at new object. + + ucb::Command aSetPropsCommand( + rtl::OUString::createFromAscii( "setPropertyValues" ), + -1, + uno::makeAny( aPropValues ) ); + + xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv ); + + // @@@ What to do with source props that are not supported by the + // new object? addProperty ??? +} + +//========================================================================= +uno::Reference< io::XInputStream > getInputStream( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) + throw( uno::Exception ) +{ + uno::Reference< io::XInputStream > xInputStream; + + ////////////////////////////////////////////////////////////////////// + // + // (1) Try to get data as XInputStream via XActiveDataSink. + // + ////////////////////////////////////////////////////////////////////// + + try + { + uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink; + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused + + ucb::Command aOpenCommand( + rtl::OUString::createFromAscii( "open" ), + -1, + uno::makeAny( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + xInputStream = xSink->getInputStream(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + // will be handled below. + } + + if ( !xInputStream.is() ) + { + ////////////////////////////////////////////////////////////////// + // + // (2) Try to get data via XOutputStream. + // + ////////////////////////////////////////////////////////////////// + + try + { + uno::Reference< io::XOutputStream > xOutputStream( + rContext.xSMgr->createInstance( + rtl::OUString::createFromAscii( "com.sun.star.io.Pipe" ) ), + uno::UNO_QUERY ); + + if ( xOutputStream.is() ) + { + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xOutputStream; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); + + ucb::Command aOpenCommand( + rtl::OUString::createFromAscii( "open" ), + -1, + uno::makeAny( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + + xInputStream = uno::Reference< io::XInputStream >( + xOutputStream, uno::UNO_QUERY ); + } + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_ENSURE( sal_False, "unable to get input stream from document!" ); + } + } + + return xInputStream; +} + +//========================================================================= +uno::Reference< sdbc::XResultSet > getResultSet( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) + throw( uno::Exception ) +{ + uno::Reference< sdbc::XResultSet > xResultSet; + + uno::Sequence< beans::Property > aProps( 3 ); + + aProps[ 0 ].Name = rtl::OUString::createFromAscii( "IsFolder" ); + aProps[ 0 ].Handle = -1; /* unknown */ + aProps[ 1 ].Name = rtl::OUString::createFromAscii( "IsDocument" ); + aProps[ 1 ].Handle = -1; /* unknown */ + aProps[ 2 ].Name = rtl::OUString::createFromAscii( "TargetURL" ); + aProps[ 2 ].Handle = -1; /* unknown */ + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::ALL; + aArg.Priority = 0; // unused + aArg.Sink = 0; + aArg.Properties = aProps; + + ucb::Command aOpenCommand( rtl::OUString::createFromAscii( "open" ), + -1, + uno::makeAny( aArg ) ); + try + { + uno::Reference< ucb::XDynamicResultSet > xSet; + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet; + + if ( xSet.is() ) + xResultSet = xSet->getStaticResultSet(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_ENSURE( sal_False, "unable to get result set from folder!" ); + } + + return xResultSet; +} + +//========================================================================= +void handleNameClashRename( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xNew, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorN, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorS, + /* [inout] */ uno::Reference< io::XInputStream > & xInputStream ) + throw( uno::Exception ) +{ + sal_Int32 nTry = 0; + + // Obtain old title. + uno::Sequence< beans::Property > aProps( 1 ); + aProps[ 0 ].Name = rtl::OUString::createFromAscii( "Title" ); + aProps[ 0 ].Handle = -1; + + ucb::Command aGetPropsCommand( + rtl::OUString::createFromAscii( "getPropertyValues" ), + -1, + uno::makeAny( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Any aProps2 + = uno::makeAny( + beans::PropertyValue( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Uri" ) ), + -1, + uno::makeAny( + xNew->getIdentifier()->getContentIdentifier() ), + beans::PropertyState_DIRECT_VALUE ) ); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >( &aProps2, 1 ), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Unable to get properties from new object!" ), + rContext.xProcessor ); + // Unreachable + } + + rtl::OUString aOldTitle = xRow->getString( 1 ); + if ( !aOldTitle.getLength() ) + { + ucbhelper::cancelCommandExecution( + uno::makeAny( beans::UnknownPropertyException( + rtl::OUString::createFromAscii( + "Unable to get property 'Title' " + "from new object!" ), + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // Some pseudo-intelligence for not destroying file extensions. + rtl::OUString aOldTitlePre; + rtl::OUString aOldTitlePost; + sal_Int32 nPos = aOldTitle.lastIndexOf( '.' ); + if ( nPos != -1 ) + { + aOldTitlePre = aOldTitle.copy( 0, nPos ); + aOldTitlePost = aOldTitle.copy( nPos ); + } + else + aOldTitlePre = aOldTitle; + + if ( nPos > 0 ) + aOldTitlePre += rtl::OUString::createFromAscii( "_" ); + + sal_Bool bContinue = sal_True; + do + { + nTry++; + + rtl::OUString aNewTitle = aOldTitlePre; + aNewTitle += rtl::OUString::valueOf( nTry ); + aNewTitle += aOldTitlePost; + + // Set new title + setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle ); + + // Retry inserting the content. + try + { + // Previous try may have read from stream. Seek to begin (if + // optional interface XSeekable is supported) or get a new stream. + if ( xInputStream.is() ) + { + uno::Reference< io::XSeekable > xSeekable( + xInputStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + { + try + { + xSeekable->seek( 0 ); + } + catch ( lang::IllegalArgumentException const & ) + { + xInputStream.clear(); + } + catch ( io::IOException const & ) + { + xInputStream.clear(); + } + } + else + xInputStream.clear(); + + if ( !xInputStream.is() ) + { + xInputStream + = getInputStream( rContext, xCommandProcessorS ); + if ( !xInputStream.is() ) + { + uno::Any aProps2 + = uno::makeAny( + beans::PropertyValue( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( "Uri" ) ), + -1, + uno::makeAny( + xNew->getIdentifier()-> + getContentIdentifier() ), + beans::PropertyState_DIRECT_VALUE ) ); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >( &aProps2, 1 ), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Got no data stream from source!" ), + rContext.xProcessor ); + // Unreachable + } + } + } + + ucb::InsertCommandArgument aArg; + aArg.Data = xInputStream; + aArg.ReplaceExisting = sal_False; + + ucb::Command aInsertCommand( + rtl::OUString::createFromAscii( "insert" ), + -1, + uno::makeAny( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + + // Success! + bContinue = sal_False; + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + } + } + while ( bContinue && ( nTry < 50 ) ); + + if ( nTry == 50 ) + { + ucbhelper::cancelCommandExecution( + uno::makeAny( + ucb::UnsupportedNameClashException( + rtl::OUString::createFromAscii( + "Unable to resolve name clash!" ), + rContext.xProcessor, + ucb::NameClash::RENAME ) ), + rContext.xOrigEnv ); + // Unreachable + } +} + +//========================================================================= +void globalTransfer_( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xSource, + const uno::Reference< ucb::XContent > & xTarget, + const uno::Reference< sdbc::XRow > & xSourceProps ) + throw( uno::Exception ) +{ + // IsFolder: property is required. + sal_Bool bSourceIsFolder = xSourceProps->getBoolean( 1 ); + if ( !bSourceIsFolder && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::makeAny( beans::UnknownPropertyException( + rtl::OUString::createFromAscii( + "Unable to get property 'IsFolder' " + "from source object!" ), + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // IsDocument: property is required. + sal_Bool bSourceIsDocument = xSourceProps->getBoolean( 2 ); + if ( !bSourceIsDocument && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::makeAny( beans::UnknownPropertyException( + rtl::OUString::createFromAscii( + "Unable to get property 'IsDocument' " + "from source object!" ), + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // TargetURL: property is optional. + sal_Bool bSourceIsLink = ( xSourceProps->getString( 3 ).getLength() > 0 ); + + ////////////////////////////////////////////////////////////////////// + // + // (1) Try to find a matching target type for the source object and + // create a new, empty object of that type. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< ucb::XContent > xNew = createNew( rContext, + xTarget, + bSourceIsFolder, + bSourceIsDocument, + bSourceIsLink ); + if ( !xNew.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Folder")), + -1, + uno::makeAny(rContext.aArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "No matching content type at target!" ), + rContext.xProcessor ); + // Unreachable + } + + ////////////////////////////////////////////////////////////////////// + // + // (2) Transfer property values from source to new object. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorN( + xNew, uno::UNO_QUERY ); + if ( !xCommandProcessorN.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny( + xNew->getIdentifier()-> + getContentIdentifier()), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "New content is not a XCommandProcessor!" ), + rContext.xProcessor ); + // Unreachable + } + + // Obtain all properties from source. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorS( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessorS.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Source content is not a XCommandProcessor!" ), + rContext.xProcessor ); + // Unreachable + } + + transferProperties( rContext, xCommandProcessorS, xCommandProcessorN ); + + ////////////////////////////////////////////////////////////////////// + // + // (3) Try to obtain a data stream from source. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< io::XInputStream > xInputStream; + + if ( bSourceIsDocument && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xInputStream = getInputStream( rContext, xCommandProcessorS ); + + ////////////////////////////////////////////////////////////////////// + // + // (4) Try to obtain a resultset (children) from source. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< sdbc::XResultSet > xResultSet; + + if ( bSourceIsFolder && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xResultSet = getResultSet( rContext, xCommandProcessorS ); + + ////////////////////////////////////////////////////////////////////// + // + // (5) Insert (store) new content. + // + ////////////////////////////////////////////////////////////////////// + + ucb::InsertCommandArgument aArg; + aArg.Data = xInputStream; + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + aArg.ReplaceExisting = sal_True; + break; + + case ucb::NameClash::ERROR: + case ucb::NameClash::RENAME: + case ucb::NameClash::KEEP: // deprecated + case ucb::NameClash::ASK: + aArg.ReplaceExisting = sal_False; + break; + + default: + aArg.ReplaceExisting = sal_False; + OSL_ENSURE( sal_False, "Unknown nameclash directive!" ); + break; + } + + rtl::OUString aDesiredName = createDesiredName( rContext.aArg ); + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aInsertCommand( + rtl::OUString::createFromAscii( "insert" ), + -1, + uno::makeAny( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( !aArg.ReplaceExisting, + "BUG: UnsupportedNameClashException not allowed here!" ); + + if (exc.NameClash != ucb::NameClash::ERROR) { + OSL_ENSURE( false, "BUG: NameClash::ERROR expected!" ); + } + + // No chance to solve name clashes, because I'm not able to detect + // whether there is one. + throw ucb::UnsupportedNameClashException( + rtl::OUString::createFromAscii( + "Unable to resolve name clashes, no chance to detect " + "that there is one!" ), + rContext.xProcessor, + rContext.aArg.NameClash ); + } + catch ( ucb::NameClashException const & ) + { + // The 'insert' command throws a NameClashException if the parameter + // ReplaceExisting of the command's argument was set to false and + // there exists a resource with a clashing name in the target folder + // of the operation. + + // 'insert' command has no direct support for name clashes other + // than ERROR ( ReplaceExisting == false ) and OVERWRITE + // ( ReplaceExisting == true ). So we have to implement the + // other name clash handling directives on top of the content. + + // @@@ 'insert' command should be extended that it accepts a + // name clash handling directive, exactly like 'transfer' command. + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + { + ucbhelper::cancelCommandExecution( + uno::makeAny( + ucb::UnsupportedNameClashException( + rtl::OUString::createFromAscii( + "BUG: insert + replace == true MUST NOT " + "throw NameClashException." ), + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + // Unreachable + } + + case ucb::NameClash::ERROR: + throw; + + case ucb::NameClash::RENAME: + { + // "invent" a new valid title. + handleNameClashRename( rContext, + xNew, + xCommandProcessorN, + xCommandProcessorS, + xInputStream ); + break; + } + + case ucb::NameClash::ASK: + { + uno::Any aExc; + rtl::OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + rContext.xOrigEnv, // always use original environment! + rContext.aArg.TargetURL, // target folder URL + aDesiredName, + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); + // break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall-thru intended. + + case ABORT: + throw ucb::CommandFailedException( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "abort requested via interaction " + "handler" ) ), + uno::Reference< uno::XInterface >(), + aExc ); + // break; + + case OVERWRITE: + OSL_ENSURE( aArg.ReplaceExisting == sal_False, + "Hu? ReplaceExisting already true?" + ); + aArg.ReplaceExisting = sal_True; + bRetry = true; + break; + + case NEW_NAME: + { + // set new name -> set "Title" property... + if ( setTitle( xCommandProcessorN, + rContext.xEnv, + aNewTitle ) ) + { + // remember suggested title... + aDesiredName = aNewTitle; + + // ... and try again. + bRetry = true; + } + else + { + // error setting title. Abort. + throw ucb::CommandFailedException( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "error setting Title property!" + ) ), + uno::Reference< uno::XInterface >(), + aExc ); + } + break; + } + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + break; + + case ucb::NameClash::KEEP: // deprecated + default: + { + ucbhelper::cancelCommandExecution( + uno::makeAny( + ucb::UnsupportedNameClashException( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "default action, don't know how to " + "handle name clash" ) ), + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + // Unreachable + } + } + } + } + while ( bRetry ); + + ////////////////////////////////////////////////////////////////////// + // + // (6) Process children of source. + // + ////////////////////////////////////////////////////////////////////// + + if ( xResultSet.is() ) + { + try + { + // Iterate over children... + + uno::Reference< sdbc::XRow > xChildRow( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildRow.is() ) + { + uno::Any aProps + = uno::makeAny( + beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Unable to get properties from children of source!" ), + rContext.xProcessor ); + // Unreachable + } + + uno::Reference< ucb::XContentAccess > xChildAccess( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildAccess.is() ) + { + uno::Any aProps + = uno::makeAny( + beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + rtl::OUString::createFromAscii( + "Unable to get children of source!" ), + rContext.xProcessor ); + // Unreachable + } + + if ( xResultSet->first() ) + { + ucb::GlobalTransferCommandArgument aTransArg( + rContext.aArg.Operation, // Operation + rtl::OUString(), // SourceURL; filled later + xNew->getIdentifier() + ->getContentIdentifier(), // TargetURL + rtl::OUString(), // NewTitle; + rContext.aArg.NameClash ); // NameClash + + TransferCommandContext aSubCtx( + rContext.xSMgr, + rContext.xProcessor, + rContext.xEnv, + rContext.xOrigEnv, + aTransArg ); + do + { + uno::Reference< ucb::XContent > xChild + = xChildAccess->queryContent(); + if ( xChild.is() ) + { + // Recursion! + + aSubCtx.aArg.SourceURL + = xChild->getIdentifier()->getContentIdentifier(); + + globalTransfer_( aSubCtx, + xChild, + xNew, + xChildRow ); + } + } + while ( xResultSet->next() ); + } + } + catch ( sdbc::SQLException const & ) + { + } + } + + try { + uno::Reference< ucb::XCommandProcessor > xcp( + xTarget, uno::UNO_QUERY ); + + uno::Any aAny; + uno::Reference< ucb::XCommandInfo > xci; + if(xcp.is()) + aAny = + xcp->execute( + ucb::Command( + rtl::OUString::createFromAscii("getCommandInfo"), + -1, + uno::Any()), + 0, + rContext.xEnv ); + + const rtl::OUString cmdName = + rtl::OUString::createFromAscii("flush"); + if((aAny >>= xci) && xci->hasCommandByName(cmdName)) + xcp->execute( + ucb::Command( + cmdName, + -1, + uno::Any()) , + 0, + rContext.xEnv ); + } + catch( uno::Exception const & ) + { + } +} + +} /* namescpace */ + +//========================================================================= +// +// UniversalContentBroker implementation ( XCommandProcessor commands ). +// +//========================================================================= + +uno::Reference< ucb::XCommandInfo > +UniversalContentBroker::getCommandInfo() +{ + return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() ); +} + +//========================================================================= +void UniversalContentBroker::globalTransfer( + const ucb::GlobalTransferCommandArgument & rArg, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) + throw( uno::Exception ) +{ + // Use own command environment with own interaction handler intercepting + // some interaction requests that shall not be handled by the user-supplied + // interaction handler. + uno::Reference< ucb::XCommandEnvironment > xLocalEnv; + if (xEnv.is()) + { + uno::Reference< beans::XPropertySet > const xProps( + m_xSMgr, uno::UNO_QUERY_THROW ); + uno::Reference< uno::XComponentContext > xCtx; + xCtx.set( xProps->getPropertyValue( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ) ), + uno::UNO_QUERY_THROW ); + + xLocalEnv.set( ucb::CommandEnvironment::create( + xCtx, + new InteractionHandlerProxy( xEnv->getInteractionHandler() ), + xEnv->getProgressHandler() ) ); + } + + ////////////////////////////////////////////////////////////////////// + // + // (1) Try to transfer the content using 'transfer' command. + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< ucb::XContent > xTarget; + uno::Reference< ucb::XContentIdentifier > xId + = createContentIdentifier( rArg.TargetURL ); + if ( xId.is() ) + { + try + { + xTarget = queryContent( xId ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + + if ( !xTarget.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + xEnv, + rtl::OUString::createFromAscii( + "Can't instanciate target object!" ), + this ); + // Unreachable + } + + if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) || + ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) ) + { + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Any aProps + = uno::makeAny( + beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rArg.TargetURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + xEnv, + rtl::OUString::createFromAscii( + "Target content is not a XCommandProcessor!" ), + this ); + // Unreachable + } + + ucb::TransferInfo aTransferArg( + ( rArg.Operation + == ucb::TransferCommandOperation_MOVE ), // MoveData + rArg.SourceURL, // SourceURL + rArg.NewTitle, // NewTitle + rArg.NameClash ); // NameClash + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aCommand( + rtl::OUString::createFromAscii( "transfer" ), // Name + -1, // Handle + uno::makeAny( aTransferArg ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::InteractiveBadTransferURLException const & ) + { + // Source URL is not supported by target. Try to transfer + // the content "manually". + } + catch ( ucb::UnsupportedCommandException const & ) + { + // 'transfer' command is not supported by commandprocessor. + // Try to transfer manually. + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( aTransferArg.NameClash == exc.NameClash, + "nameclash mismatch!" ); + if ( exc.NameClash == ucb::NameClash::ASK ) + { + // Try to detect a name clash by invoking "transfer" with + // NameClash::ERROR. + try + { + ucb::TransferInfo aTransferArg1( + aTransferArg.MoveData, + aTransferArg.SourceURL, + aTransferArg.NewTitle, + ucb::NameClash::ERROR ); + + ucb::Command aCommand1( + rtl::OUString::createFromAscii( "transfer" ), + -1, + uno::makeAny( aTransferArg1 ) ); + + xCommandProcessor->execute( aCommand1, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::UnsupportedNameClashException const & ) + { + // No chance to solve name clashes, because I'm not + // able to detect whether there is one. + throw exc; // Not just 'throw;'! + } + catch ( ucb::NameClashException const & ) + { + // There's a clash. Use interaction handler to solve it. + + uno::Any aExc; + rtl::OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + xEnv, // always use original environment! + rArg.TargetURL, // target folder URL + createDesiredName( + aTransferArg ), // clashing name + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); +// break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall-thru intended. + + case ABORT: + throw ucb::CommandFailedException( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "abort requested via interaction " + "handler" ) ), + uno::Reference< uno::XInterface >(), + aExc ); +// break; + + case OVERWRITE: + aTransferArg.NameClash + = ucb::NameClash::OVERWRITE; + bRetry = true; + break; + + case NEW_NAME: + aTransferArg.NewTitle = aNewTitle; + bRetry = true; + break; + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + } + else + { + throw; + } + } + } + while ( bRetry ); + } + + ////////////////////////////////////////////////////////////////////// + // + // (2) Try to transfer the content "manually". + // + ////////////////////////////////////////////////////////////////////// + + uno::Reference< ucb::XContent > xSource; + try + { + uno::Reference< ucb::XContentIdentifier > xId2 + = createContentIdentifier( rArg.SourceURL ); + if ( xId2.is() ) + xSource = queryContent( xId2 ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + // Error handling via "if ( !xSource.is() )" below. + } + + if ( !xSource.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + xEnv, + rtl::OUString::createFromAscii( + "Can't instanciate source object!" ), + this ); + // Unreachable + } + + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Any aProps + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + xEnv, + rtl::OUString::createFromAscii( + "Source content is not a XCommandProcessor!" ), + this ); + // Unreachable + } + + // Obtain interesting property values from source... + + uno::Sequence< beans::Property > aProps( 4 ); + + aProps[ 0 ].Name = rtl::OUString::createFromAscii( "IsFolder" ); + aProps[ 0 ].Handle = -1; /* unknown */ + aProps[ 1 ].Name = rtl::OUString::createFromAscii( "IsDocument" ); + aProps[ 1 ].Handle = -1; /* unknown */ + aProps[ 2 ].Name = rtl::OUString::createFromAscii( "TargetURL" ); + aProps[ 2 ].Handle = -1; /* unknown */ + aProps[ 3 ].Name = rtl::OUString::createFromAscii( "BaseURI" ); + aProps[ 3 ].Handle = -1; /* unknown */ + + ucb::Command aGetPropsCommand( + rtl::OUString::createFromAscii( "getPropertyValues" ), + -1, + uno::makeAny( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Any aProps2 + = uno::makeAny(beans::PropertyValue( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Uri")), + -1, + uno::makeAny(rArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps2, 1), + xEnv, + rtl::OUString::createFromAscii( + "Unable to get properties from source object!" ), + this ); + // Unreachable + } + + TransferCommandContext aTransferCtx( + m_xSMgr, this, xLocalEnv, xEnv, rArg ); + + if ( rArg.NewTitle.getLength() == 0 ) + { + // BaseURI: property is optional. + rtl::OUString aBaseURI( xRow->getString( 4 ) ); + if ( aBaseURI.getLength() ) + { + aTransferCtx.aArg.NewTitle + = createDesiredName( aBaseURI, rtl::OUString() ); + } + } + + // Do it! + globalTransfer_( aTransferCtx, xSource, xTarget, xRow ); + + ////////////////////////////////////////////////////////////////////// + // + // (3) Delete source, if operation is MOVE. + // + ////////////////////////////////////////////////////////////////////// + + if ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) + { + try + { + ucb::Command aCommand( + rtl::OUString::createFromAscii( "delete" ), // Name + -1, // Handle + uno::makeAny( sal_Bool( sal_True ) ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + } + catch ( uno::Exception const & ) + { + OSL_ENSURE( sal_False, "Cannot delete source object!" ); + throw; + } + } +} |