From 966d20e35d5a2be2fce6c204af5c156c3ead7063 Mon Sep 17 00:00:00 2001 From: Cédric Bosdonnat Date: Tue, 29 May 2012 15:18:29 +0200 Subject: CMIS ucp: write documents back to CMIS server This change needs a git repo build of libcmis. Change-Id: I I740d18dbf3c36d9387b867e750fcbe8e114e5362 --- sfx2/source/doc/docfile.cxx | 28 +- ucb/source/ucp/cmis/cmis_content.cxx | 516 +++++++++++++++++++------ ucb/source/ucp/cmis/cmis_content.hxx | 16 +- ucb/source/ucp/cmis/cmis_provider.cxx | 31 +- ucb/source/ucp/cmis/cmis_provider.hxx | 7 + ucb/source/ucp/cmis/cmis_url.cxx | 47 ++- ucb/source/ucp/cmis/cmis_url.hxx | 18 +- ucbhelper/Library_ucbhelper.mk | 2 + ucbhelper/Package_inc.mk | 2 + ucbhelper/inc/ucbhelper/std_inputstream.hxx | 119 ++++++ ucbhelper/inc/ucbhelper/std_outputstream.hxx | 73 ++++ ucbhelper/source/provider/std_inputstream.cxx | 187 +++++++++ ucbhelper/source/provider/std_outputstream.cxx | 98 +++++ 13 files changed, 1004 insertions(+), 140 deletions(-) create mode 100644 ucbhelper/inc/ucbhelper/std_inputstream.hxx create mode 100644 ucbhelper/inc/ucbhelper/std_outputstream.hxx create mode 100644 ucbhelper/source/provider/std_inputstream.cxx create mode 100644 ucbhelper/source/provider/std_outputstream.cxx diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 27d4398da299..6271acd84493 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1918,6 +1919,7 @@ void SfxMedium::Transfer_Impl() Reference< ::com::sun::star::ucb::XProgressHandler >() ); rtl::OUString aDestURL( aDest.GetMainURL( INetURLObject::NO_DECODE ) ); + if ( ::utl::LocalFileHelper::IsLocalFile( aDestURL ) || !aDest.removeSegment() ) { TransactedTransferForFS_Impl( aSource, aDest, xComEnv ); @@ -1939,8 +1941,30 @@ void SfxMedium::Transfer_Impl() ::ucbhelper::Content aSourceContent; ::ucbhelper::Content aTransferContent; - String aFileName = GetLongName(); - if ( !aFileName.Len() ) + // Get the parent URL from the XChild if possible: why would the URL necessarily have + // a hierarchical path? It's not the case for CMIS. + ::ucbhelper::Content aDestContent; + ::ucbhelper::Content::create( aDestURL, xComEnv, aDestContent ); + Reference< ::com::sun::star::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY ); + rtl::OUString sParentUrl; + if ( xChild.is( ) ) + { + Reference< ::com::sun::star::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY ); + if ( xParent.is( ) ) + { + sParentUrl = xParent->getIdentifier( )->getContentIdentifier(); + } + } + + if ( !sParentUrl.isEmpty() ) + aDest = INetURLObject( sParentUrl ); + + // LongName wasn't defined anywhere, only used here... get the Title instead + // as it's less probably empty + rtl::OUString aFileName; + Any aAny = aDestContent.getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Title" )) ); + aAny >>= aFileName; + if ( aFileName.isEmpty() ) aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET ); try diff --git a/ucb/source/ucp/cmis/cmis_content.cxx b/ucb/source/ucp/cmis/cmis_content.cxx index 8c12f93157b8..ead9f2da4ab0 100644 --- a/ucb/source/ucp/cmis/cmis_content.cxx +++ b/ucb/source/ucp/cmis/cmis_content.cxx @@ -32,10 +32,19 @@ #include #include #include +#include +#include +#include +#include #include #include +#include +#include +#include +#include #include #include +#include #include #include #include @@ -44,17 +53,80 @@ #include #include -#include +#include +#include #include +#include #include "cmis_content.hxx" #include "cmis_provider.hxx" -#include "cmis_url.hxx" +#define OUSTR_TO_STDSTR(s) string( rtl::OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() ) using namespace com::sun::star; using namespace std; +namespace +{ + class AuthProvider : public libcmis::AuthProvider + { + const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment>& m_xEnv; + rtl::OUString m_sUrl; + rtl::OUString m_sBindingUrl; + + public: + AuthProvider ( const com::sun::star::uno::Reference< + com::sun::star::ucb::XCommandEnvironment>& xEnv, + rtl::OUString sUrl, + rtl::OUString sBindingUrl ): + m_xEnv( xEnv ), m_sUrl( sUrl ), m_sBindingUrl( sBindingUrl ) { } + + bool authenticationQuery( string& username, string& password ); + }; + + bool AuthProvider::authenticationQuery( string& username, string& password ) + { + if ( m_xEnv.is() ) + { + uno::Reference< task::XInteractionHandler > xIH + = m_xEnv->getInteractionHandler(); + + if ( xIH.is() ) + { + rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest + = new ucbhelper::SimpleAuthenticationRequest( + m_sUrl, m_sBindingUrl, ::rtl::OUString(), + rtl::OUString::createFromAscii( username.c_str( ) ), + rtl::OUString::createFromAscii( password.c_str( ) ), + ::rtl::OUString(), true, false ); + 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() ) + { + const rtl::Reference< + ucbhelper::InteractionSupplyAuthentication > & xSupp + = xRequest->getAuthenticationSupplier(); + + username = OUSTR_TO_STDSTR( xSupp->getUserName() ); + password = OUSTR_TO_STDSTR( xSupp->getPassword() ); + + return true; + } + } + } + } + return false; + } +} + namespace cmis { Content::Content( const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, @@ -62,43 +134,85 @@ namespace cmis throw ( ucb::ContentCreationException ) : ContentImplHelper( rxSMgr, pProvider, Identifier ), m_pProvider( pProvider ), - m_pSession( NULL ) + m_pSession( NULL ), + m_bTransient( false ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf(stderr, "New Content ('%s')\n", rtl::OUStringToOString(m_xIdentifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8).getStr()); -#endif // Split the URL into bits - cmis::URL url( m_xIdentifier->getContentIdentifier() ); + m_sURL = m_xIdentifier->getContentIdentifier( ); + cmis::URL url( m_sURL ); - // Initiate a CMIS session - m_pSession = libcmis::SessionFactory::createSession( url.getSessionParams( ) ); + // Look for a cached session + m_pSession = pProvider->getSession( url.getBindingUrl( ) ); + if ( NULL == m_pSession ) + { + // Initiate a CMIS session and register it as we found nothing + m_pSession = libcmis::SessionFactory::createSession( url.getSessionParams( ) ); + pProvider->registerSession( url.getBindingUrl( ), m_pSession ); + } - // Get the content Object - m_pObject = m_pSession->getObject( url.getObjectId() ); + m_sObjectId = url.getObjectId( ); + m_sBindingUrl = url.getBindingUrl( ); } Content::Content( const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, ContentProvider *pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier, - sal_Bool /*bIsFolder*/) + sal_Bool bIsFolder ) throw ( ucb::ContentCreationException ) : ContentImplHelper( rxSMgr, pProvider, Identifier ), m_pProvider( pProvider ), - m_pSession( NULL ) + m_pSession( NULL ), + m_bTransient( true ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf(stderr, "TODO - Create Content ('%s')\n", rtl::OUStringToOString(m_xIdentifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8).getStr()); -#endif - // TODO Implement me + // Split the URL into bits + m_sURL = m_xIdentifier->getContentIdentifier( ); + cmis::URL url( m_sURL ); + + // Look for a cached session + m_pSession = pProvider->getSession( url.getBindingUrl( ) ); + if ( NULL == m_pSession ) + { + // Initiate a CMIS session and register it as we found nothing + m_pSession = libcmis::SessionFactory::createSession( url.getSessionParams( ) ); + pProvider->registerSession( url.getBindingUrl( ), m_pSession ); + } + + m_sObjectId = url.getObjectId( ); + m_sBindingUrl = url.getBindingUrl( ); + + // Get the object type + string typeId = bIsFolder ? "cmis:folder" : "cmis:document"; + m_pObjectType = m_pSession->getType( typeId ); } Content::~Content() { - delete m_pSession; } - bool Content::isFolder(const uno::Reference< ucb::XCommandEnvironment >& /*xEnv*/) + libcmis::ObjectPtr Content::getObject( ) { - return m_pObject->getBaseType( ) == "cmis::folder"; + try + { + if ( !m_pObject.get() ) + m_pObject = m_pSession->getObject( OUSTR_TO_STDSTR( m_sObjectId ) ); + } + catch ( const libcmis::Exception& e ) + { + SAL_INFO( "cmisucp", "Unexpected exception: " << e.what() ); + } + + return m_pObject; + } + + void Content::resetAuthProvider( const uno::Reference< ucb::XCommandEnvironment >& xEnv ) + { + libcmis::AuthProviderPtr authProvider( new AuthProvider( xEnv, m_sURL, m_sBindingUrl ) ); + m_pSession->setAuthenticationProvider( authProvider ); + } + + bool Content::isFolder(const uno::Reference< ucb::XCommandEnvironment >& xEnv ) + { + resetAuthProvider( xEnv ); + return getObject( )->getBaseType( ) == "cmis:folder"; } uno::Any Content::getBadArgExcept() @@ -110,11 +224,10 @@ namespace cmis uno::Reference< sdbc::XRow > Content::getPropertyValues( const uno::Sequence< beans::Property >& rProperties, - const uno::Reference< ucb::XCommandEnvironment >& /*xEnv*/ ) + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::getPropertyValues()\n" ); -#endif + resetAuthProvider( xEnv ); + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xSMgr ); sal_Int32 nProps; @@ -126,59 +239,84 @@ namespace cmis for( sal_Int32 n = 0; n < nProps; ++n ) { const beans::Property& rProp = pProps[ n ]; -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "Property: %s\n", rtl::OUStringToOString( rProp.Name, RTL_TEXTENCODING_UTF8 ).getStr() ); -#endif if ( rProp.Name == "IsDocument" ) { - if ( m_pObject->getBaseType( ) == "cmis:document" ) + if ( getObject()->getBaseType( ) == "cmis:document" ) xRow->appendBoolean( rProp, true ); else xRow->appendVoid( rProp ); } else if ( rProp.Name == "IsFolder" ) { - if( m_pObject->getBaseType( ) == "cmis:folder" ) + if( getObject()->getBaseType( ) == "cmis:folder" ) xRow->appendBoolean( rProp, true ); else xRow->appendVoid( rProp ); } + else if ( rProp.Name == "Title" ) + { + string name = getObject()->getName(); + xRow->appendString( rProp, rtl::OUString::createFromAscii( name.c_str() ) ); + } else if ( rProp.Name == "TitleOnServer" ) { - // TODO Set the path instead of the name - xRow->appendString( rProp, rtl::OUString::createFromAscii( m_pObject->getName().c_str() ) ); + string path; + libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject().get( ) ); + if ( NULL != document ) + { + vector< boost::shared_ptr< libcmis::Folder > > parents = document->getParents( ); + if ( parents.size() > 0 ) + path = parents.front( )->getPath( ); + + if ( path[ path.length() - 1 ] != '/' ) + path += "/"; + path += getObject()->getName( ); + } + else + { + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( getObject().get( ) ); + if ( NULL != folder ) + path = folder->getPath( ); + } + + xRow->appendString( rProp, rtl::OUString::createFromAscii( path.c_str() ) ); } else if ( rProp.Name == "IsReadOnly" ) { - // TODO Fix this value - xRow->appendBoolean( rProp, sal_True ); + boost::shared_ptr< libcmis::AllowableActions > allowableActions = getObject()->getAllowableActions( ); + sal_Bool bReadOnly = sal_False; + if ( allowableActions->isAllowed( libcmis::ObjectAction::SetContentStream ) ) + bReadOnly = sal_True; + + xRow->appendBoolean( rProp, bReadOnly ); } else if ( rProp.Name == "DateCreated" ) { // TODO Fix this value + SAL_INFO( "cmisucp", "TODO - Fix property value " << rProp.Name ); xRow->appendVoid( rProp ); } else if ( rProp.Name == "DateModified" ) { // TODO Fix this value + SAL_INFO( "cmisucp", "TODO - Fix property value " << rProp.Name ); xRow->appendVoid( rProp ); } else if ( rProp.Name == "Size" ) { - libcmis::Document* document = dynamic_cast< libcmis::Document* >( m_pObject.get( ) ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject().get( ) ); if ( NULL != document ) xRow->appendLong( rProp, document->getContentLength() ); else xRow->appendVoid( rProp ); } -#if OSL_DEBUG_LEVEL > 1 - else + else if ( rProp.Name == "CreatableContentsInfo" ) { - fprintf( stderr, "Looking for unsupported property %s\n", - rtl::OUStringToOString( rProp.Name, RTL_TEXTENCODING_UTF8 ).getStr( ) ); + xRow->appendObject( rProp, uno::makeAny( queryCreatableContentsInfo( xEnv ) ) ); } -#endif + else + SAL_INFO( "cmisucp", "Looking for unsupported property " << rProp.Name ); } return uno::Reference< sdbc::XRow >( xRow.get() ); @@ -186,9 +324,7 @@ namespace cmis void Content::queryChildren( ContentRefList& /*rChildren*/ ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::queryChildren()\n" ); -#endif + SAL_INFO( "cmisucp", "TODO - Content::queryChildren()" ); // TODO Implement me } @@ -210,10 +346,8 @@ namespace cmis if ( bOpenFolder && bIsFolder ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::open() - Folder case\n" ); -#endif - // TODO Implement the folder case + SAL_INFO( "cmisucp", "TODO - Content::open() - Folder case" ); + // TODO Handle the folder case } else if ( rOpenCommand.Sink.is() ) { @@ -234,10 +368,7 @@ namespace cmis // Note: rOpenCommand.Sink may contain an XStream // implementation. Support for this type of // sink is optional... -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "Failed to load data from '%s'\n", - rtl::OUStringToOString( m_xIdentifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8 ).getStr() ); -#endif + SAL_INFO( "cmisucp", "Failed to copy data to sink" ); ucbhelper::cancelCommandExecution( uno::makeAny (ucb::UnsupportedDataSinkException @@ -247,36 +378,115 @@ namespace cmis } } else - fprintf( stderr, "Open falling through ..." ); + SAL_INFO( "cmisucp", "Open falling through ..." ); return aRet; } - void Content::transfer( const ucb::TransferInfo& /*rTransferInfo*/, - const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) + void Content::transfer( const ucb::TransferInfo& rTransferInfo, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) throw( uno::Exception ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::transfer()\n" ); -#endif - // TODO Implement me + // If the source isn't on the same CMIS repository, then simply copy + INetURLObject aSourceUrl( rTransferInfo.SourceURL ); + if ( aSourceUrl.GetProtocol() != INET_PROT_CMIS_ATOM ) + { + rtl::OUString sSrcBindingUrl = URL( rTransferInfo.SourceURL ).getBindingUrl( ); + if ( sSrcBindingUrl != m_sBindingUrl ) + { + ucbhelper::cancelCommandExecution( + uno::makeAny( + ucb::InteractiveBadTransferURLException( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "Unsupported URL scheme!" )), + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + } + + SAL_INFO( "cmisucp", "TODO - Content::transfer()" ); } - void Content::insert( const uno::Reference< io::XInputStream > & /*xInputStream*/, - sal_Bool /*bReplaceExisting*/, const uno::Reference< ucb::XCommandEnvironment >& /*xEnv*/ ) + void Content::insert( const uno::Reference< io::XInputStream > & xInputStream, + sal_Bool bReplaceExisting, const uno::Reference< ucb::XCommandEnvironment >& xEnv ) throw( uno::Exception ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::insert()\n" ); -#endif - // TODO Implement me + SAL_INFO( "cmisucp", "TODO - Content::insert()" ); + + if ( !xInputStream.is() ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( ucb::MissingInputStreamException + ( rtl::OUString(), static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + + try + { + // For transient content, the URL is the one of the parent + if ( m_bTransient ) + { + // Try to get the object from the server if there is any + libcmis::Folder* pFolder = dynamic_cast< libcmis::Folder* >( getObject( ).get( ) ); + if ( pFolder != NULL ) + { + map< string, libcmis::PropertyPtr >::iterator it = m_pObjectProps.find( "cmis:name" ); + if ( it == m_pObjectProps.end( ) ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( uno::RuntimeException( "Missing name property", + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + string newName = it->second->getStrings( ).front( ); + string newPath = pFolder->getPath( ); + if ( newPath[ newPath.size( ) - 1 ] != '/' ) + newPath += "/"; + newPath += newName; + + libcmis::ObjectPtr object = m_pSession->getObjectByPath( newPath ); + + if ( NULL != object.get( ) ) + { + // Are the base type matching? + if ( object->getBaseType( ) != m_pObjectType->getBaseType( )->getId() ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( uno::RuntimeException( "Can't change a folder into a document and vice-versa.", + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + + // Update the existing object if it's a document + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get( ) ); + if ( NULL != document ) + { + boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) ); + uno::Reference < io::XOutputStream > xOutput = new ucbhelper::StdOutputStream( pOut ); + copyData( xInputStream, xOutput ); + document->setContentStream( pOut, string( ), bReplaceExisting ); + } + } + else + { + // TODO We need to create a brand new object... either folder or document + } + } + } + else + { + // TODO Update the current object... but I'm not sure this case can happen with UCB + } + } + catch ( const libcmis::Exception& e ) + { + throw uno::Exception( rtl::OUString::createFromAscii( e.what( ) ), *this ); + } } - void Content::destroy( sal_Bool /*bDeletePhysical*/ ) throw( uno::Exception ) + void Content::destroy( sal_Bool /*bDeletePhysical*/ ) throw ( uno::Exception ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::destroy()\n" ); -#endif + SAL_INFO( "cmisucp", "TODO - Content::destroy()" ); // TODO Implement me } @@ -298,13 +508,57 @@ namespace cmis const uno::Sequence< beans::PropertyValue >& rValues, const uno::Reference< ucb::XCommandEnvironment >& ) { + // Get the already set properties if possible + if ( !m_bTransient && getObject( ).get( ) ) + m_pObjectProps = getObject()->getProperties( ); + sal_Int32 nCount = rValues.getLength(); uno::Sequence< uno::Any > aRet( nCount ); -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::setPropertyValue()\n" ); -#endif - // TODO Implement me + const beans::PropertyValue* pValues = rValues.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const beans::PropertyValue& rValue = pValues[ n ]; + if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) || + rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) || + rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) || + rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) || + rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Size" ) ) || + rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) ) + { + lang::IllegalAccessException e ( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Property is read-only!")), + static_cast< cppu::OWeakObject* >( this ) ); + aRet[ n ] <<= e; + } + else if ( rValue.Name == "Title" ) + { + rtl::OUString aNewTitle; + if (!( rValue.Value >>= aNewTitle )) + { + aRet[ n ] <<= beans::IllegalTypeException + ( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Property value has wrong type!")), + static_cast< cppu::OWeakObject * >( this ) ); + continue; + } + + if ( aNewTitle.getLength() <= 0 ) + { + aRet[ n ] <<= lang::IllegalArgumentException + ( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Empty title not allowed!")), + static_cast< cppu::OWeakObject * >( this ), -1 ); + continue; + + } + + setCmisProperty( "cmis:name", OUSTR_TO_STDSTR( aNewTitle ) ); + } + else + { + lang::IllegalAccessException e ( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Property is read-only!")), + static_cast< cppu::OWeakObject* >( this ) ); + aRet[ n ] <<= e; + } + } return aRet; } @@ -317,14 +571,18 @@ namespace cmis uno::Reference< io::XOutputStream > xOut = uno::Reference< io::XOutputStream >(xSink, uno::UNO_QUERY ); uno::Reference< io::XActiveDataSink > xDataSink = uno::Reference< io::XActiveDataSink >(xSink, uno::UNO_QUERY ); + uno::Reference< io::XActiveDataStreamer > xDataStreamer = uno::Reference< io::XActiveDataStreamer >( xSink, uno::UNO_QUERY ); - if ( !xOut.is() && !xDataSink.is() ) + if ( !xOut.is() && !xDataSink.is() && ( !xDataStreamer.is() || !xDataStreamer->getStream().is() ) ) return sal_False; - libcmis::Document* document = dynamic_cast< libcmis::Document* >( m_pObject.get() ); - FILE* fd = document->getContent( ); + if ( xDataStreamer.is() && !xOut.is() ) + xOut = xDataStreamer->getStream()->getOutputStream(); + + libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject().get() ); + boost::shared_ptr< istream > aIn = document->getContentStream( ); - uno::Reference< io::XInputStream > xIn = new ucbhelper::FdInputStream( fd ); + uno::Reference< io::XInputStream > xIn = new ucbhelper::StdInputStream( aIn ); if( !xIn.is( ) ) return sal_False; @@ -339,17 +597,17 @@ namespace cmis sal_Bool Content::exchangeIdentity( const uno::Reference< ucb::XContentIdentifier >& /*xNewId*/ ) { sal_Bool bRet = sal_False; -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::exchangeIdentity()\n" ); -#endif + SAL_INFO( "cmisucp", "TODO - Content::exchangeIdentity()" ); // TODO Implement me return bRet; } uno::Sequence< beans::Property > Content::getProperties( - const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) { + resetAuthProvider( xEnv ); + static const beans::Property aGenericProperties[] = { beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDocument" ) ), @@ -358,6 +616,9 @@ namespace cmis beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFolder" ) ), -1, getCppuBooleanType(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), + beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ), + -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ), + beans::PropertyAttribute::BOUND ), beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TitleOnServer" ) ), -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ), beans::PropertyAttribute::BOUND ), @@ -373,6 +634,9 @@ namespace cmis beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Size" ) ), -1, getCppuType( static_cast< const sal_Int64 * >( 0 ) ), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), + beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CreatableContentsInfo" ) ), + -1, getCppuType( static_cast< const uno::Sequence< ucb::ContentInfo > * >( 0 ) ), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), }; const int nProps = SAL_N_ELEMENTS(aGenericProperties); @@ -382,6 +646,8 @@ namespace cmis uno::Sequence< ucb::CommandInfo > Content::getCommands( const uno::Reference< ucb::XCommandEnvironment > & xEnv ) { + resetAuthProvider( xEnv ); + static ucb::CommandInfo aCommandInfoTable[] = { // Required commands @@ -426,10 +692,31 @@ namespace cmis { rtl::OUString sRet; -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::getParentURL()\n" ); -#endif - // TODO Implement me + SAL_INFO( "cmisucp", "Content::getParentURL()" ); + + string parentId; + + libcmis::ObjectPtr pObj = getObject( ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject( ).get( ) ); + if ( NULL != document ) + { + vector< boost::shared_ptr< libcmis::Folder > > parents = document->getParents( ); + if ( parents.size( ) > 0 ) + parentId = parents.front( )->getId( ); + } + else + { + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( getObject( ).get( ) ); + if ( NULL != folder ) + parentId = folder->getFolderParent( )->getId( ); + } + + if ( !parentId.empty() ) + { + URL aUrl( m_sURL ); + aUrl.setObjectId( rtl::OUString::createFromAscii( parentId.c_str( ) ) ); + sRet = aUrl.asString( ); + } return sRet; } @@ -478,6 +765,9 @@ namespace cmis const uno::Reference< ucb::XCommandEnvironment >& xEnv ) throw( uno::Exception, ucb::CommandAbortedException, uno::RuntimeException ) { + SAL_INFO( "cmisucp", "Content::execute( ) - " << aCommand.Name ); + resetAuthProvider( xEnv ); + uno::Any aRet; if ( aCommand.Name == "getPropertyValues" ) @@ -531,19 +821,11 @@ namespace cmis { sal_Bool bDeletePhysical = sal_False; aCommand.Argument >>= bDeletePhysical; - -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::execute() - delete\n" ); -#endif - // TODO Actually delete it - destroy( bDeletePhysical ); } else { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "cmis::Content::execute() - UNKNOWN COMMAND\n" ); -#endif + SAL_INFO( "cmisucp", "Unknown command to execute" ); ucbhelper::cancelCommandExecution ( uno::makeAny( ucb::UnsupportedCommandException @@ -557,9 +839,7 @@ namespace cmis void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ ) throw( uno::RuntimeException ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "TODO - cmis::Content::abort()\n" ); -#endif + SAL_INFO( "cmisucp", "TODO - Content::abort()" ); // TODO Implement me } @@ -573,7 +853,6 @@ namespace cmis const ucb::ContentInfo& Info ) throw( uno::RuntimeException ) { bool create_document; - const char *name; if ( Info.Type == CMIS_FILE_TYPE ) create_document = true; @@ -581,33 +860,23 @@ namespace cmis create_document = false; else { -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "Failed to create new content '%s'", - rtl::OUStringToOString(Info.Type, RTL_TEXTENCODING_UTF8).getStr() ); -#endif + SAL_INFO( "cmisucp", "Unknown type of content to create" ); return uno::Reference< ucb::XContent >(); } -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "createNewContent (%d)", (int) create_document ); -#endif - - rtl::OUString aURL = m_xIdentifier->getContentIdentifier(); - - if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() ) - aURL += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")); - - name = create_document ? "[New_Content]" : "[New_Collection]"; - aURL += rtl::OUString::createFromAscii( name ); + rtl::OUString sParentURL = m_xIdentifier->getContentIdentifier(); + URL aParentURL( sParentURL ); - uno::Reference< ucb::XContentIdentifier > xId(new ::ucbhelper::ContentIdentifier(m_xSMgr, aURL)); + // Set the parent URL for the transient objects + uno::Reference< ucb::XContentIdentifier > xId(new ::ucbhelper::ContentIdentifier(m_xSMgr, sParentURL)); try { return new ::cmis::Content( m_xSMgr, m_pProvider, xId, !create_document ); - } catch ( ucb::ContentCreationException & ) + } + catch ( ucb::ContentCreationException & ) { - return uno::Reference< ucb::XContent >(); + return uno::Reference< ucb::XContent >(); } } @@ -652,6 +921,7 @@ namespace cmis const uno::Reference< ucb::XCommandEnvironment >& xEnv) throw( uno::RuntimeException ) { + resetAuthProvider( xEnv ); if ( isFolder( xEnv ) ) { uno::Sequence< ucb::ContentInfo > seq(2); @@ -682,6 +952,24 @@ namespace cmis return uno::Sequence< ucb::ContentInfo >(); } } + + void Content::setCmisProperty( std::string sName, std::string sValue ) + { + if ( m_pObjectType.get( ) ) + { + map< string, libcmis::PropertyTypePtr > propsTypes = m_pObjectType->getPropertiesTypes( ); + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propsTypes.find( sName ); + + if ( typeIt != propsTypes.end( ) ) + { + libcmis::PropertyTypePtr propType = typeIt->second; + vector< string > values; + values.push_back( sValue ); + libcmis::PropertyPtr property( new libcmis::Property( propType, values ) ); + m_pObjectProps.insert( pair< string, libcmis::PropertyPtr >( sName, property ) ); + } + } + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/cmis/cmis_content.hxx b/ucb/source/ucp/cmis/cmis_content.hxx index ef7503a0dfbe..e80a6d278bf3 100644 --- a/ucb/source/ucp/cmis/cmis_content.hxx +++ b/ucb/source/ucp/cmis/cmis_content.hxx @@ -29,6 +29,8 @@ #ifndef CMIS_CONTENT_HXX #define CMIS_CONTENT_HXX +#include "cmis_url.hxx" + #include #include #include @@ -68,9 +70,18 @@ class Content : public ::ucbhelper::ContentImplHelper, public com::sun::star::uc private: ContentProvider* m_pProvider; libcmis::Session* m_pSession; - libcmis::CmisObjectPtr m_pObject; + libcmis::ObjectPtr m_pObject; + rtl::OUString m_sObjectId; + rtl::OUString m_sURL; + rtl::OUString m_sBindingUrl; + + // Members to be set for non-persistent content + bool m_bTransient; + libcmis::ObjectTypePtr m_pObjectType; + std::map< std::string, libcmis::PropertyPtr > m_pObjectProps; bool isFolder(const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >& xEnv); + void setCmisProperty( std::string sName, std::string sValue ); com::sun::star::uno::Any getBadArgExcept(); @@ -112,6 +123,9 @@ private: sal_Bool exchangeIdentity(const com::sun::star::uno::Reference< com::sun::star::ucb::XContentIdentifier >& xNewId); + void resetAuthProvider( const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >& xEnv ); + libcmis::ObjectPtr getObject( ); + public: Content( const com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >& rxSMgr, ContentProvider *pProvider, diff --git a/ucb/source/ucp/cmis/cmis_provider.cxx b/ucb/source/ucp/cmis/cmis_provider.cxx index f733cf0ea22a..a13e9f777ff1 100644 --- a/ucb/source/ucp/cmis/cmis_provider.cxx +++ b/ucb/source/ucp/cmis/cmis_provider.cxx @@ -45,12 +45,6 @@ ContentProvider::queryContent( throw( com::sun::star::ucb::IllegalIdentifierException, uno::RuntimeException ) { -#if OSL_DEBUG_LEVEL > 1 - fprintf(stderr, "QueryContent: '%s'\n", - rtl::OUStringToOString - (Identifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8).getStr()); -#endif - osl::MutexGuard aGuard( m_aMutex ); // Check, if a content with given id already exists... @@ -60,7 +54,8 @@ ContentProvider::queryContent( try { - xContent = new ::cmis::Content(m_xSMgr, this, Identifier); + xContent = new ::cmis::Content( m_xSMgr, this, Identifier ); + registerNewContent( xContent ); } catch ( com::sun::star::ucb::ContentCreationException const & ) { @@ -73,14 +68,36 @@ ContentProvider::queryContent( return xContent; } +libcmis::Session* ContentProvider::getSession( const rtl::OUString& sBindingUrl ) +{ + libcmis::Session* pSession = NULL; + std::map< rtl::OUString, libcmis::Session* >::iterator it = m_aSessionCache.find( sBindingUrl ); + if ( it != m_aSessionCache.end( ) ) + { + pSession = it->second; + } + return pSession; +} + +void ContentProvider::registerSession( const rtl::OUString& sBindingUrl, libcmis::Session* pSession ) +{ + m_aSessionCache.insert( std::pair< rtl::OUString, libcmis::Session* >( sBindingUrl, pSession ) ); +} + ContentProvider::ContentProvider( const uno::Reference< lang::XMultiServiceFactory >& rSMgr ) : ::ucbhelper::ContentProviderImplHelper( rSMgr ) { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "ContentProvider::ContentProvider( )\n" ); +#endif } ContentProvider::~ContentProvider() { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "ContentProvider::~ContentProvider( )\n" ); +#endif } XINTERFACE_IMPL_3( ContentProvider, diff --git a/ucb/source/ucp/cmis/cmis_provider.hxx b/ucb/source/ucp/cmis/cmis_provider.hxx index 090cde0f1ddf..2ade10052725 100644 --- a/ucb/source/ucp/cmis/cmis_provider.hxx +++ b/ucb/source/ucp/cmis/cmis_provider.hxx @@ -31,12 +31,16 @@ #include #include +#include namespace cmis { class ContentProvider : public ::ucbhelper::ContentProviderImplHelper { +private: + std::map< rtl::OUString, libcmis::Session* > m_aSessionCache; + public: ContentProvider( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& rSMgr ); @@ -58,6 +62,9 @@ public: ::com::sun::star::ucb::XContentIdentifier >& Identifier ) throw( ::com::sun::star::ucb::IllegalIdentifierException, ::com::sun::star::uno::RuntimeException ); + + libcmis::Session* getSession( const rtl::OUString& sBindingUrl ); + void registerSession( const rtl::OUString& sBindingUrl, libcmis::Session* pSession ); }; } diff --git a/ucb/source/ucp/cmis/cmis_url.cxx b/ucb/source/ucp/cmis/cmis_url.cxx index 92a6f7e24435..655518d58365 100644 --- a/ucb/source/ucp/cmis/cmis_url.cxx +++ b/ucb/source/ucp/cmis/cmis_url.cxx @@ -27,7 +27,6 @@ */ #include -#include #include "cmis_url.hxx" @@ -38,17 +37,17 @@ using namespace std; namespace cmis { - URL::URL( rtl::OUString const & urlStr ) + URL::URL( rtl::OUString const & urlStr ) : + m_aUrl( urlStr ) { - INetURLObject url( urlStr ); - string bindingUrl( "http://" ); - bindingUrl += OUSTR_TO_STDSTR( url.GetHostPort( ) ); - bindingUrl += OUSTR_TO_STDSTR( url.GetURLPath( ) ); + rtl::OUString bindingUrl( "http://" ); + bindingUrl += m_aUrl.GetHostPort( ); + bindingUrl += m_aUrl.GetURLPath( ); m_sBindingUrl = bindingUrl; // Split the query into bits and locate the repo-id key - rtl::OUString query = url.GetParam( ); + rtl::OUString query = m_aUrl.GetParam( ); while ( !query.isEmpty() ) { sal_Int32 nPos = query.indexOfAsciiL( "&", 1 ); @@ -65,8 +64,8 @@ namespace cmis } sal_Int32 nEqPos = segment.indexOfAsciiL( "=", 1 ); - string key = OUSTR_TO_STDSTR( segment.copy( 0, nEqPos ) ); - string value = OUSTR_TO_STDSTR( segment.copy( nEqPos +1 ) ); + rtl::OUString key = segment.copy( 0, nEqPos ); + rtl::OUString value = segment.copy( nEqPos +1 ); if ( key == "repo-id" ) m_sRepositoryId = value; @@ -79,16 +78,40 @@ namespace cmis map< int, string > URL::getSessionParams( ) { map< int, string > params; - params[ATOMPUB_URL] = m_sBindingUrl; - params[REPOSITORY_ID] = m_sRepositoryId; + params[ATOMPUB_URL] = OUSTR_TO_STDSTR( m_sBindingUrl ); + params[REPOSITORY_ID] = OUSTR_TO_STDSTR( m_sRepositoryId ); + params[USERNAME] = OUSTR_TO_STDSTR( m_aUrl.GetUser() ); + params[PASSWORD] = OUSTR_TO_STDSTR( m_aUrl.GetPass() ); return params; } - string URL::getObjectId( ) + rtl::OUString URL::getObjectId( ) { return m_aQuery["id"]; } + + rtl::OUString URL::getBindingUrl( ) + { + return m_sBindingUrl; + } + + void URL::setObjectId( rtl::OUString sId ) + { + m_aQuery["id"] = sId; + updateUrlQuery( ); + } + + rtl::OUString URL::asString( ) + { + return m_aUrl.GetMainURL( INetURLObject::NO_DECODE ); + } + + void URL::updateUrlQuery( ) + { + rtl::OUString sParam = "repo-id=" + m_sRepositoryId + "&id=" + m_aQuery["id"]; + m_aUrl.SetParam( sParam ); + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/cmis/cmis_url.hxx b/ucb/source/ucp/cmis/cmis_url.hxx index 1859102fa59e..18d13f166f37 100644 --- a/ucb/source/ucp/cmis/cmis_url.hxx +++ b/ucb/source/ucp/cmis/cmis_url.hxx @@ -32,22 +32,32 @@ #include #include +#include namespace cmis { class URL { private: - std::string m_sBindingUrl; - std::string m_sRepositoryId; + INetURLObject m_aUrl; + rtl::OUString m_sBindingUrl; + rtl::OUString m_sRepositoryId; - std::map< std::string, std::string > m_aQuery; + std::map< rtl::OUString, rtl::OUString > m_aQuery; public: URL( rtl::OUString const & urlStr ); std::map< int, std::string > getSessionParams( ); - std::string getObjectId( ); + rtl::OUString getObjectId( ); + rtl::OUString getBindingUrl( ); + void setObjectId( rtl::OUString sId ); + + rtl::OUString asString( ); + + private: + + void updateUrlQuery( ); }; } diff --git a/ucbhelper/Library_ucbhelper.mk b/ucbhelper/Library_ucbhelper.mk index 166226d616c4..7de5f82cd96e 100644 --- a/ucbhelper/Library_ucbhelper.mk +++ b/ucbhelper/Library_ucbhelper.mk @@ -70,6 +70,8 @@ $(eval $(call gb_Library_add_exception_objects,ucbhelper,\ ucbhelper/source/provider/interactionrequest \ ucbhelper/source/provider/providerhelper \ ucbhelper/source/provider/fd_inputstream \ + ucbhelper/source/provider/std_inputstream \ + ucbhelper/source/provider/std_outputstream \ ucbhelper/source/client/proxydecider \ ucbhelper/source/client/content \ ucbhelper/source/client/contentbroker \ diff --git a/ucbhelper/Package_inc.mk b/ucbhelper/Package_inc.mk index ccce9c9eaed2..25a197ebd47a 100644 --- a/ucbhelper/Package_inc.mk +++ b/ucbhelper/Package_inc.mk @@ -55,5 +55,7 @@ $(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/providerhelper.hxx $(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/simplenameclashresolverequest.hxx,ucbhelper/simplenameclashresolverequest.hxx)) $(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/macros.hxx,ucbhelper/macros.hxx)) $(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/fd_inputstream.hxx,ucbhelper/fd_inputstream.hxx)) +$(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/std_inputstream.hxx,ucbhelper/std_inputstream.hxx)) +$(eval $(call gb_Package_add_file,ucbhelper_inc,inc/ucbhelper/std_outputstream.hxx,ucbhelper/std_outputstream.hxx)) # vim: set noet sw=4 ts=4: diff --git a/ucbhelper/inc/ucbhelper/std_inputstream.hxx b/ucbhelper/inc/ucbhelper/std_inputstream.hxx new file mode 100644 index 000000000000..2068f45eab4a --- /dev/null +++ b/ucbhelper/inc/ucbhelper/std_inputstream.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright 2012 LibreOffice contributors. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef _UCBHELPER_STD_INPUTSTREAM_HXX_ +#define _UCBHELPER_STD_INPUTSTREAM_HXX_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ucbhelper/ucbhelperdllapi.h" + + +namespace ucbhelper +{ + namespace css = com::sun::star; + + /** Implements a seekable InputStream + * working on an std::istream + */ + class UCBHELPER_DLLPUBLIC StdInputStream + : public cppu::OWeakObject, + public com::sun::star::io::XInputStream, + public com::sun::star::io::XSeekable + { + public: + + StdInputStream( boost::shared_ptr< std::istream > pStream ); + + ~StdInputStream(); + + virtual css::uno::Any SAL_CALL queryInterface ( const css::uno::Type& rType ) + throw ( css::uno::RuntimeException ); + + virtual void SAL_CALL acquire( ) throw ( ); + + virtual void SAL_CALL release( ) throw ( ); + + virtual sal_Int32 SAL_CALL + readBytes ( css::uno::Sequence< sal_Int8 >& aData, + sal_Int32 nBytesToRead ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual sal_Int32 SAL_CALL + readSomeBytes ( css::uno::Sequence< sal_Int8 >& aData, + sal_Int32 nMaxBytesToRead ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual void SAL_CALL + skipBytes ( sal_Int32 nBytesToSkip ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual sal_Int32 SAL_CALL + available ( ) + throw ( css::io::NotConnectedException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual void SAL_CALL + closeInput ( ) + throw ( css::io::NotConnectedException, + css::io::IOException, + css::uno::RuntimeException ); + + + /** XSeekable + */ + + virtual void SAL_CALL + seek ( sal_Int64 location ) + throw ( css::lang::IllegalArgumentException, + css::io::IOException, + css::uno::RuntimeException ); + + + virtual sal_Int64 SAL_CALL + getPosition ( ) + throw ( css::io::IOException, css::uno::RuntimeException ); + + + virtual sal_Int64 SAL_CALL + getLength ( ) + throw ( css::io::IOException, + css::uno::RuntimeException ); + + private: + + osl::Mutex m_aMutex; + boost::shared_ptr< std::istream > m_pStream; + sal_Int64 m_nLength; + }; + + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/inc/ucbhelper/std_outputstream.hxx b/ucbhelper/inc/ucbhelper/std_outputstream.hxx new file mode 100644 index 000000000000..b83b17e630e1 --- /dev/null +++ b/ucbhelper/inc/ucbhelper/std_outputstream.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright 2012 LibreOffice contributors. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef _UCBHELPER_STD_OUTPUTSTREAM_HXX_ +#define _UCBHELPER_STD_OUTPUTSTREAM_HXX_ + +#include +#include + +#include +#include +#include +#include +#include "ucbhelper/ucbhelperdllapi.h" + +namespace ucbhelper +{ + namespace css = com::sun::star; + + /** Implements a OutputStream + * working on an std::ostream + */ + class UCBHELPER_DLLPUBLIC StdOutputStream : + public cppu::OWeakObject, + public css::io::XOutputStream + { + public: + + StdOutputStream( boost::shared_ptr< std::ostream > pStream ); + + ~StdOutputStream( ); + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) + throw ( css::uno::RuntimeException ); + + virtual void SAL_CALL acquire ( ) throw ( ); + + virtual void SAL_CALL release ( ) throw ( ); + + virtual void SAL_CALL writeBytes ( const css::uno::Sequence< sal_Int8 >& aData ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual void SAL_CALL flush ( ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + virtual void SAL_CALL closeOutput ( ) + throw ( css::io::NotConnectedException, + css::io::BufferSizeExceededException, + css::io::IOException, + css::uno::RuntimeException ); + + private: + + osl::Mutex m_aMutex; + boost::shared_ptr< std::ostream > m_pStream; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/provider/std_inputstream.cxx b/ucbhelper/source/provider/std_inputstream.cxx new file mode 100644 index 000000000000..049313197e8a --- /dev/null +++ b/ucbhelper/source/provider/std_inputstream.cxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright 2012 LibreOffice contributors. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ucbhelper/std_inputstream.hxx" + +using namespace std; +using namespace com::sun::star; + +namespace ucbhelper +{ + StdInputStream::StdInputStream( boost::shared_ptr< istream > pStream ) : + m_pStream( pStream ), + m_nLength( 0 ) + { + if ( m_pStream.get() ) + { + streampos nInitPos = m_pStream->tellg( ); + m_pStream->seekg( 0, ios_base::end ); + streampos nEndPos = m_pStream->tellg( ); + m_pStream->seekg( nInitPos, ios_base::beg ); + + m_nLength = sal_Int64( nEndPos - nInitPos ); + } + } + + StdInputStream::~StdInputStream() + { + } + + uno::Any SAL_CALL StdInputStream::queryInterface( const uno::Type& rType ) throw ( uno::RuntimeException ) + { + uno::Any aRet = ::cppu::queryInterface( rType, + ( static_cast< XInputStream* >( this ) ), + ( static_cast< XSeekable* >( this ) ) ); + + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); + } + + void SAL_CALL StdInputStream::acquire( ) throw( ) + { + OWeakObject::acquire(); + } + + void SAL_CALL StdInputStream::release( ) throw( ) + { + OWeakObject::release(); + } + + sal_Int32 SAL_CALL StdInputStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) + throw( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( 0 <= nBytesToRead && aData.getLength() < nBytesToRead ) + aData.realloc( nBytesToRead ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + sal_Int32 nRead = 0; + try + { + m_pStream->read( reinterpret_cast< char* >( aData.getArray( ) ), nBytesToRead ); + nRead = m_pStream->gcount(); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "StdInputStream::readBytes() error: " << e.what() ); + throw io::IOException( ); + } + + return nRead; + } + + sal_Int32 SAL_CALL StdInputStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, + sal_Int32 nMaxBytesToRead ) + throw( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( 0 <= nMaxBytesToRead && aData.getLength() < nMaxBytesToRead ) + aData.realloc( nMaxBytesToRead ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + sal_Int32 nRead = 0; + try + { + nRead = m_pStream->readsome( reinterpret_cast< char* >( aData.getArray( ) ), nMaxBytesToRead ); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "StdInputStream::readBytes() error: " << e.what() ); + throw io::IOException( ); + } + return nRead; + } + + void SAL_CALL StdInputStream::skipBytes( sal_Int32 nBytesToSkip ) + throw( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + try + { + m_pStream->seekg( nBytesToSkip, ios_base::cur ); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "StdInputStream::readBytes() error: " << e.what() ); + throw io::IOException( ); + } + } + + sal_Int32 SAL_CALL StdInputStream::available( ) + throw(io::NotConnectedException, io::IOException, uno::RuntimeException ) + { + return sal::static_int_cast< sal_Int32 >( m_nLength - getPosition() ); + } + + void SAL_CALL StdInputStream::closeInput( ) + throw( io::NotConnectedException, io::IOException, uno::RuntimeException) + { + // No need to implement this for an istream + } + + void SAL_CALL StdInputStream::seek( sal_Int64 location ) + throw( lang::IllegalArgumentException, io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( location < 0 || location > m_nLength ) + throw lang::IllegalArgumentException( + "Location can't be negative or greater than the length", + static_cast< cppu::OWeakObject* >( this ), 0 ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + try + { + m_pStream->clear( ); // may be needed to rewind the stream + m_pStream->seekg( location, ios_base::beg ); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "StdInputStream::readBytes() error: " << e.what() ); + throw io::IOException( ); + } + } + + sal_Int64 SAL_CALL StdInputStream::getPosition( ) + throw( io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + sal_Int64 nPos = m_pStream->tellg( ); + if ( -1 == nPos ) + throw io::IOException( ); + + return nPos; + } + + sal_Int64 SAL_CALL StdInputStream::getLength( ) + throw ( io::IOException, uno::RuntimeException ) + { + return m_nLength; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/provider/std_outputstream.cxx b/ucbhelper/source/provider/std_outputstream.cxx new file mode 100644 index 000000000000..f2f7ebdc2746 --- /dev/null +++ b/ucbhelper/source/provider/std_outputstream.cxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright 2012 LibreOffice contributors. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ucbhelper/std_outputstream.hxx" + +using namespace std; +using namespace com::sun::star; + +namespace ucbhelper +{ + StdOutputStream::StdOutputStream( boost::shared_ptr< ostream > pStream ) : + m_pStream( pStream ) + { + } + + StdOutputStream::~StdOutputStream() + { + if ( m_pStream.get( ) ) + m_pStream->setstate( ios::eofbit ); + } + + uno::Any SAL_CALL StdOutputStream::queryInterface( const uno::Type& rType ) throw ( uno::RuntimeException ) + { + uno::Any aRet = ::cppu::queryInterface( rType, ( static_cast< XOutputStream* >( this ) ) ); + + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); + } + + void SAL_CALL StdOutputStream::acquire( ) throw( ) + { + OWeakObject::acquire(); + } + + void SAL_CALL StdOutputStream::release( ) throw( ) + { + OWeakObject::release(); + } + + void SAL_CALL StdOutputStream::writeBytes ( const uno::Sequence< sal_Int8 >& aData ) + throw ( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + try + { + m_pStream->write( reinterpret_cast< const char* >( aData.getConstArray( ) ), aData.getLength( ) ); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "Exception caught when calling write: " << e.what() ); + throw io::IOException( ); + } + } + + void SAL_CALL StdOutputStream::flush ( ) + throw ( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + try + { + m_pStream->flush( ); + } + catch ( const ios_base::failure& e ) + { + SAL_INFO( "ucbhelper", "Exception caught when calling flush: " << e.what() ); + throw io::IOException( ); + } + } + + void SAL_CALL StdOutputStream::closeOutput ( ) + throw ( io::NotConnectedException, io::BufferSizeExceededException, + io::IOException, uno::RuntimeException ) + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_pStream.get() ) + throw io::IOException( ); + + m_pStream->setstate( ios_base::eofbit ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit