summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/ext
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ucb/source/ucp/ext/ucpext.component26
-rw-r--r--ucb/source/ucp/ext/ucpext_content.cxx624
-rw-r--r--ucb/source/ucp/ext/ucpext_content.hxx134
-rw-r--r--ucb/source/ucp/ext/ucpext_datasupplier.cxx313
-rw-r--r--ucb/source/ucp/ext/ucpext_datasupplier.hxx88
-rw-r--r--ucb/source/ucp/ext/ucpext_provider.cxx171
-rw-r--r--ucb/source/ucp/ext/ucpext_provider.hxx54
-rw-r--r--ucb/source/ucp/ext/ucpext_resultset.cxx75
-rw-r--r--ucb/source/ucp/ext/ucpext_resultset.hxx61
9 files changed, 1546 insertions, 0 deletions
diff --git a/ucb/source/ucp/ext/ucpext.component b/ucb/source/ucp/ext/ucpext.component
new file mode 100644
index 000000000..879837e5f
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.comp.ucp.ext.ContentProvider"
+ constructor="ucb_ext_ContentProvider_get_implementation" single-instance="true">
+ <service name="com.sun.star.ucb.ExtensionContentProvider"/>
+ </implementation>
+</component>
diff --git a/ucb/source/ucp/ext/ucpext_content.cxx b/ucb/source/ucp/ext/ucpext_content.cxx
new file mode 100644
index 000000000..71d6cbbae
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_content.cxx
@@ -0,0 +1,624 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ucpext_content.hxx"
+#include "ucpext_provider.hxx"
+#include "ucpext_resultset.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#include <com/sun/star/deployment/PackageInformationProvider.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/content.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <string_view>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::ucb::XContentIdentifier;
+ using ::com::sun::star::ucb::XCommandEnvironment;
+ using ::com::sun::star::ucb::Command;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::ucb::OpenCommandArgument2;
+ using ::com::sun::star::ucb::XDynamicResultSet;
+ using ::com::sun::star::ucb::UnsupportedCommandException;
+ using ::com::sun::star::sdbc::XRow;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::lang::IllegalAccessException;
+ using ::com::sun::star::ucb::CommandInfo;
+ using ::com::sun::star::deployment::PackageInformationProvider;
+ using ::com::sun::star::deployment::XPackageInformationProvider;
+
+ namespace OpenMode = ::com::sun::star::ucb::OpenMode;
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+ //= helper
+
+ namespace
+ {
+
+ OUString lcl_compose( std::u16string_view i_rBaseURL, const OUString& i_rRelativeURL )
+ {
+ ENSURE_OR_RETURN( !i_rBaseURL.empty(), "illegal base URL", i_rRelativeURL );
+
+ OUStringBuffer aComposer( i_rBaseURL );
+ if ( !o3tl::ends_with(i_rBaseURL, u"/") )
+ aComposer.append( '/' );
+ aComposer.append( i_rRelativeURL );
+ return aComposer.makeStringAndClear();
+ }
+
+
+ struct SelectPropertyName
+ {
+ const OUString& operator()( const Property& i_rProperty ) const
+ {
+ return i_rProperty.Name;
+ }
+ };
+ }
+
+
+ //= Content
+
+
+ Content::Content( const Reference< XComponentContext >& rxContext, ::ucbhelper::ContentProviderImplHelper* i_pProvider,
+ const Reference< XContentIdentifier >& i_rIdentifier )
+ :Content_Base( rxContext, i_pProvider, i_rIdentifier )
+ ,m_eExtContentType( E_UNKNOWN )
+ {
+ const OUString sURL( getIdentifier()->getContentIdentifier() );
+ if ( denotesRootContent( sURL ) )
+ {
+ m_eExtContentType = E_ROOT;
+ }
+ else
+ {
+ const std::u16string_view sRelativeURL( sURL.subView( ContentProvider::getRootURL().getLength() ) );
+ const size_t nSepPos = sRelativeURL.find( '/' );
+ if ( ( nSepPos == std::u16string_view::npos ) || ( nSepPos == sRelativeURL.size() - 1 ) )
+ {
+ m_eExtContentType = E_EXTENSION_ROOT;
+ }
+ else
+ {
+ m_eExtContentType = E_EXTENSION_CONTENT;
+ }
+ }
+
+ if ( m_eExtContentType == E_ROOT )
+ return;
+
+ const OUString sRootURL = ContentProvider::getRootURL();
+ m_sExtensionId = sURL.copy( sRootURL.getLength() );
+
+ const sal_Int32 nNextSep = m_sExtensionId.indexOf( '/' );
+ if ( nNextSep > -1 )
+ {
+ m_sPathIntoExtension = m_sExtensionId.copy( nNextSep + 1 );
+ m_sExtensionId = m_sExtensionId.copy( 0, nNextSep );
+ }
+ m_sExtensionId = Content::decodeIdentifier( m_sExtensionId );
+ }
+
+
+ Content::~Content()
+ {
+ }
+
+
+ OUString SAL_CALL Content::getImplementationName()
+ {
+ return "org.openoffice.comp.ucp.ext.Content";
+ }
+
+
+ Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+ {
+ return { "com.sun.star.ucb.Content", "com.sun.star.ucb.ExtensionContent" };
+ }
+
+
+ OUString SAL_CALL Content::getContentType()
+ {
+ impl_determineContentType();
+ return *m_aContentType;
+ }
+
+
+ Any SAL_CALL Content::execute( const Command& aCommand, sal_Int32 /* CommandId */, const Reference< XCommandEnvironment >& i_rEnvironment )
+ {
+ Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+ Sequence< Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties, i_rEnvironment );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+ Sequence< PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ if ( !aProperties.hasElements() )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+ // implemented by base class.
+ aRet <<= getPropertySetInfo( i_rEnvironment );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+ // implemented by base class.
+ aRet <<= getCommandInfo( i_rEnvironment );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+ OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ bool bOpenFolder =
+ ( ( aOpenCommand.Mode == OpenMode::ALL ) ||
+ ( aOpenCommand.Mode == OpenMode::FOLDERS ) ||
+ ( aOpenCommand.Mode == OpenMode::DOCUMENTS ) );
+
+
+ if ( bOpenFolder && impl_isFolder() )
+ {
+ Reference< XDynamicResultSet > xSet = new ResultSet( m_xContext, this, aOpenCommand, i_rEnvironment );
+ aRet <<= xSet;
+ }
+
+ if ( aOpenCommand.Sink.is() )
+ {
+ const OUString sPhysicalContentURL( getPhysicalURL() );
+ ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEnvironment, m_xContext );
+ aRet = aRequestedContent.executeCommand( "open", Any( aOpenCommand ) );
+ }
+ }
+
+ else
+ {
+ ::ucbhelper::cancelCommandExecution( Any( UnsupportedCommandException(
+ OUString(), *this ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ return aRet;
+ }
+
+
+ void SAL_CALL Content::abort( sal_Int32 )
+ {
+ }
+
+
+ OUString Content::encodeIdentifier( const OUString& i_rIdentifier )
+ {
+ return ::rtl::Uri::encode( i_rIdentifier, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ OUString Content::decodeIdentifier( const OUString& i_rIdentifier )
+ {
+ return ::rtl::Uri::decode( i_rIdentifier, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ bool Content::denotesRootContent( const OUString& i_rContentIdentifier )
+ {
+ const OUString sRootURL( ContentProvider::getRootURL() );
+ if ( i_rContentIdentifier == sRootURL )
+ return true;
+
+ // the root URL contains only two trailing /, but we also recognize 3 of them as denoting the root URL
+ if ( i_rContentIdentifier.match( sRootURL )
+ && ( i_rContentIdentifier.getLength() == sRootURL.getLength() + 1 )
+ && ( i_rContentIdentifier[ i_rContentIdentifier.getLength() - 1 ] == '/' )
+ )
+ return true;
+
+ return false;
+ }
+
+
+ OUString Content::getParentURL()
+ {
+ const OUString sRootURL( ContentProvider::getRootURL() );
+
+ switch ( m_eExtContentType )
+ {
+ case E_ROOT:
+ // don't have a parent
+ return sRootURL;
+
+ case E_EXTENSION_ROOT:
+ // our parent is the root itself
+ return sRootURL;
+
+ case E_EXTENSION_CONTENT:
+ {
+ const OUString sURL = m_xIdentifier->getContentIdentifier();
+
+ // cut the root URL
+ if ( !sURL.match( sRootURL ) )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no root" );
+ break;
+ }
+
+ OUString sRelativeURL( sURL.copy( sRootURL.getLength() ) );
+
+ // cut the extension ID
+ const OUString sSeparatedExtensionId( encodeIdentifier( m_sExtensionId ) + "/" );
+ if ( !sRelativeURL.match( sSeparatedExtensionId ) )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no extension ID" );
+ break;
+ }
+
+ sRelativeURL = sRelativeURL.copy( sSeparatedExtensionId.getLength() );
+
+ // cut the final slash (if any)
+ if ( sRelativeURL.isEmpty() )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - ExtensionContent should have a level below the extension ID" );
+ break;
+ }
+
+ if ( sRelativeURL.endsWith("/") )
+ sRelativeURL = sRelativeURL.copy( 0, sRelativeURL.getLength() - 1 );
+
+ // remove the last segment
+ const sal_Int32 nLastSep = sRelativeURL.lastIndexOf( '/' );
+ sRelativeURL = sRelativeURL.copy( 0, nLastSep != -1 ? nLastSep : 0 );
+
+ return sRootURL + sSeparatedExtensionId + sRelativeURL;
+ }
+
+ default:
+ OSL_FAIL( "Content::getParentURL: unhandled case!" );
+ break;
+ }
+ return OUString();
+ }
+
+
+ Reference< XRow > Content::getArtificialNodePropertyValues( const Reference< XComponentContext >& rxContext,
+ const Sequence< Property >& i_rProperties, const OUString& i_rTitle )
+ {
+ // note: empty sequence means "get values of all supported properties".
+ ::rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ if ( i_rProperties.hasElements() )
+ {
+ for ( const Property& rProp : i_rProperties )
+ {
+ // Process Core properties.
+ if ( rProp.Name == "ContentType" )
+ {
+ xRow->appendString ( rProp, ContentProvider::getArtificialNodeContentType() );
+ }
+ else if ( rProp.Name == "Title" )
+ {
+ xRow->appendString ( rProp, i_rTitle );
+ }
+ else if ( rProp.Name == "IsDocument" )
+ {
+ xRow->appendBoolean( rProp, false );
+ }
+ else if ( rProp.Name == "IsFolder" )
+ {
+ xRow->appendBoolean( rProp, true );
+ }
+ else
+ {
+ // append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ else
+ {
+ // Append all Core Properties.
+ xRow->appendString ( Property( "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ ContentProvider::getArtificialNodeContentType() );
+ xRow->appendString ( Property( "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ i_rTitle );
+ xRow->appendBoolean( Property( "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ false );
+ xRow->appendBoolean( Property( "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ true );
+ }
+
+ return xRow;
+ }
+
+
+ OUString Content::getPhysicalURL() const
+ {
+ ENSURE_OR_RETURN( m_eExtContentType != E_ROOT, "illegal call", OUString() );
+
+ // create a ucb::XContent for the physical file within the deployed extension
+ const Reference< XPackageInformationProvider > xPackageInfo = PackageInformationProvider::get(m_xContext);
+ const OUString sPackageLocation( xPackageInfo->getPackageLocation( m_sExtensionId ) );
+
+ if ( m_sPathIntoExtension.isEmpty() )
+ return sPackageLocation;
+ return lcl_compose( sPackageLocation, m_sPathIntoExtension );
+ }
+
+
+ Reference< XRow > Content::getPropertyValues( const Sequence< Property >& i_rProperties, const Reference< XCommandEnvironment >& i_rEnv )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ switch ( m_eExtContentType )
+ {
+ case E_ROOT:
+ return getArtificialNodePropertyValues( m_xContext, i_rProperties, ContentProvider::getRootURL() );
+ case E_EXTENSION_ROOT:
+ return getArtificialNodePropertyValues( m_xContext, i_rProperties, m_sExtensionId );
+ case E_EXTENSION_CONTENT:
+ {
+ const OUString sPhysicalContentURL( getPhysicalURL() );
+ ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEnv, m_xContext );
+
+ // translate the property request
+ Sequence< OUString > aPropertyNames( i_rProperties.getLength() );
+ ::std::transform(
+ i_rProperties.begin(),
+ i_rProperties.end(),
+ aPropertyNames.getArray(),
+ SelectPropertyName()
+ );
+ const Sequence< Any > aPropertyValues = aRequestedContent.getPropertyValues( aPropertyNames );
+ const ::rtl::Reference< ::ucbhelper::PropertyValueSet > xValueRow = new ::ucbhelper::PropertyValueSet( m_xContext );
+ sal_Int32 i=0;
+ for ( const Any* value = aPropertyValues.getConstArray();
+ value != aPropertyValues.getConstArray() + aPropertyValues.getLength();
+ ++value, ++i
+ )
+ {
+ xValueRow->appendObject( aPropertyNames[i], *value );
+ }
+ return xValueRow;
+ }
+
+ default:
+ OSL_FAIL( "Content::getPropertyValues: unhandled case!" );
+ break;
+ }
+
+ OSL_FAIL( "Content::getPropertyValues: unreachable!" );
+ return nullptr;
+ }
+
+
+ Sequence< Any > Content::setPropertyValues( const Sequence< PropertyValue >& i_rValues)
+ {
+ ::osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ Sequence< Any > aRet( i_rValues.getLength() );
+
+ PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ aEvent.PropertyHandle = -1;
+
+ for ( auto& rRet : asNonConstRange(aRet) )
+ {
+ // all our properties are read-only ...
+ rRet <<= IllegalAccessException("property is read-only.", *this );
+ }
+
+ return aRet;
+ }
+
+
+ Sequence< CommandInfo > Content::getCommands( const Reference< XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const CommandInfo aCommandInfoTable[] =
+ {
+ // Mandatory commands
+
+ CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<Sequence< Property >>::get()
+ ),
+ CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<Sequence< PropertyValue >>::get()
+ )
+
+ // Optional standard commands
+
+ , CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<OpenCommandArgument2>::get()
+ )
+ };
+
+ return Sequence< CommandInfo >( aCommandInfoTable, SAL_N_ELEMENTS(aCommandInfoTable) );
+ }
+
+
+ Sequence< Property > Content::getProperties( const Reference< XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const Property aProperties[] =
+ {
+ Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ )
+ };
+ return Sequence< Property >( aProperties, SAL_N_ELEMENTS( aProperties ) );
+ }
+
+
+ bool Content::impl_isFolder()
+ {
+ if ( !!m_aIsFolder )
+ return *m_aIsFolder;
+
+ bool bIsFolder = false;
+ try
+ {
+ Sequence< Property > aProps{ { /*Name*/ "IsFolder", {}, {}, {} } };
+ Reference< XRow > xRow( getPropertyValues( aProps, nullptr ), UNO_SET_THROW );
+ bIsFolder = xRow->getBoolean(1);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ m_aIsFolder = bIsFolder;
+ return *m_aIsFolder;
+ }
+
+
+ void Content::impl_determineContentType()
+ {
+ if ( !!m_aContentType )
+ return;
+
+ m_aContentType = ContentProvider::getArtificialNodeContentType();
+ if ( m_eExtContentType != E_EXTENSION_CONTENT )
+ return;
+
+ try
+ {
+ Sequence< Property > aProps{ { /*Name*/ "ContentType", {}, {}, {} } };
+ Reference< XRow > xRow( getPropertyValues( aProps, nullptr ), UNO_SET_THROW );
+ m_aContentType = xRow->getString(1);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ }
+
+
+} // namespace ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_content.hxx b/ucb/source/ucp/ext/ucpext_content.hxx
new file mode 100644
index 000000000..3e7136f07
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_content.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <ucbhelper/contenthelper.hxx>
+
+#include <optional>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ //= ExtensionContentType
+
+ enum ExtensionContentType
+ {
+ E_ROOT,
+ E_EXTENSION_ROOT,
+ E_EXTENSION_CONTENT,
+
+ E_UNKNOWN
+ };
+
+
+ //= ContentProvider
+
+ typedef ::ucbhelper::ContentImplHelper Content_Base;
+ class Content : public Content_Base
+ {
+ public:
+ Content(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ::ucbhelper::ContentProviderImplHelper* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier
+ );
+
+ static css::uno::Reference< css::sdbc::XRow >
+ getArtificialNodePropertyValues(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const OUString& rTitle
+ );
+
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues(
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv
+ );
+
+ static OUString
+ encodeIdentifier( const OUString& i_rIdentifier );
+ static OUString
+ decodeIdentifier( const OUString& i_rIdentifier );
+
+ virtual OUString getParentURL() override;
+
+ ExtensionContentType getExtensionContentType() const { return m_eExtContentType; }
+
+ /** retrieves the URL of the underlying physical content. Not to be called when getExtensionContentType()
+ returns E_ROOT.
+ */
+ OUString getPhysicalURL() const;
+
+ protected:
+ virtual ~Content() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XContent
+ virtual OUString SAL_CALL getContentType() override;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL
+ execute(
+ const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment
+ ) override;
+
+ virtual void SAL_CALL
+ abort(
+ sal_Int32 CommandId
+ ) override;
+
+ private:
+ virtual css::uno::Sequence< css::beans::Property > getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment >& i_rEnv ) override;
+ virtual css::uno::Sequence< css::ucb::CommandInfo > getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment >& i_rEnv ) override;
+
+ css::uno::Sequence< css::uno::Any >
+ setPropertyValues(
+ const css::uno::Sequence< css::beans::PropertyValue >& rValues
+ );
+
+ static bool denotesRootContent( const OUString& i_rContentIdentifier );
+
+ bool impl_isFolder();
+ void impl_determineContentType();
+
+ private:
+ ExtensionContentType m_eExtContentType;
+ ::std::optional< bool > m_aIsFolder;
+ ::std::optional< OUString > m_aContentType;
+ OUString m_sExtensionId;
+ OUString m_sPathIntoExtension;
+ };
+
+
+} // namespace ucb::ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_datasupplier.cxx b/ucb/source/ucp/ext/ucpext_datasupplier.cxx
new file mode 100644
index 000000000..3c515358f
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_datasupplier.cxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "ucpext_datasupplier.hxx"
+#include "ucpext_content.hxx"
+#include "ucpext_provider.hxx"
+
+#include <com/sun/star/deployment/PackageInformationProvider.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <o3tl/string_view.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <string_view>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::ucb::XContent;
+ using ::com::sun::star::ucb::XContentIdentifier;
+ using ::com::sun::star::sdbc::XRow;
+ using ::com::sun::star::ucb::IllegalIdentifierException;
+ using ::com::sun::star::deployment::PackageInformationProvider;
+ using ::com::sun::star::deployment::XPackageInformationProvider;
+ using ::com::sun::star::sdbc::XResultSet;
+
+
+ //= helper
+
+ namespace
+ {
+ OUString lcl_compose( std::u16string_view i_rBaseURL, const OUString& i_rRelativeURL )
+ {
+ ENSURE_OR_RETURN( !i_rBaseURL.empty(), "illegal base URL", i_rRelativeURL );
+
+ OUStringBuffer aComposer( i_rBaseURL );
+ if ( !o3tl::ends_with(i_rBaseURL, u"/") )
+ aComposer.append( '/' );
+ aComposer.append( i_rRelativeURL );
+ return aComposer.makeStringAndClear();
+ }
+ }
+
+
+ //= DataSupplier
+
+
+ DataSupplier::DataSupplier( const Reference< XComponentContext >& rxContext,
+ const ::rtl::Reference< Content >& i_rContent )
+ :m_xContent( i_rContent )
+ ,m_xContext( rxContext )
+ {
+ }
+
+
+ void DataSupplier::fetchData()
+ {
+ try
+ {
+ const Reference< XPackageInformationProvider > xPackageInfo = PackageInformationProvider::get( m_xContext );
+
+ const OUString sContentIdentifier( m_xContent->getIdentifier()->getContentIdentifier() );
+
+ switch ( m_xContent->getExtensionContentType() )
+ {
+ case E_ROOT:
+ {
+ const Sequence< Sequence< OUString > > aExtensionInfo( xPackageInfo->getExtensionList() );
+ for ( auto const & extInfo : aExtensionInfo )
+ {
+ if ( !extInfo.hasElements() )
+ {
+ SAL_WARN( "ucb.ucp.ext", "illegal extension info" );
+ continue;
+ }
+
+ const OUString& rLocalId = extInfo[0];
+ ResultListEntry aEntry;
+ aEntry.sId = ContentProvider::getRootURL() + Content::encodeIdentifier( rLocalId ) + "/";
+ m_aResults.push_back( aEntry );
+ }
+ }
+ break;
+ case E_EXTENSION_ROOT:
+ case E_EXTENSION_CONTENT:
+ {
+ const OUString sPackageLocation( m_xContent->getPhysicalURL() );
+ ::ucbhelper::Content aWrappedContent( sPackageLocation, getResultSet()->getEnvironment(), m_xContext );
+
+ // obtain the properties which our result set is set up for from the wrapped content
+ Sequence< OUString > aPropertyNames { "Title" };
+
+ const Reference< XResultSet > xFolderContent( aWrappedContent.createCursor( aPropertyNames ), UNO_SET_THROW );
+ const Reference< XRow > xContentRow( xFolderContent, UNO_QUERY_THROW );
+ while ( xFolderContent->next() )
+ {
+ ResultListEntry aEntry;
+ aEntry.sId = lcl_compose( sContentIdentifier, xContentRow->getString( 1 ) );
+ m_aResults.push_back( aEntry );
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "DataSupplier::fetchData: unimplemented content type!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ }
+
+
+ DataSupplier::~DataSupplier()
+ {
+ }
+
+
+ OUString DataSupplier::queryContentIdentifierString( sal_uInt32 i_nIndex )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ if ( i_nIndex < m_aResults.size() )
+ {
+ const OUString sId = m_aResults[ i_nIndex ].sId;
+ if ( !sId.isEmpty() )
+ return sId;
+ }
+
+ OSL_FAIL( "DataSupplier::queryContentIdentifierString: illegal index, or illegal result entry id!" );
+ return OUString();
+ }
+
+
+ Reference< XContentIdentifier > DataSupplier::queryContentIdentifier( sal_uInt32 i_nIndex )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ if ( i_nIndex < m_aResults.size() )
+ {
+ Reference< XContentIdentifier > xId( m_aResults[ i_nIndex ].xId );
+ if ( xId.is() )
+ return xId;
+ }
+
+ OUString sId = queryContentIdentifierString( i_nIndex );
+ if ( !sId.isEmpty() )
+ {
+ Reference< XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( sId );
+ m_aResults[ i_nIndex ].xId = xId;
+ return xId;
+ }
+
+ return Reference< XContentIdentifier >();
+ }
+
+
+ Reference< XContent > DataSupplier::queryContent( sal_uInt32 i_nIndex )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+ ENSURE_OR_RETURN( i_nIndex < m_aResults.size(), "illegal index!", nullptr );
+
+
+ ::rtl::Reference< Content > pContent( m_aResults[ i_nIndex ].pContent );
+ if ( pContent.is() )
+ return pContent;
+
+ Reference< XContentIdentifier > xId( queryContentIdentifier( i_nIndex ) );
+ if ( xId.is() )
+ {
+ try
+ {
+ Reference< XContent > xContent( m_xContent->getProvider()->queryContent( xId ) );
+ pContent.set( dynamic_cast< Content* >( xContent.get() ) );
+ OSL_ENSURE( pContent.is() || !xContent.is(), "DataSupplier::queryContent: invalid content implementation!" );
+ m_aResults[ i_nIndex ].pContent = pContent;
+ return pContent;
+
+ }
+ catch ( const IllegalIdentifierException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ }
+
+ return Reference< XContent >();
+ }
+
+
+ bool DataSupplier::getResult( sal_uInt32 i_nIndex )
+ {
+ ::osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex );
+
+ // true if result already present.
+ return m_aResults.size() > i_nIndex;
+ }
+
+
+ sal_uInt32 DataSupplier::totalCount()
+ {
+ ::osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex );
+ return m_aResults.size();
+ }
+
+
+ sal_uInt32 DataSupplier::currentCount()
+ {
+ return m_aResults.size();
+ }
+
+
+ bool DataSupplier::isCountFinal()
+ {
+ return true;
+ }
+
+
+ Reference< XRow > DataSupplier::queryPropertyValues( sal_uInt32 i_nIndex )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ ENSURE_OR_RETURN( i_nIndex < m_aResults.size(), "DataSupplier::queryPropertyValues: illegal index!", nullptr );
+
+ Reference< XRow > xRow = m_aResults[ i_nIndex ].xRow;
+ if ( xRow.is() )
+ return xRow;
+
+ ENSURE_OR_RETURN( queryContent( i_nIndex ).is(), "could not retrieve the content", nullptr );
+
+ switch ( m_xContent->getExtensionContentType() )
+ {
+ case E_ROOT:
+ {
+ const OUString& rId( m_aResults[ i_nIndex ].sId );
+ const OUString sRootURL( ContentProvider::getRootURL() );
+ OUString sTitle = Content::decodeIdentifier( rId.copy( sRootURL.getLength() ) );
+ if ( sTitle.endsWith("/") )
+ sTitle = sTitle.copy( 0, sTitle.getLength() - 1 );
+ xRow = Content::getArtificialNodePropertyValues( m_xContext, getResultSet()->getProperties(), sTitle );
+ }
+ break;
+
+ case E_EXTENSION_ROOT:
+ case E_EXTENSION_CONTENT:
+ {
+ xRow = m_aResults[ i_nIndex ].pContent->getPropertyValues(
+ getResultSet()->getProperties(), getResultSet()->getEnvironment() );
+ }
+ break;
+ default:
+ OSL_FAIL( "DataSupplier::queryPropertyValues: unhandled case!" );
+ break;
+ }
+
+ m_aResults[ i_nIndex ].xRow = xRow;
+ return xRow;
+ }
+
+
+ void DataSupplier::releasePropertyValues( sal_uInt32 i_nIndex )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ if ( i_nIndex < m_aResults.size() )
+ m_aResults[ i_nIndex ].xRow.clear();
+ }
+
+
+ void DataSupplier::close()
+ {
+ }
+
+
+ void DataSupplier::validate()
+ {
+ }
+
+
+} // namespace ucb::ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_datasupplier.hxx b/ucb/source/ucp/ext/ucpext_datasupplier.hxx
new file mode 100644
index 000000000..f0118d679
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_datasupplier.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "ucpext_content.hxx"
+
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+
+#include <vector>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ class Content;
+
+
+ //= DataSupplier
+
+ class DataSupplier : public ::ucbhelper::ResultSetDataSupplier
+ {
+ public:
+ DataSupplier(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent
+ );
+
+ void fetchData();
+
+ protected:
+ virtual ~DataSupplier() override;
+
+ virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier > queryContentIdentifier( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContent > queryContent( sal_uInt32 nIndex ) override;
+
+ virtual bool getResult( sal_uInt32 nIndex ) override;
+
+ virtual sal_uInt32 totalCount() override;
+ virtual sal_uInt32 currentCount() override;
+ virtual bool isCountFinal() override;
+
+ virtual css::uno::Reference< css::sdbc::XRow > queryPropertyValues( sal_uInt32 nIndex ) override;
+ virtual void releasePropertyValues( sal_uInt32 nIndex ) override;
+
+ virtual void close() override;
+
+ virtual void validate() override;
+
+ private:
+ struct ResultListEntry
+ {
+ OUString sId;
+ css::uno::Reference< css::ucb::XContentIdentifier > xId;
+ ::rtl::Reference< Content > pContent;
+ css::uno::Reference< css::sdbc::XRow > xRow;
+ };
+ typedef ::std::vector< ResultListEntry > ResultList;
+ ::osl::Mutex m_aMutex;
+ ResultList m_aResults;
+ ::rtl::Reference< Content > m_xContent;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ };
+
+
+} // namespace ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_provider.cxx b/ucb/source/ucp/ext/ucpext_provider.cxx
new file mode 100644
index 000000000..765ea7856
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_provider.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ucpext_provider.hxx"
+#include "ucpext_content.hxx"
+
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <cppuhelper/weak.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ustrbuf.hxx>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::ucb::XContentIdentifier;
+ using ::com::sun::star::ucb::IllegalIdentifierException;
+ using ::com::sun::star::ucb::XContent;
+ using ::com::sun::star::uno::XComponentContext;
+
+
+ //= ContentProvider
+
+
+ ContentProvider::ContentProvider( const Reference< XComponentContext >& rxContext )
+ :ContentProvider_Base( rxContext )
+ {
+ }
+
+
+ ContentProvider::~ContentProvider()
+ {
+ }
+
+
+ OUString SAL_CALL ContentProvider::getImplementationName()
+ {
+ return "org.openoffice.comp.ucp.ext.ContentProvider";
+ }
+
+
+ Sequence< OUString > SAL_CALL ContentProvider::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.ucb.ContentProvider", "com.sun.star.ucb.ExtensionContentProvider" };
+ }
+
+
+ OUString ContentProvider::getRootURL()
+ {
+ return "vnd.sun.star.extension://";
+ }
+
+
+ OUString ContentProvider::getArtificialNodeContentType()
+ {
+ return "application/vnd.sun.star.extension-content";
+ }
+
+
+ namespace
+ {
+ void lcl_ensureAndTransfer( std::u16string_view& io_rIdentifierFragment, OUStringBuffer& o_rNormalization, const sal_Unicode i_nLeadingChar )
+ {
+ if ( ( io_rIdentifierFragment.empty() ) || ( io_rIdentifierFragment[0] != i_nLeadingChar ) )
+ throw IllegalIdentifierException();
+ io_rIdentifierFragment = io_rIdentifierFragment.substr( 1 );
+ o_rNormalization.append( i_nLeadingChar );
+ }
+ }
+
+
+ Reference< XContent > SAL_CALL ContentProvider::queryContent( const Reference< XContentIdentifier >& i_rIdentifier )
+ {
+ // Check URL scheme...
+ static const OUStringLiteral sScheme( u"vnd.sun.star.extension" );
+ if ( !i_rIdentifier->getContentProviderScheme().equalsIgnoreAsciiCase( sScheme ) )
+ throw IllegalIdentifierException();
+
+ // normalize the identifier
+ const OUString sIdentifier( i_rIdentifier->getContentIdentifier() );
+
+ // the scheme needs to be lower-case
+ OUStringBuffer aComposer;
+ aComposer.append( sIdentifier.copy( 0, sScheme.getLength() ).toAsciiLowerCase() );
+
+ // one : is required after the scheme
+ std::u16string_view sRemaining( sIdentifier.subView( sScheme.getLength() ) );
+ lcl_ensureAndTransfer( sRemaining, aComposer, ':' );
+
+ // and at least one /
+ lcl_ensureAndTransfer( sRemaining, aComposer, '/' );
+
+ // the normalized form requires one additional /, but we also accept identifiers which don't have it
+ if ( sRemaining.empty() )
+ {
+ // the root content is a special case, it requires /
+ aComposer.append( "//" );
+ }
+ else
+ {
+ if ( sRemaining[0] != '/' )
+ {
+ aComposer.append( '/' );
+ aComposer.append( sRemaining );
+ }
+ else
+ {
+ lcl_ensureAndTransfer( sRemaining, aComposer, '/' );
+ // by now, we moved "vnd.sun.star.extension://" from the URL to aComposer
+ if ( sRemaining.empty() )
+ {
+ // again, it's the root content, but one / is missing
+ aComposer.append( '/' );
+ }
+ else
+ {
+ aComposer.append( sRemaining );
+ }
+ }
+ }
+ const Reference< XContentIdentifier > xNormalizedIdentifier( new ::ucbhelper::ContentIdentifier( aComposer.makeStringAndClear() ) );
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // check if a content with given id already exists...
+ Reference< XContent > xContent( queryExistingContent( xNormalizedIdentifier ) );
+ if ( xContent.is() )
+ return xContent;
+
+ // create a new content
+ xContent = new Content( m_xContext, this, xNormalizedIdentifier );
+ if ( !xContent->getIdentifier().is() )
+ throw IllegalIdentifierException();
+
+ registerNewContent( xContent );
+ return xContent;
+ }
+
+
+} // namespace ucb::ucp::ext
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_ext_ContentProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ucb::ucp::ext::ContentProvider(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_provider.hxx b/ucb/source/ucp/ext/ucpext_provider.hxx
new file mode 100644
index 000000000..e1dae3447
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_provider.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ucbhelper/providerhelper.hxx>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ //= ContentProvider
+
+ typedef ::ucbhelper::ContentProviderImplHelper ContentProvider_Base;
+ class ContentProvider : public ContentProvider_Base
+ {
+ public:
+ explicit ContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ContentProvider() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XContentProvider
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override;
+
+ public:
+ static OUString getRootURL();
+ static OUString getArtificialNodeContentType();
+ };
+
+
+} // namespace ucb::ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_resultset.cxx b/ucb/source/ucp/ext/ucpext_resultset.cxx
new file mode 100644
index 000000000..2ccbded55
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_resultset.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ucpext_resultset.hxx"
+#include "ucpext_content.hxx"
+#include "ucpext_datasupplier.hxx"
+
+#include <ucbhelper/resultset.hxx>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::ucb::OpenCommandArgument2;
+ using ::com::sun::star::ucb::XCommandEnvironment;
+
+
+ //= ResultSet
+
+
+ ResultSet::ResultSet( const Reference< XComponentContext >& rxContext, const ::rtl::Reference< Content >& i_rContent,
+ const OpenCommandArgument2& i_rCommand, const Reference< XCommandEnvironment >& i_rEnv )
+ :ResultSetImplHelper( rxContext, i_rCommand )
+ ,m_xEnvironment( i_rEnv )
+ ,m_xContent( i_rContent )
+ {
+ }
+
+
+ void ResultSet::initStatic()
+ {
+ ::rtl::Reference< DataSupplier > pDataSupplier( new DataSupplier(
+ m_xContext,
+ m_xContent
+ ) );
+ m_xResultSet1 = new ::ucbhelper::ResultSet(
+ m_xContext,
+ m_aCommand.Properties,
+ pDataSupplier,
+ m_xEnvironment
+ );
+ pDataSupplier->fetchData();
+ }
+
+
+ void ResultSet::initDynamic()
+ {
+ initStatic();
+ m_xResultSet2 = m_xResultSet1;
+ }
+
+
+} // namespace ucb::ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ext/ucpext_resultset.hxx b/ucb/source/ucp/ext/ucpext_resultset.hxx
new file mode 100644
index 000000000..abd9fee9a
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_resultset.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "ucpext_content.hxx"
+
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ class Content;
+
+
+ //= ResultSet
+
+ class ResultSet : public ::ucbhelper::ResultSetImplHelper
+ {
+ public:
+ ResultSet(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& i_rContent,
+ const css::ucb::OpenCommandArgument2& i_rCommand,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& i_rEnv
+ );
+
+ private:
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnvironment;
+ ::rtl::Reference< Content > m_xContent;
+
+ private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+ };
+
+
+} // namespace ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */