summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/hierarchy
diff options
context:
space:
mode:
Diffstat (limited to 'ucb/source/ucp/hierarchy')
-rw-r--r--ucb/source/ucp/hierarchy/dynamicresultset.cxx76
-rw-r--r--ucb/source/ucp/hierarchy/dynamicresultset.hxx45
-rw-r--r--ucb/source/ucp/hierarchy/hierarchycontent.cxx1776
-rw-r--r--ucb/source/ucp/hierarchy/hierarchycontent.hxx255
-rw-r--r--ucb/source/ucp/hierarchy/hierarchycontentcaps.cxx681
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydata.cxx1098
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydata.hxx134
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydatasource.cxx864
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydatasource.hxx83
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydatasupplier.cxx369
-rw-r--r--ucb/source/ucp/hierarchy/hierarchydatasupplier.hxx92
-rw-r--r--ucb/source/ucp/hierarchy/hierarchyprovider.cxx280
-rw-r--r--ucb/source/ucp/hierarchy/hierarchyprovider.hxx112
-rw-r--r--ucb/source/ucp/hierarchy/hierarchyuri.cxx178
-rw-r--r--ucb/source/ucp/hierarchy/hierarchyuri.hxx67
-rw-r--r--ucb/source/ucp/hierarchy/ucphier1.component31
16 files changed, 6141 insertions, 0 deletions
diff --git a/ucb/source/ucp/hierarchy/dynamicresultset.cxx b/ucb/source/ucp/hierarchy/dynamicresultset.cxx
new file mode 100644
index 000000000..e5d863bee
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/dynamicresultset.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - This implementation is not a dynamic result set!!! It only implements
+ the necessary interfaces, but never recognizes/notifies changes!!!
+
+ *************************************************************************/
+#include "hierarchydatasupplier.hxx"
+#include "dynamicresultset.hxx"
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+// DynamicResultSet Implementation.
+
+
+DynamicResultSet::DynamicResultSet(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< HierarchyContent >& rxContent,
+ const ucb::OpenCommandArgument2& rCommand )
+: ResultSetImplHelper( rxContext, rCommand ),
+ m_xContent( rxContent )
+{
+}
+
+
+// Non-interface methods.
+
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet(
+ m_xContext,
+ m_aCommand.Properties,
+ new HierarchyResultSetDataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ) );
+}
+
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet(
+ m_xContext,
+ m_aCommand.Properties,
+ new HierarchyResultSetDataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ) );
+ m_xResultSet2 = m_xResultSet1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/dynamicresultset.hxx b/ucb/source/ucp/hierarchy/dynamicresultset.hxx
new file mode 100644
index 000000000..6452c26ea
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/dynamicresultset.hxx
@@ -0,0 +1,45 @@
+/* -*- 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 <rtl/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#include "hierarchycontent.hxx"
+
+namespace hierarchy_ucp {
+
+class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+{
+ rtl::Reference< HierarchyContent > m_xContent;
+
+private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+public:
+ DynamicResultSet(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< HierarchyContent >& rxContent,
+ const css::ucb::OpenCommandArgument2& rCommand );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchycontent.cxx b/ucb/source/ucp/hierarchy/hierarchycontent.cxx
new file mode 100644
index 000000000..1a773cd24
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchycontent.cxx
@@ -0,0 +1,1776 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - optimize transfer command. "Move" should be implementable much more
+ efficient!
+
+ **************************************************************************
+
+ - Root Folder vs. 'normal' Folder
+ - root doesn't support command 'delete'
+ - root doesn't support command 'insert'
+ - root needs not created via XContentCreator - queryContent with root
+ folder id ( HIERARCHY_ROOT_FOLDER_URL ) always returns a value != 0
+ - root has no parent.
+
+ *************************************************************************/
+#include <osl/diagnose.h>
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/macros.hxx>
+#include "hierarchycontent.hxx"
+#include "hierarchyprovider.hxx"
+#include "dynamicresultset.hxx"
+#include "hierarchyuri.hxx"
+
+#include "../inc/urihelper.hxx"
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+// HierarchyContent Implementation.
+
+
+// static ( "virtual" ctor )
+rtl::Reference<HierarchyContent> HierarchyContent::create(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ // Fail, if content does not exist.
+ HierarchyContentProperties aProps;
+ if ( !loadData( rxContext, pProvider, Identifier, aProps ) )
+ return nullptr;
+
+ return new HierarchyContent( rxContext, pProvider, Identifier, aProps );
+}
+
+
+// static ( "virtual" ctor )
+rtl::Reference<HierarchyContent> HierarchyContent::create(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ const ucb::ContentInfo& Info )
+{
+ if ( Info.Type.isEmpty() )
+ return nullptr;
+
+ if ( Info.Type != HIERARCHY_FOLDER_CONTENT_TYPE && Info.Type != HIERARCHY_LINK_CONTENT_TYPE )
+ return nullptr;
+
+ return new HierarchyContent( rxContext, pProvider, Identifier, Info );
+}
+
+
+HierarchyContent::HierarchyContent(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ const HierarchyContentProperties& rProps )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_aProps( rProps ),
+ m_eState( PERSISTENT ),
+ m_pProvider( pProvider ),
+ m_bCheckedReadOnly( false ),
+ m_bIsReadOnly( true )
+{
+ setKind( Identifier );
+}
+
+
+HierarchyContent::HierarchyContent(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ const ucb::ContentInfo& Info )
+ : ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_aProps( Info.Type == HIERARCHY_FOLDER_CONTENT_TYPE ? HierarchyEntryData::FOLDER : HierarchyEntryData::LINK ),
+ m_eState( TRANSIENT ),
+ m_pProvider( pProvider ),
+ m_bCheckedReadOnly( false ),
+ m_bIsReadOnly( true )
+{
+ setKind( Identifier );
+}
+
+
+// virtual
+HierarchyContent::~HierarchyContent()
+{
+}
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL HierarchyContent::acquire()
+ noexcept
+{
+ ContentImplHelper::acquire();
+}
+
+
+// virtual
+void SAL_CALL HierarchyContent::release()
+ noexcept
+{
+ ContentImplHelper::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL HierarchyContent::queryInterface( const uno::Type & rType )
+{
+ uno::Any aRet = ContentImplHelper::queryInterface( rType );
+
+ if ( !aRet.hasValue() )
+ {
+ // Note: isReadOnly may be relative expensive. So avoid calling it
+ // unless it is really necessary.
+ aRet = cppu::queryInterface(
+ rType, static_cast< ucb::XContentCreator * >( this ) );
+ if ( aRet.hasValue() )
+ {
+ if ( !isFolder() || isReadOnly() )
+ return uno::Any();
+ }
+ }
+
+ return aRet;
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( HierarchyContent );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL HierarchyContent::getTypes()
+{
+ if ( isFolder() && !isReadOnly() )
+ {
+ static cppu::OTypeCollection s_aFolderTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ),
+ CPPU_TYPE_REF( ucb::XContentCreator ) );
+
+
+ return s_aFolderTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aDocumentTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+
+ return s_aDocumentTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL HierarchyContent::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.HierarchyContent";
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL
+HierarchyContent::getSupportedServiceNames()
+{
+ uno::Sequence< OUString > aSNS( 1 );
+
+ if ( m_eKind == LINK )
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyLinkContent";
+ else if ( m_eKind == FOLDER )
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyFolderContent";
+ else
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyRootFolderContent";
+
+ return aSNS;
+}
+
+
+// XContent methods.
+
+
+// virtual
+OUString SAL_CALL HierarchyContent::getContentType()
+{
+ return m_aProps.getContentType();
+}
+
+
+// virtual
+uno::Reference< ucb::XContentIdentifier > SAL_CALL
+HierarchyContent::getIdentifier()
+{
+ // Transient?
+ if ( m_eState == TRANSIENT )
+ {
+ // Transient contents have no identifier.
+ return uno::Reference< ucb::XContentIdentifier >();
+ }
+
+ return ContentImplHelper::getIdentifier();
+}
+
+
+// XCommandProcessor methods.
+
+
+// virtual
+uno::Any SAL_CALL HierarchyContent::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+
+ // getPropertyValues
+
+
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+
+ // setPropertyValues
+
+
+ uno::Sequence< beans::PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !aProperties.hasElements() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "No properties!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties, Environment );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+
+ // getPropertySetInfo
+
+
+ aRet <<= getPropertySetInfo( Environment );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+
+ // getCommandInfo
+
+
+ aRet <<= getCommandInfo( Environment );
+ }
+ else if ( aCommand.Name == "open" && isFolder() )
+ {
+
+ // open command for a folder content
+
+
+ ucb::OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet( m_xContext, this, aOpenCommand );
+ aRet <<= xSet;
+ }
+ else if ( aCommand.Name == "insert" && ( m_eKind != ROOT ) && !isReadOnly() )
+ {
+
+ // insert
+ // ( Not available at root folder )
+
+
+ ucb::InsertCommandArgument aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ sal_Int32 nNameClash = aArg.ReplaceExisting
+ ? ucb::NameClash::OVERWRITE
+ : ucb::NameClash::ERROR;
+ insert( nNameClash, Environment );
+ }
+ else if ( aCommand.Name == "delete" && ( m_eKind != ROOT ) && !isReadOnly() )
+ {
+
+ // delete
+ // ( Not available at root folder )
+
+
+ bool bDeletePhysical = false;
+ aCommand.Argument >>= bDeletePhysical;
+ destroy( bDeletePhysical, Environment );
+
+ // Remove own and all children's persistent data.
+ if ( !removeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ Environment,
+ "Cannot remove persistent data!",
+ this );
+ // Unreachable
+ }
+
+ // Remove own and all children's Additional Core Properties.
+ removeAdditionalPropertySet();
+ }
+ else if ( aCommand.Name == "transfer" && isFolder() && !isReadOnly() )
+ {
+
+ // transfer
+ // ( Not available at link objects )
+
+
+ ucb::TransferInfo aInfo;
+ if ( !( aCommand.Argument >>= aInfo ) )
+ {
+ OSL_FAIL( "Wrong argument type!" );
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ transfer( aInfo, Environment );
+ }
+ else if ( aCommand.Name == "createNewContent" && isFolder() && !isReadOnly() )
+ {
+
+ // createNewContent
+ // ( Not available at link objects )
+
+
+ ucb::ContentInfo aInfo;
+ if ( !( aCommand.Argument >>= aInfo ) )
+ {
+ OSL_FAIL( "Wrong argument type!" );
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= createNewContent( aInfo );
+ }
+ else
+ {
+
+ // Unsupported command
+
+
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ return aRet;
+}
+
+
+// virtual
+void SAL_CALL HierarchyContent::abort( sal_Int32 /*CommandId*/ )
+{
+ // @@@ Generally, no action takes much time...
+}
+
+
+// XContentCreator methods.
+
+
+// virtual
+uno::Sequence< ucb::ContentInfo > SAL_CALL
+HierarchyContent::queryCreatableContentsInfo()
+{
+ return m_aProps.getCreatableContentsInfo();
+}
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+HierarchyContent::createNewContent( const ucb::ContentInfo& Info )
+{
+ if ( isFolder() )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( Info.Type.isEmpty() )
+ return uno::Reference< ucb::XContent >();
+
+ bool bCreateFolder = Info.Type == HIERARCHY_FOLDER_CONTENT_TYPE;
+
+ if ( !bCreateFolder && Info.Type != HIERARCHY_LINK_CONTENT_TYPE )
+ return uno::Reference< ucb::XContent >();
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ OSL_ENSURE( !aURL.isEmpty(),
+ "HierarchyContent::createNewContent - empty identifier!" );
+
+ if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
+ aURL += "/";
+
+ if ( bCreateFolder )
+ aURL += "New_Folder";
+ else
+ aURL += "New_Link";
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aURL );
+
+ return create( m_xContext, m_pProvider, xId, Info );
+ }
+ else
+ {
+ OSL_FAIL( "createNewContent called on non-folder object!" );
+ return uno::Reference< ucb::XContent >();
+ }
+}
+
+
+// virtual
+OUString HierarchyContent::getParentURL()
+{
+ HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
+ return aUri.getParentUri();
+}
+
+
+//static
+bool HierarchyContent::hasData(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ OUString aURL = Identifier->getContentIdentifier();
+
+ // Am I a root folder?
+ HierarchyUri aUri( aURL );
+ if ( aUri.isRootFolder() )
+ {
+ // hasData must always return 'true' for root folder
+ // even if no persistent data exist!!!
+ return true;
+ }
+
+ return HierarchyEntry( rxContext, pProvider, aURL ).hasData();
+}
+
+
+//static
+bool HierarchyContent::loadData(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ HierarchyContentProperties& rProps )
+{
+ OUString aURL = Identifier->getContentIdentifier();
+
+ // Am I a root folder?
+ HierarchyUri aUri( aURL );
+ if ( aUri.isRootFolder() )
+ {
+ rProps = HierarchyContentProperties( HierarchyEntryData::FOLDER );
+ }
+ else
+ {
+ HierarchyEntry aEntry( rxContext, pProvider, aURL );
+ HierarchyEntryData aData;
+ if ( !aEntry.getData( aData ) )
+ return false;
+
+ rProps = HierarchyContentProperties( aData );
+ }
+ return true;
+}
+
+
+bool HierarchyContent::storeData()
+{
+ HierarchyEntry aEntry(
+ m_xContext, m_pProvider, m_xIdentifier->getContentIdentifier() );
+ return aEntry.setData( m_aProps.getHierarchyEntryData() );
+}
+
+
+void HierarchyContent::renameData(
+ const uno::Reference< ucb::XContentIdentifier >& xOldId,
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ HierarchyEntry aEntry(
+ m_xContext, m_pProvider, xOldId->getContentIdentifier() );
+ aEntry.move( xNewId->getContentIdentifier(),
+ m_aProps.getHierarchyEntryData() );
+}
+
+
+bool HierarchyContent::removeData()
+{
+ HierarchyEntry aEntry(
+ m_xContext, m_pProvider, m_xIdentifier->getContentIdentifier() );
+ return aEntry.remove();
+}
+
+
+void HierarchyContent::setKind(
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ if ( m_aProps.getIsFolder() )
+ {
+ // Am I a root folder?
+ HierarchyUri aUri( Identifier->getContentIdentifier() );
+ if ( aUri.isRootFolder() )
+ m_eKind = ROOT;
+ else
+ m_eKind = FOLDER;
+ }
+ else
+ m_eKind = LINK;
+}
+
+
+bool HierarchyContent::isReadOnly()
+{
+ if ( !m_bCheckedReadOnly )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ if ( !m_bCheckedReadOnly )
+ {
+ m_bCheckedReadOnly = true;
+ m_bIsReadOnly = true;
+
+ HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
+ uno::Reference< lang::XMultiServiceFactory > xConfigProv
+ = m_pProvider->getConfigProvider( aUri.getService() );
+ if ( xConfigProv.is() )
+ {
+ uno::Sequence< OUString > aNames
+ = xConfigProv->getAvailableServiceNames();
+ m_bIsReadOnly = comphelper::findValue(aNames, "com.sun.star.ucb.HierarchyDataReadWriteAccess") == -1;
+ }
+ }
+ }
+
+ return m_bIsReadOnly;
+}
+
+
+uno::Reference< ucb::XContentIdentifier >
+HierarchyContent::makeNewIdentifier( const OUString& rTitle )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Assemble new content identifier...
+ HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
+ OUString aNewURL = aUri.getParentUri() + "/";
+ aNewURL += ::ucb_impl::urihelper::encodeSegment( rTitle );
+
+ return uno::Reference< ucb::XContentIdentifier >(
+ new ::ucbhelper::ContentIdentifier( aNewURL ) );
+}
+
+
+void HierarchyContent::queryChildren( HierarchyContentRefVector& rChildren )
+{
+ if ( ( m_eKind != FOLDER ) && ( m_eKind != ROOT ) )
+ return;
+
+ // Obtain a list with a snapshot of all currently instantiated contents
+ // from provider and extract the contents which are direct children
+ // of this content.
+
+ ::ucbhelper::ContentRefList aAllContents;
+ m_xProvider->queryExistingContents( aAllContents );
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
+
+ if ( nURLPos != ( aURL.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aURL += "/";
+ }
+
+ sal_Int32 nLen = aURL.getLength();
+
+ for ( const auto& rContent : aAllContents )
+ {
+ ::ucbhelper::ContentImplHelperRef xChild = rContent;
+ OUString aChildURL
+ = xChild->getIdentifier()->getContentIdentifier();
+
+ // Is aURL a prefix of aChildURL?
+ if ( ( aChildURL.getLength() > nLen ) &&
+ ( aChildURL.startsWith( aURL ) ) )
+ {
+ sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
+
+ if ( ( nPos == -1 ) ||
+ ( nPos == ( aChildURL.getLength() - 1 ) ) )
+ {
+ // No further slashes/ only a final slash. It's a child!
+ rChildren.emplace_back(
+ static_cast< HierarchyContent * >( xChild.get() ) );
+ }
+ }
+ }
+}
+
+
+bool HierarchyContent::exchangeIdentity(
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ if ( !xNewId.is() )
+ return false;
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Already persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ OSL_FAIL( "HierarchyContent::exchangeIdentity - Not persistent!" );
+ return false;
+ }
+
+ // Am I the root folder?
+ if ( m_eKind == ROOT )
+ {
+ OSL_FAIL( "HierarchyContent::exchangeIdentity - "
+ "Not supported by root folder!" );
+ return false;
+ }
+
+ // Exchange own identity.
+
+ // Fail, if a content with given id already exists.
+ if ( !hasData( xNewId ) )
+ {
+ OUString aOldURL = m_xIdentifier->getContentIdentifier();
+
+ aGuard.clear();
+ if ( exchange( xNewId ) )
+ {
+ if ( m_eKind == FOLDER )
+ {
+ // Process instantiated children...
+
+ HierarchyContentRefVector aChildren;
+ queryChildren( aChildren );
+
+ for ( const auto& rChild : aChildren )
+ {
+ HierarchyContentRef xChild = rChild;
+
+ // Create new content identifier for the child...
+ uno::Reference< ucb::XContentIdentifier > xOldChildId
+ = xChild->getIdentifier();
+ OUString aOldChildURL
+ = xOldChildId->getContentIdentifier();
+ OUString aNewChildURL
+ = aOldChildURL.replaceAt(
+ 0,
+ aOldURL.getLength(),
+ xNewId->getContentIdentifier() );
+ uno::Reference< ucb::XContentIdentifier > xNewChildId
+ = new ::ucbhelper::ContentIdentifier( aNewChildURL );
+
+ if ( !xChild->exchangeIdentity( xNewChildId ) )
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ OSL_FAIL( "HierarchyContent::exchangeIdentity - "
+ "Panic! Cannot exchange identity!" );
+ return false;
+}
+
+
+// static
+uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ const HierarchyContentProperties& rData,
+ HierarchyContentProvider* pProvider,
+ const OUString& rContentId )
+{
+ // Note: Empty sequence means "get values of all supported properties".
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ if ( rProperties.hasElements() )
+ {
+ uno::Reference< beans::XPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ for ( const beans::Property& rProp : rProperties )
+ {
+ // Process Core properties.
+
+ if ( rProp.Name == "ContentType" )
+ {
+ xRow->appendString ( rProp, rData.getContentType() );
+ }
+ else if ( rProp.Name == "Title" )
+ {
+ xRow->appendString ( rProp, rData.getTitle() );
+ }
+ else if ( rProp.Name == "IsDocument" )
+ {
+ xRow->appendBoolean( rProp, rData.getIsDocument() );
+ }
+ else if ( rProp.Name == "IsFolder" )
+ {
+ xRow->appendBoolean( rProp, rData.getIsFolder() );
+ }
+ else if ( rProp.Name == "CreatableContentsInfo" )
+ {
+ xRow->appendObject(
+ rProp, uno::Any( rData.getCreatableContentsInfo() ) );
+ }
+ else if ( rProp.Name == "TargetURL" )
+ {
+ // TargetURL is only supported by links.
+
+ if ( rData.getIsDocument() )
+ xRow->appendString( rProp, rData.getTargetURL() );
+ else
+ xRow->appendVoid( rProp );
+ }
+ else
+ {
+ // Not a Core Property! Maybe it's an Additional Core Property?!
+
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet =
+ pProvider->getAdditionalPropertySet( rContentId,
+ false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ if ( !xRow->appendPropertySetValue(
+ xAdditionalPropSet,
+ rProp ) )
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ else
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Append all Core Properties.
+ xRow->appendString (
+ beans::Property( "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getContentType() );
+ xRow->appendString (
+ beans::Property( "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ // @@@ Might actually be read-only!
+ beans::PropertyAttribute::BOUND ),
+ rData.getTitle() );
+ xRow->appendBoolean(
+ beans::Property( "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getIsDocument() );
+ xRow->appendBoolean(
+ beans::Property( "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getIsFolder() );
+
+ if ( rData.getIsDocument() )
+ xRow->appendString(
+ beans::Property( "TargetURL",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ // @@@ Might actually be read-only!
+ beans::PropertyAttribute::BOUND ),
+ rData.getTargetURL() );
+ xRow->appendObject(
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ uno::Any( rData.getCreatableContentsInfo() ) );
+
+ // Append all Additional Core Properties.
+
+ uno::Reference< beans::XPropertySet > xSet =
+ pProvider->getAdditionalPropertySet( rContentId, false );
+ xRow->appendPropertySet( xSet );
+ }
+
+ return xRow;
+}
+
+
+uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ return getPropertyValues( m_xContext,
+ rProperties,
+ m_aProps,
+ m_pProvider,
+ m_xIdentifier->getContentIdentifier() );
+}
+
+
+uno::Sequence< uno::Any > HierarchyContent::setPropertyValues(
+ const uno::Sequence< beans::PropertyValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ResettableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< uno::Any > aRet( rValues.getLength() );
+ auto aRetRange = asNonConstRange(aRet);
+ uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
+ sal_Int32 nChanged = 0;
+
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+// aEvent.PropertyName =
+ aEvent.PropertyHandle = -1;
+// aEvent.OldValue =
+// aEvent.NewValue =
+
+ const beans::PropertyValue* pValues = rValues.getConstArray();
+ sal_Int32 nCount = rValues.getLength();
+
+ uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ bool bExchange = false;
+ OUString aOldTitle;
+ OUString aOldName;
+ sal_Int32 nTitlePos = -1;
+
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::PropertyValue& rValue = pValues[ n ];
+
+ if ( rValue.Name == "ContentType" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "IsDocument" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "IsFolder" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "CreatableContentsInfo" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "Title" )
+ {
+ if ( isReadOnly() )
+ {
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty titles!
+ if ( !aNewValue.isEmpty() )
+ {
+ if ( aNewValue != m_aProps.getTitle() )
+ {
+ // modified title -> modified URL -> exchange !
+ if ( m_eState == PERSISTENT )
+ bExchange = true;
+
+ aOldTitle = m_aProps.getTitle();
+ aOldName = m_aProps.getName();
+
+ m_aProps.setTitle( aNewValue );
+ m_aProps.setName(
+ ::ucb_impl::urihelper::encodeSegment(
+ aNewValue ) );
+
+ // property change event will be set later...
+
+ // remember position within sequence of values
+ // (for error handling).
+ nTitlePos = n;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= lang::IllegalArgumentException(
+ "Empty title not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= beans::IllegalTypeException(
+ "Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ else if ( rValue.Name == "TargetURL" )
+ {
+ if ( isReadOnly() )
+ {
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ // TargetURL is only supported by links.
+
+ if ( m_eKind == LINK )
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty target URL's!
+ if ( !aNewValue.isEmpty() )
+ {
+ if ( aNewValue != m_aProps.getTargetURL() )
+ {
+ aEvent.PropertyName = rValue.Name;
+ aEvent.OldValue <<= m_aProps.getTargetURL();
+ aEvent.NewValue <<= aNewValue;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+
+ m_aProps.setTargetURL( aNewValue );
+ nChanged++;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= lang::IllegalArgumentException(
+ "Empty target URL not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= beans::IllegalTypeException(
+ "Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= beans::UnknownPropertyException(
+ "TargetURL only supported by links!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ else
+ {
+ // Not a Core Property! Maybe it's an Additional Core Property?!
+
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet = getAdditionalPropertySet( false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ try
+ {
+ uno::Any aOldValue = xAdditionalPropSet->getPropertyValue(
+ rValue.Name );
+ if ( aOldValue != rValue.Value )
+ {
+ xAdditionalPropSet->setPropertyValue(
+ rValue.Name, rValue.Value );
+
+ aEvent.PropertyName = rValue.Name;
+ aEvent.OldValue = aOldValue;
+ aEvent.NewValue = rValue.Value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( beans::UnknownPropertyException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::WrappedTargetException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( beans::PropertyVetoException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::IllegalArgumentException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= uno::Exception(
+ "No property set for storing the value!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+
+ if ( bExchange )
+ {
+ uno::Reference< ucb::XContentIdentifier > xOldId
+ = m_xIdentifier;
+ uno::Reference< ucb::XContentIdentifier > xNewId
+ = makeNewIdentifier( m_aProps.getTitle() );
+
+ aGuard.clear();
+ if ( exchangeIdentity( xNewId ) )
+ {
+ // Adapt persistent data.
+ renameData( xOldId, xNewId );
+
+ // Adapt Additional Core Properties.
+ renameAdditionalPropertySet( xOldId->getContentIdentifier(),
+ xNewId->getContentIdentifier() );
+ }
+ else
+ {
+ // Roll-back.
+ m_aProps.setTitle( aOldTitle );
+ m_aProps.setName ( aOldName );
+
+ aOldTitle.clear();
+ aOldName.clear();
+
+ // Set error .
+ aRetRange[ nTitlePos ] <<= uno::Exception(
+ "Exchange failed!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ aGuard.reset();
+ }
+
+ if ( !aOldTitle.isEmpty() )
+ {
+ aEvent.PropertyName = "Title";
+ aEvent.OldValue <<= aOldTitle;
+ aEvent.NewValue <<= m_aProps.getTitle();
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+
+ if ( nChanged > 0 )
+ {
+ // Save changes, if content was already made persistent.
+ if ( !bExchange && ( m_eState == PERSISTENT ) )
+ {
+ if ( !storeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot store persistent data!",
+ this );
+ // Unreachable
+ }
+ }
+
+ aChanges.realloc( nChanged );
+
+ aGuard.clear();
+ notifyPropertiesChange( aChanges );
+ }
+
+ return aRet;
+}
+
+
+void HierarchyContent::insert( sal_Int32 nNameClashResolve,
+ const uno::Reference<
+ ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ // Am I the root folder?
+ if ( m_eKind == ROOT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not supported by root folder!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Check, if all required properties were set.
+ if ( m_aProps.getTitle().isEmpty() )
+ {
+ uno::Sequence<OUString> aProps { "Title" };
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::MissingPropertiesException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ aProps ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Assemble new content identifier...
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = makeNewIdentifier( m_aProps.getTitle() );
+
+ // Handle possible name clash...
+
+ switch ( nNameClashResolve )
+ {
+ // fail.
+ case ucb::NameClash::ERROR:
+ if ( hasData( xId ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ m_aProps.getTitle() ) ),
+ xEnv );
+ // Unreachable
+ }
+ break;
+
+ // replace existing object.
+ case ucb::NameClash::OVERWRITE:
+ break;
+
+ // "invent" a new valid title.
+ case ucb::NameClash::RENAME:
+ if ( hasData( xId ) )
+ {
+ sal_Int32 nTry = 0;
+
+ do
+ {
+ OUString aNewId = xId->getContentIdentifier() + "_";
+ aNewId += OUString::number( ++nTry );
+ xId = new ::ucbhelper::ContentIdentifier( aNewId );
+ }
+ while ( hasData( xId ) && ( nTry < 1000 ) );
+
+ if ( nTry == 1000 )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedNameClashException(
+ "Unable to resolve name clash!",
+ static_cast< cppu::OWeakObject * >( this ),
+ nNameClashResolve ) ),
+ xEnv );
+ // Unreachable
+ }
+ else
+ {
+ OUString aNewTitle( m_aProps.getTitle() + "_" + OUString::number( nTry ) );
+ m_aProps.setTitle( aNewTitle );
+ }
+ }
+ break;
+
+ case ucb::NameClash::KEEP: // deprecated
+ case ucb::NameClash::ASK:
+ default:
+ if ( hasData( xId ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedNameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ nNameClashResolve ) ),
+ xEnv );
+ // Unreachable
+ }
+ break;
+ }
+
+ // Identifier changed?
+ bool bNewId = ( xId->getContentIdentifier()
+ != m_xIdentifier->getContentIdentifier() );
+ m_xIdentifier = xId;
+
+ if ( !storeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot store persistent data!",
+ this );
+ // Unreachable
+ }
+
+ m_eState = PERSISTENT;
+
+ if ( bNewId )
+ {
+ aGuard.clear();
+ inserted();
+ }
+}
+
+
+void HierarchyContent::destroy( bool bDeletePhysical,
+ const uno::Reference<
+ ucb::XCommandEnvironment > & xEnv )
+{
+ // @@@ take care about bDeletePhysical -> trashcan support
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not persistent!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Am I the root folder?
+ if ( m_eKind == ROOT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not supported by root folder!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ m_eState = DEAD;
+
+ aGuard.clear();
+ deleted();
+
+ if ( m_eKind == FOLDER )
+ {
+ // Process instantiated children...
+
+ HierarchyContentRefVector aChildren;
+ queryChildren( aChildren );
+
+ for ( auto & child : aChildren)
+ {
+ child->destroy( bDeletePhysical, xEnv );
+ }
+ }
+}
+
+
+void HierarchyContent::transfer(
+ const ucb::TransferInfo& rInfo,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ // Persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not persistent!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Is source a hierarchy content?
+ if ( !rInfo.SourceURL.startsWith( HIERARCHY_URL_SCHEME ":/" ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::InteractiveBadTransferURLException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Is source not a parent of me / not me?
+ OUString aId = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nPos = aId.lastIndexOf( '/' );
+ if ( nPos != ( aId.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aId += "/";
+ }
+
+ if ( rInfo.SourceURL.getLength() <= aId.getLength() )
+ {
+ if ( aId.startsWith( rInfo.SourceURL ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_RECURSIVE,
+ aArgs,
+ xEnv,
+ "Target is equal to or is a child of source!",
+ this );
+ // Unreachable
+ }
+ }
+
+
+ // 0) Obtain content object for source.
+
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );
+
+ // Note: The static cast is okay here, because its sure that
+ // m_xProvider is always the HierarchyContentProvider.
+ rtl::Reference< HierarchyContent > xSource;
+
+ try
+ {
+ xSource = static_cast< HierarchyContent * >(
+ m_xProvider->queryContent( xId ).get() );
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+
+ if ( !xSource.is() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(xId->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ xEnv,
+ "Cannot instantiate source object!",
+ this );
+ // Unreachable
+ }
+
+
+ // 1) Create new child content.
+
+
+ OUString aType = xSource->isFolder()
+ ? OUString( HIERARCHY_FOLDER_CONTENT_TYPE )
+ : OUString( HIERARCHY_LINK_CONTENT_TYPE );
+ ucb::ContentInfo aContentInfo;
+ aContentInfo.Type = aType;
+ aContentInfo.Attributes = 0;
+
+ // Note: The static cast is okay here, because its sure that
+ // createNewContent always creates a HierarchyContent.
+ rtl::Reference< HierarchyContent > xTarget
+ = static_cast< HierarchyContent * >(
+ createNewContent( aContentInfo ).get() );
+ if ( !xTarget.is() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Folder", uno::Any(aId)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_CREATE,
+ aArgs,
+ xEnv,
+ "XContentCreator::createNewContent failed!",
+ this );
+ // Unreachable
+ }
+
+
+ // 2) Copy data from source content to child content.
+
+
+ uno::Sequence< beans::Property > aSourceProps
+ = xSource->getPropertySetInfo( xEnv )->getProperties();
+ sal_Int32 nCount = aSourceProps.getLength();
+
+ if ( nCount )
+ {
+ bool bHadTitle = rInfo.NewTitle.isEmpty();
+
+ // Get all source values.
+ uno::Reference< sdbc::XRow > xRow
+ = xSource->getPropertyValues( aSourceProps );
+
+ uno::Sequence< beans::PropertyValue > aValues( nCount );
+ beans::PropertyValue* pValues = aValues.getArray();
+
+ const beans::Property* pProps = aSourceProps.getConstArray();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::Property& rProp = pProps[ n ];
+ beans::PropertyValue& rValue = pValues[ n ];
+
+ rValue.Name = rProp.Name;
+ rValue.Handle = rProp.Handle;
+
+ if ( !bHadTitle && rProp.Name == "Title" )
+ {
+ // Set new title instead of original.
+ bHadTitle = true;
+ rValue.Value <<= rInfo.NewTitle;
+ }
+ else
+ rValue.Value = xRow->getObject(
+ n + 1,
+ uno::Reference< container::XNameAccess >() );
+
+ rValue.State = beans::PropertyState_DIRECT_VALUE;
+
+ if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE )
+ {
+ // Add Additional Core Property.
+ try
+ {
+ xTarget->addProperty( rProp.Name,
+ rProp.Attributes,
+ rValue.Value );
+ }
+ catch ( beans::PropertyExistException const & )
+ {
+ }
+ catch ( beans::IllegalTypeException const & )
+ {
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ }
+ }
+ }
+
+ // Set target values.
+ xTarget->setPropertyValues( aValues, xEnv );
+ }
+
+
+ // 3) Commit (insert) child.
+
+
+ xTarget->insert( rInfo.NameClash, xEnv );
+
+
+ // 4) Transfer (copy) children of source.
+
+
+ if ( xSource->isFolder() )
+ {
+ HierarchyEntry aFolder(
+ m_xContext, m_pProvider, xId->getContentIdentifier() );
+ HierarchyEntry::iterator it;
+
+ while ( aFolder.next( it ) )
+ {
+ const HierarchyEntryData& rResult = *it;
+
+ OUString aChildId = xId->getContentIdentifier();
+ if ( ( aChildId.lastIndexOf( '/' ) + 1 ) != aChildId.getLength() )
+ aChildId += "/";
+
+ aChildId += rResult.getName();
+
+ ucb::TransferInfo aInfo;
+ aInfo.MoveData = false;
+ aInfo.NewTitle.clear();
+ aInfo.SourceURL = aChildId;
+ aInfo.NameClash = rInfo.NameClash;
+
+ // Transfer child to target.
+ xTarget->transfer( aInfo, xEnv );
+ }
+ }
+
+
+ // 5) Destroy source ( when moving only ) .
+
+
+ if ( !rInfo.MoveData )
+ return;
+
+ xSource->destroy( true, xEnv );
+
+ // Remove all persistent data of source and its children.
+ if ( !xSource->removeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(xSource->m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot remove persistent data of source object!",
+ this );
+ // Unreachable
+ }
+
+ // Remove own and all children's Additional Core Properties.
+ xSource->removeAdditionalPropertySet();
+}
+
+
+// HierarchyContentProperties Implementation.
+
+
+uno::Sequence< ucb::ContentInfo >
+HierarchyContentProperties::getCreatableContentsInfo() const
+{
+ if ( getIsFolder() )
+ {
+ uno::Sequence< ucb::ContentInfo > aSeq( 2 );
+
+ // Folder.
+ aSeq.getArray()[ 0 ].Type = HIERARCHY_FOLDER_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
+
+ uno::Sequence< beans::Property > aFolderProps( 1 );
+ aFolderProps.getArray()[ 0 ] = beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+ aSeq.getArray()[ 0 ].Properties = aFolderProps;
+
+ // Link.
+ aSeq.getArray()[ 1 ].Type = HIERARCHY_LINK_CONTENT_TYPE;
+ aSeq.getArray()[ 1 ].Attributes = ucb::ContentInfoAttribute::KIND_LINK;
+
+ uno::Sequence< beans::Property > aLinkProps( 2 );
+ aLinkProps.getArray()[ 0 ] = beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+ aLinkProps.getArray()[ 1 ] = beans::Property(
+ "TargetURL",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+ aSeq.getArray()[ 1 ].Properties = aLinkProps;
+
+ return aSeq;
+ }
+ else
+ {
+ return uno::Sequence< ucb::ContentInfo >( 0 );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchycontent.hxx b/ucb/source/ucp/hierarchy/hierarchycontent.hxx
new file mode 100644
index 000000000..94e75730a
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchycontent.hxx
@@ -0,0 +1,255 @@
+/* -*- 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 <vector>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include <ucbhelper/contenthelper.hxx>
+#include "hierarchydata.hxx"
+#include "hierarchyprovider.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+ struct PropertyValue;
+}
+
+namespace com::sun::star::sdbc {
+ class XRow;
+}
+
+namespace com::sun::star::ucb {
+ struct TransferInfo;
+}
+
+namespace hierarchy_ucp
+{
+
+
+class HierarchyContentProperties
+{
+public:
+ HierarchyContentProperties() {};
+
+ explicit HierarchyContentProperties( const HierarchyEntryData::Type & rType )
+ : m_aData( rType ),
+ m_aContentType( rType == HierarchyEntryData::FOLDER
+ ? OUString( HIERARCHY_FOLDER_CONTENT_TYPE )
+ : OUString( HIERARCHY_LINK_CONTENT_TYPE ) ) {}
+
+ explicit HierarchyContentProperties( const HierarchyEntryData & rData )
+ : m_aData( rData ),
+ m_aContentType( rData.getType() == HierarchyEntryData::FOLDER
+ ? OUString( HIERARCHY_FOLDER_CONTENT_TYPE )
+ : OUString( HIERARCHY_LINK_CONTENT_TYPE ) ) {}
+
+ const OUString & getName() const { return m_aData.getName(); }
+ void setName( const OUString & rName ) { m_aData.setName( rName ); };
+
+ const OUString & getTitle() const { return m_aData.getTitle(); }
+ void setTitle( const OUString & rTitle )
+ { m_aData.setTitle( rTitle ); };
+
+ const OUString & getTargetURL() const
+ { return m_aData.getTargetURL(); }
+ void setTargetURL( const OUString & rURL )
+ { m_aData.setTargetURL( rURL ); };
+
+ const OUString & getContentType() const { return m_aContentType; }
+
+ bool getIsFolder() const
+ { return m_aData.getType() == HierarchyEntryData::FOLDER; }
+
+ bool getIsDocument() const { return !getIsFolder(); }
+
+ css::uno::Sequence< css::ucb::ContentInfo >
+ getCreatableContentsInfo() const;
+
+ const HierarchyEntryData & getHierarchyEntryData() const { return m_aData; }
+
+private:
+ HierarchyEntryData m_aData;
+ OUString m_aContentType;
+};
+
+
+class HierarchyContentProvider;
+
+class HierarchyContent : public ::ucbhelper::ContentImplHelper,
+ public css::ucb::XContentCreator
+{
+ enum ContentKind { LINK, FOLDER, ROOT };
+ enum ContentState { TRANSIENT, // created via CreateNewContent,
+ // but did not process "insert" yet
+ PERSISTENT, // processed "insert"
+ DEAD // processed "delete"
+ };
+
+ HierarchyContentProperties m_aProps;
+ ContentKind m_eKind;
+ ContentState m_eState;
+ HierarchyContentProvider* m_pProvider;
+ bool m_bCheckedReadOnly;
+ bool m_bIsReadOnly;
+
+private:
+ HierarchyContent(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const HierarchyContentProperties& rProps );
+ HierarchyContent(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const css::ucb::ContentInfo& Info );
+
+ virtual css::uno::Sequence< css::beans::Property >
+ getProperties( const css::uno::Reference<css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual css::uno::Sequence< css::ucb::CommandInfo >
+ getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual OUString getParentURL() override;
+
+ static bool hasData(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier );
+ bool hasData(
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier )
+ { return hasData( m_xContext, m_pProvider, Identifier ); }
+ static bool loadData(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ HierarchyContentProperties& rProps );
+ bool storeData();
+ void renameData( const css::uno::Reference< css::ucb::XContentIdentifier >& xOldId,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+ bool removeData();
+
+ void setKind( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier );
+
+ bool isReadOnly();
+
+ bool isFolder() const { return ( m_eKind > LINK ); }
+
+ css::uno::Reference< css::ucb::XContentIdentifier >
+ makeNewIdentifier( const OUString& rTitle );
+
+ typedef rtl::Reference< HierarchyContent > HierarchyContentRef;
+ typedef std::vector< HierarchyContentRef > HierarchyContentRefVector;
+ void queryChildren( HierarchyContentRefVector& rChildren );
+
+ bool exchangeIdentity(
+ const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties );
+ /// @throws css::uno::Exception
+ css::uno::Sequence< css::uno::Any >
+ setPropertyValues(
+ const css::uno::Sequence< css::beans::PropertyValue >& rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ void insert( sal_Int32 nNameClashResolve,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ void destroy( bool bDeletePhysical,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ void transfer( const css::ucb::TransferInfo& rInfo,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+public:
+ // Create existing content. Fail, if not already exists.
+ static rtl::Reference<HierarchyContent> create(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference<
+ css::ucb::XContentIdentifier >& Identifier );
+
+ // Create new content. Fail, if already exists.
+ static rtl::Reference<HierarchyContent> create(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const css::ucb::ContentInfo& Info );
+
+ virtual ~HierarchyContent() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XContent
+ virtual OUString SAL_CALL
+ getContentType() override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
+ getIdentifier() 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;
+
+
+ // Additional interfaces
+
+
+ // XContentCreator
+ virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL
+ queryCreatableContentsInfo() override;
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createNewContent( const css::ucb::ContentInfo& Info ) override;
+
+
+ // Non-interface methods.
+
+
+ static css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const HierarchyContentProperties& rData,
+ HierarchyContentProvider* pProvider,
+ const OUString& rContentId );
+};
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchycontentcaps.cxx b/ucb/source/ucp/hierarchy/hierarchycontentcaps.cxx
new file mode 100644
index 000000000..836a136f6
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchycontentcaps.cxx
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ **************************************************************************
+
+ Props/Commands:
+
+ root folder folder link link
+ (new) (new)
+ ----------------------------------------------------------------
+ ContentType x x x x x
+ IsDocument x x x x x
+ IsFolder x x x x x
+ Title x x x x x
+ TargetURL x x
+ CreatableContentsInfo x x x x x
+
+ getCommandInfo x x x x x
+ getPropertySetInfo x x x x x
+ getPropertyValues x x x x x
+ setPropertyValues x x x x x
+ createNewContent x x
+ insert x x
+ delete x x
+ open x x
+ transfer x x
+
+ *************************************************************************/
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/macros.h>
+#include "hierarchycontent.hxx"
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+// HierarchyContent implementation.
+
+
+#define MAKEPROPSEQUENCE( a ) \
+ uno::Sequence< beans::Property >( a, SAL_N_ELEMENTS( a ) )
+
+#define MAKECMDSEQUENCE( a ) \
+ uno::Sequence< ucb::CommandInfo >( a, SAL_N_ELEMENTS( a ) )
+
+
+// IMPORTANT: If any property data ( name / type / ... ) are changed, then
+// HierarchyContent::getPropertyValues(...) must be adapted too!
+
+
+// virtual
+uno::Sequence< beans::Property > HierarchyContent::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_eKind == LINK )
+ {
+
+
+ // Link: Supported properties
+
+
+ if ( isReadOnly() )
+ {
+ static const beans::Property aLinkPropertyInfoTable[] =
+ {
+
+ // Required properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "TargetURL",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aLinkPropertyInfoTable );
+ }
+ else
+ {
+ static const beans::Property aLinkPropertyInfoTable[] =
+ {
+
+ // Required properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "TargetURL",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aLinkPropertyInfoTable );
+ }
+ }
+ else if ( m_eKind == FOLDER )
+ {
+
+
+ // Folder: Supported properties
+
+
+ if ( isReadOnly() )
+ {
+ static const beans::Property aFolderPropertyInfoTable[] =
+ {
+
+ // Required properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aFolderPropertyInfoTable );
+ }
+ else
+ {
+ static const beans::Property aFolderPropertyInfoTable[] =
+ {
+
+ // Required properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aFolderPropertyInfoTable );
+ }
+ }
+ else
+ {
+
+
+ // Root Folder: Supported properties
+
+
+ // Currently no difference between read-only/read-write
+ // -> all props are read-only
+
+ static const beans::Property aRootFolderPropertyInfoTable[] =
+ {
+
+ // Required properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aRootFolderPropertyInfoTable );
+ }
+}
+
+
+// virtual
+uno::Sequence< ucb::CommandInfo > HierarchyContent::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_eKind == LINK )
+ {
+
+
+ // Link: Supported commands
+
+
+ if ( isReadOnly() )
+ {
+ static const ucb::CommandInfo aLinkCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ )
+
+ // Optional standard commands
+
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aLinkCommandInfoTable );
+ }
+ else
+ {
+ static const ucb::CommandInfo aLinkCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aLinkCommandInfoTable );
+ }
+ }
+ else if ( m_eKind == FOLDER )
+ {
+
+
+ // Folder: Supported commands
+
+
+ if ( isReadOnly() )
+ {
+ static const ucb::CommandInfo aFolderCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aFolderCommandInfoTable );
+ }
+ else
+ {
+ static const ucb::CommandInfo aFolderCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aFolderCommandInfoTable );
+ }
+ }
+ else
+ {
+
+
+ // Root Folder: Supported commands
+
+
+ if ( isReadOnly() )
+ {
+ static const ucb::CommandInfo aRootFolderCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aRootFolderCommandInfoTable );
+ }
+ else
+ {
+ static const ucb::CommandInfo aRootFolderCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aRootFolderCommandInfoTable );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydata.cxx b/ucb/source/ucp/hierarchy/hierarchydata.cxx
new file mode 100644
index 000000000..3e802a4fd
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydata.cxx
@@ -0,0 +1,1098 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - HierarchyEntry::move
+ --> Rewrite to use XNamed ( once this is supported by config db api ).
+
+ *************************************************************************/
+#include "hierarchydata.hxx"
+
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XOfficeInstallationDirectories.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/propertysequence.hxx>
+#include "hierarchyprovider.hxx"
+#include "hierarchyuri.hxx"
+
+using namespace com::sun::star;
+
+namespace hierarchy_ucp
+{
+
+
+static void makeXMLName( const OUString & rIn, OUStringBuffer & rBuffer )
+{
+ sal_Int32 nCount = rIn.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const sal_Unicode c = rIn[ n ];
+ switch ( c )
+ {
+ case '&':
+ rBuffer.append( "&amp;" );
+ break;
+
+ case '"':
+ rBuffer.append( "&quot;" );
+ break;
+
+ case '\'':
+ rBuffer.append( "&apos;" );
+ break;
+
+ case '<':
+ rBuffer.append( "&lt;" );
+ break;
+
+ case '>':
+ rBuffer.append( "&gt;" );
+ break;
+
+ default:
+ rBuffer.append( c );
+ break;
+ }
+ }
+}
+
+
+// HierarchyEntry Implementation.
+
+
+constexpr OUStringLiteral READ_SERVICE_NAME = u"com.sun.star.ucb.HierarchyDataReadAccess";
+constexpr OUStringLiteral READWRITE_SERVICE_NAME = u"com.sun.star.ucb.HierarchyDataReadWriteAccess";
+
+// describe path of cfg entry
+constexpr OUStringLiteral CFGPROPERTY_NODEPATH = u"nodepath";
+
+
+HierarchyEntry::HierarchyEntry(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const OUString& rURL )
+: m_xContext( rxContext ),
+ m_xOfficeInstDirs( pProvider->getOfficeInstallationDirectories() ),
+ m_bTriedToGetRootReadAccess( false )
+{
+ HierarchyUri aUri( rURL );
+ m_aServiceSpecifier = aUri.getService();
+
+ m_xConfigProvider
+ = pProvider->getConfigProvider( m_aServiceSpecifier );
+ m_xRootReadAccess
+ = pProvider->getRootConfigReadNameAccess( m_aServiceSpecifier );
+
+ // Note: do not init m_aPath in init list. createPathFromHierarchyURL
+ // needs m_xContext and m_aMutex.
+ m_aPath = createPathFromHierarchyURL( aUri );
+
+ // Extract language independent name from URL.
+ sal_Int32 nPos = rURL.lastIndexOf( '/' );
+ if ( nPos > HIERARCHY_URL_SCHEME_LENGTH )
+ m_aName = rURL.copy( nPos + 1 );
+ else
+ OSL_FAIL( "HierarchyEntry - Invalid URL!" );
+}
+
+
+bool HierarchyEntry::hasData()
+{
+ uno::Reference< container::XHierarchicalNameAccess > xRootReadAccess
+ = getRootReadAccess();
+
+ OSL_ENSURE( xRootReadAccess.is(), "HierarchyEntry::hasData - No root!" );
+
+ if ( xRootReadAccess.is() )
+ return xRootReadAccess->hasByHierarchicalName( m_aPath );
+
+ return false;
+}
+
+
+bool HierarchyEntry::getData( HierarchyEntryData& rData )
+{
+ try
+ {
+ uno::Reference< container::XHierarchicalNameAccess > xRootReadAccess
+ = getRootReadAccess();
+
+ OSL_ENSURE( xRootReadAccess.is(),
+ "HierarchyEntry::getData - No root!" );
+
+ if ( xRootReadAccess.is() )
+ {
+ OUString aTitlePath = m_aPath + "/Title";
+
+ // Note: Avoid NoSuchElementExceptions, because exceptions are
+ // relatively 'expensive'. Checking for availability of
+ // title value is sufficient here, because if it is
+ // there, the other values will be available too.
+ if ( !xRootReadAccess->hasByHierarchicalName( aTitlePath ) )
+ return false;
+
+ OUString aValue;
+
+ // Get Title value.
+ if ( !( xRootReadAccess->getByHierarchicalName( aTitlePath )
+ >>= aValue ) )
+ {
+ OSL_FAIL( "HierarchyEntry::getData - "
+ "Got no Title value!" );
+ return false;
+ }
+
+ rData.setTitle( aValue );
+
+ // Get TargetURL value.
+ OUString aTargetURLPath = m_aPath + "/TargetURL";
+ if ( !( xRootReadAccess->getByHierarchicalName( aTargetURLPath )
+ >>= aValue ) )
+ {
+ OSL_FAIL( "HierarchyEntry::getData - "
+ "Got no TargetURL value!" );
+ return false;
+ }
+
+ // TargetURL property may contain a reference to the Office
+ // installation directory. To ensure a reloctable office
+ // installation, the path to the office installation directory must
+ // never be stored directly. A placeholder is used instead. Replace
+ // it by actual installation directory.
+ if ( m_xOfficeInstDirs.is() && !aValue.isEmpty() )
+ aValue = m_xOfficeInstDirs->makeAbsoluteURL( aValue );
+ rData.setTargetURL( aValue );
+
+ OUString aTypePath = m_aPath + "/Type";
+ if ( xRootReadAccess->hasByHierarchicalName( aTypePath ) )
+ {
+ // Might not be present since it was introduced long after
+ // Title and TargetURL (#82433#)... So not getting it is
+ // not an error.
+
+ // Get Type value.
+ sal_Int32 nType = 0;
+ if ( xRootReadAccess->getByHierarchicalName( aTypePath )
+ >>= nType )
+ {
+ if ( nType == 0 )
+ {
+ rData.setType( HierarchyEntryData::LINK );
+ }
+ else if ( nType == 1 )
+ {
+ rData.setType( HierarchyEntryData::FOLDER );
+ }
+ else
+ {
+ OSL_FAIL( "HierarchyEntry::getData - "
+ "Unknown Type value!" );
+ return false;
+ }
+ }
+ }
+
+ rData.setName( m_aName );
+ return true;
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // getByHierarchicalName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ return false;
+}
+
+
+bool HierarchyEntry::setData( const HierarchyEntryData& rData )
+{
+ try
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xConfigProvider.is() )
+ m_xConfigProvider.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
+ uno::UNO_QUERY );
+
+ if ( m_xConfigProvider.is() )
+ {
+ // Create parent's key. It must exist!
+
+ OUString aParentPath;
+ bool bRoot = true;
+
+ sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
+ if ( nPos != -1 )
+ {
+ // Skip "/Children" segment of the path, too.
+ nPos = m_aPath.lastIndexOf( '/', nPos - 1 );
+
+ OSL_ENSURE( nPos != -1,
+ "HierarchyEntry::setData - Wrong path!" );
+
+ aParentPath += m_aPath.subView( 0, nPos );
+ bRoot = false;
+ }
+
+ uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {CFGPROPERTY_NODEPATH, uno::Any(aParentPath)}
+ }));
+
+ uno::Reference< util::XChangesBatch > xBatch(
+ m_xConfigProvider->createInstanceWithArguments(
+ READWRITE_SERVICE_NAME,
+ aArguments ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xBatch.is(),
+ "HierarchyEntry::setData - No batch!" );
+
+ uno::Reference< container::XNameAccess > xParentNameAccess(
+ xBatch, uno::UNO_QUERY );
+
+ OSL_ENSURE( xParentNameAccess.is(),
+ "HierarchyEntry::setData - No name access!" );
+
+ if ( xBatch.is() && xParentNameAccess.is() )
+ {
+ // Try to create own key. It must not exist!
+
+ bool bExists = true;
+ uno::Any aMyKey;
+
+ try
+ {
+ uno::Reference< container::XNameAccess > xNameAccess;
+
+ if ( bRoot )
+ {
+ xNameAccess = xParentNameAccess;
+ }
+ else
+ {
+ xParentNameAccess->getByName("Children") >>= xNameAccess;
+ }
+
+ if ( xNameAccess->hasByName( m_aName ) )
+ aMyKey = xNameAccess->getByName( m_aName );
+ else
+ bExists = false;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ bExists = false;
+ }
+
+ uno::Reference< container::XNameReplace > xNameReplace;
+ uno::Reference< container::XNameContainer > xContainer;
+
+ if ( bExists )
+ {
+ // Key exists. Replace values.
+
+ aMyKey >>= xNameReplace;
+
+ OSL_ENSURE( xNameReplace.is(),
+ "HierarchyEntry::setData - No name replace!" );
+ }
+ else
+ {
+ // Key does not exist. Create / fill / insert it.
+
+ uno::Reference< lang::XSingleServiceFactory > xFac;
+
+ if ( bRoot )
+ {
+ // Special handling for children of root,
+ // which is not an entry. It's only a set
+ // of entries.
+ xFac.set( xParentNameAccess, uno::UNO_QUERY );
+ }
+ else
+ {
+ // Append new entry to parents child list,
+ // which is a set of entries.
+ xParentNameAccess->getByName("Children") >>= xFac;
+ }
+
+ OSL_ENSURE( xFac.is(),
+ "HierarchyEntry::setData - No factory!" );
+
+ if ( xFac.is() )
+ {
+ xNameReplace.set( xFac->createInstance(), uno::UNO_QUERY );
+
+ OSL_ENSURE( xNameReplace.is(),
+ "HierarchyEntry::setData - No name replace!" );
+
+ if ( xNameReplace.is() )
+ {
+ xContainer.set( xFac, uno::UNO_QUERY );
+
+ OSL_ENSURE( xContainer.is(),
+ "HierarchyEntry::setData - No container!" );
+ }
+ }
+ }
+
+ if ( xNameReplace.is() )
+ {
+ // Set Title value.
+ xNameReplace->replaceByName(
+ "Title",
+ uno::Any( rData.getTitle() ) );
+
+ // Set TargetURL value.
+
+ // TargetURL property may contain a reference to the Office
+ // installation directory. To ensure a reloctable office
+ // installation, the path to the office installation
+ // directory must never be stored directly. Use a
+ // placeholder instead.
+ OUString aValue( rData.getTargetURL() );
+ if ( m_xOfficeInstDirs.is() && !aValue.isEmpty() )
+ aValue
+ = m_xOfficeInstDirs->makeRelocatableURL( aValue );
+
+ xNameReplace->replaceByName(
+ "TargetURL",
+ uno::Any( aValue ) );
+
+ // Set Type value.
+ sal_Int32 nType
+ = rData.getType() == HierarchyEntryData::LINK ? 0 : 1;
+ xNameReplace->replaceByName(
+ "Type",
+ uno::Any( nType ) );
+
+ if ( xContainer.is() )
+ xContainer->insertByName(
+ m_aName, uno::Any( xNameReplace ) );
+
+ // Commit changes.
+ xBatch->commitChanges();
+ return true;
+ }
+ }
+ }
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // replaceByName, insertByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // replaceByName, getByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( container::ElementExistException const & )
+ {
+ // insertByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ // replaceByName, insertByName, getByName, commitChanges
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( uno::Exception const & )
+ {
+ // createInstance, createInstanceWithArguments
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ return false;
+}
+
+
+bool HierarchyEntry::move(
+ const OUString& rNewURL, const HierarchyEntryData& rData )
+{
+ OUString aNewPath = createPathFromHierarchyURL( HierarchyUri(rNewURL) );
+
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( aNewPath == m_aPath )
+ return true;
+
+ bool bOldRoot = true;
+ uno::Reference< util::XChangesBatch > xOldParentBatch;
+
+ OUString aNewKey;
+ sal_Int32 nURLPos = rNewURL.lastIndexOf( '/' );
+ if ( nURLPos > HIERARCHY_URL_SCHEME_LENGTH )
+ aNewKey = rNewURL.copy( nURLPos + 1 );
+ else
+ {
+ OSL_FAIL( "HierarchyEntry::move - Invalid URL!" );
+ return false;
+ }
+
+ bool bNewRoot = true;
+ uno::Reference< util::XChangesBatch > xNewParentBatch;
+
+ bool bDifferentParents = true;
+
+ try
+ {
+ if ( !m_xConfigProvider.is() )
+ m_xConfigProvider.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
+ uno::UNO_QUERY );
+
+ if ( !m_xConfigProvider.is() )
+ return false;
+
+ OUString aOldParentPath;
+ sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
+ if ( nPos != -1 )
+ {
+ // Skip "/Children" segment of the path, too.
+ nPos = m_aPath.lastIndexOf( '/', nPos - 1 );
+
+ OSL_ENSURE( nPos != -1, "HierarchyEntry::move - Wrong path!" );
+
+ aOldParentPath += m_aPath.subView( 0, nPos );
+ bOldRoot = false;
+ }
+
+ OUString aNewParentPath;
+ nPos = aNewPath.lastIndexOf( '/' );
+ if ( nPos != -1 )
+ {
+ // Skip "/Children" segment of the path, too.
+ nPos = aNewPath.lastIndexOf( '/', nPos - 1 );
+
+ OSL_ENSURE( nPos != -1, "HierarchyEntry::move - Wrong path!" );
+
+ aNewParentPath += aNewPath.subView( 0, nPos );
+ bNewRoot = false;
+ }
+
+ uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {CFGPROPERTY_NODEPATH, uno::Any(aOldParentPath)}
+ }));
+
+ xOldParentBatch.set(
+ m_xConfigProvider->createInstanceWithArguments(
+ READWRITE_SERVICE_NAME,
+ aArguments ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xOldParentBatch.is(), "HierarchyEntry::move - No batch!" );
+
+ if ( !xOldParentBatch.is() )
+ return false;
+
+ if ( aOldParentPath == aNewParentPath )
+ {
+ bDifferentParents = false;
+ xNewParentBatch = xOldParentBatch;
+ }
+ else
+ {
+ bDifferentParents = true;
+
+ uno::Sequence<uno::Any> aArguments2(comphelper::InitAnyPropertySequence(
+ {
+ {CFGPROPERTY_NODEPATH, uno::Any(aNewParentPath)}
+ }));
+
+ xNewParentBatch.set(
+ m_xConfigProvider->createInstanceWithArguments(
+ READWRITE_SERVICE_NAME,
+ aArguments2 ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE(
+ xNewParentBatch.is(), "HierarchyEntry::move - No batch!" );
+
+ if ( !xNewParentBatch.is() )
+ return false;
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ // createInstance, createInstanceWithArguments
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+
+ // (1) Get entry...
+
+
+ uno::Any aEntry;
+ uno::Reference< container::XNameAccess > xOldParentNameAccess;
+ uno::Reference< container::XNameContainer > xOldNameContainer;
+
+ try
+ {
+ xOldParentNameAccess.set( xOldParentBatch, uno::UNO_QUERY );
+
+ OSL_ENSURE( xOldParentNameAccess.is(),
+ "HierarchyEntry::move - No name access!" );
+
+ if ( !xOldParentNameAccess.is() )
+ return false;
+
+ if ( bOldRoot )
+ {
+ xOldNameContainer.set( xOldParentNameAccess, uno::UNO_QUERY );
+ }
+ else
+ {
+ xOldParentNameAccess->getByName("Children") >>= xOldNameContainer;
+ }
+
+ aEntry = xOldNameContainer->getByName( m_aName );
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // getByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ // getByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+
+ // (2) Remove entry... Note: Insert BEFORE remove does not work!
+
+
+ try
+ {
+ xOldNameContainer->removeByName( m_aName );
+ xOldParentBatch->commitChanges();
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // getByName, removeByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+
+ // (3) Insert entry at new parent...
+
+
+ try
+ {
+ uno::Reference< container::XNameReplace > xNewNameReplace;
+ aEntry >>= xNewNameReplace;
+
+ OSL_ENSURE( xNewNameReplace.is(),
+ "HierarchyEntry::move - No name replace!" );
+
+ if ( !xNewNameReplace.is() )
+ return false;
+
+ uno::Reference< container::XNameAccess > xNewParentNameAccess;
+ if ( bDifferentParents )
+ xNewParentNameAccess.set( xNewParentBatch, uno::UNO_QUERY );
+ else
+ xNewParentNameAccess = xOldParentNameAccess;
+
+ OSL_ENSURE( xNewParentNameAccess.is(),
+ "HierarchyEntry::move - No name access!" );
+
+ if ( !xNewParentNameAccess.is() )
+ return false;
+
+ uno::Reference< container::XNameContainer > xNewNameContainer;
+ if ( bDifferentParents )
+ {
+ if ( bNewRoot )
+ {
+ xNewNameContainer.set( xNewParentNameAccess, uno::UNO_QUERY );
+ }
+ else
+ {
+ xNewParentNameAccess->getByName("Children") >>= xNewNameContainer;
+ }
+ }
+ else
+ xNewNameContainer = xOldNameContainer;
+
+ if ( !xNewNameContainer.is() )
+ return false;
+
+ xNewNameReplace->replaceByName(
+ "Title",
+ uno::Any( rData.getTitle() ) );
+
+ // TargetURL property may contain a reference to the Office
+ // installation directory. To ensure a reloctable office
+ // installation, the path to the office installation
+ // directory must never be stored directly. Use a placeholder
+ // instead.
+ OUString aValue( rData.getTargetURL() );
+ if ( m_xOfficeInstDirs.is() && !aValue.isEmpty() )
+ aValue = m_xOfficeInstDirs->makeRelocatableURL( aValue );
+ xNewNameReplace->replaceByName(
+ "TargetURL",
+ uno::Any( aValue ) );
+ sal_Int32 nType = rData.getType() == HierarchyEntryData::LINK ? 0 : 1;
+ xNewNameReplace->replaceByName(
+ "Type",
+ uno::Any( nType ) );
+
+ xNewNameContainer->insertByName( aNewKey, aEntry );
+ xNewParentBatch->commitChanges();
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // replaceByName, insertByName, getByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // replaceByName, insertByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( container::ElementExistException const & )
+ {
+ // insertByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ // replaceByName, insertByName, getByName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool HierarchyEntry::remove()
+{
+ try
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xConfigProvider.is() )
+ m_xConfigProvider.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
+ uno::UNO_QUERY );
+
+ if ( m_xConfigProvider.is() )
+ {
+ // Create parent's key. It must exist!
+
+ OUString aParentPath;
+ bool bRoot = true;
+
+ sal_Int32 nPos = m_aPath.lastIndexOf( '/' );
+ if ( nPos != -1 )
+ {
+ // Skip "/Children" segment of the path, too.
+ nPos = m_aPath.lastIndexOf( '/', nPos - 1 );
+
+ OSL_ENSURE( nPos != -1,
+ "HierarchyEntry::remove - Wrong path!" );
+
+ aParentPath += m_aPath.subView( 0, nPos );
+ bRoot = false;
+ }
+
+ uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {CFGPROPERTY_NODEPATH, uno::Any(aParentPath)}
+ }));
+
+ uno::Reference< util::XChangesBatch > xBatch(
+ m_xConfigProvider->createInstanceWithArguments(
+ READWRITE_SERVICE_NAME,
+ aArguments ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xBatch.is(),
+ "HierarchyEntry::remove - No batch!" );
+
+ uno::Reference< container::XNameAccess > xParentNameAccess(
+ xBatch, uno::UNO_QUERY );
+
+ OSL_ENSURE( xParentNameAccess.is(),
+ "HierarchyEntry::remove - No name access!" );
+
+ if ( xBatch.is() && xParentNameAccess.is() )
+ {
+ uno::Reference< container::XNameContainer > xContainer;
+
+ if ( bRoot )
+ {
+ // Special handling for children of root,
+ // which is not an entry. It's only a set
+ // of entries.
+ xContainer.set( xParentNameAccess, uno::UNO_QUERY );
+ }
+ else
+ {
+ // Append new entry to parents child list,
+ // which is a set of entries.
+ xParentNameAccess->getByName("Children") >>= xContainer;
+ }
+
+ OSL_ENSURE( xContainer.is(),
+ "HierarchyEntry::remove - No container!" );
+
+ if ( xContainer.is() )
+ {
+ xContainer->removeByName( m_aName );
+ xBatch->commitChanges();
+ return true;
+ }
+ }
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // getByName, removeByName
+
+ OSL_FAIL(
+ "HierarchyEntry::remove - caught NoSuchElementException!" );
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ // getByName, commitChanges
+
+ OSL_FAIL(
+ "HierarchyEntry::remove - caught WrappedTargetException!" );
+ }
+ catch ( uno::Exception const & )
+ {
+ // createInstance, createInstanceWithArguments
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ return false;
+}
+
+
+bool HierarchyEntry::first( iterator & it )
+{
+ if ( it.pos == -1 )
+ {
+ // Init...
+
+ try
+ {
+ uno::Reference< container::XHierarchicalNameAccess >
+ xRootHierNameAccess = getRootReadAccess();
+
+ if ( xRootHierNameAccess.is() )
+ {
+ uno::Reference< container::XNameAccess > xNameAccess;
+
+ if ( !m_aPath.isEmpty() )
+ {
+ OUString aPath = m_aPath + "/Children";
+
+ xRootHierNameAccess->getByHierarchicalName( aPath )
+ >>= xNameAccess;
+ }
+ else
+ xNameAccess.set( xRootHierNameAccess, uno::UNO_QUERY );
+
+ OSL_ENSURE( xNameAccess.is(),
+ "HierarchyEntry::first - No name access!" );
+
+ if ( xNameAccess.is() )
+ it.names = xNameAccess->getElementNames();
+
+ uno::Reference< container::XHierarchicalNameAccess >
+ xHierNameAccess( xNameAccess, uno::UNO_QUERY );
+
+ OSL_ENSURE( xHierNameAccess.is(),
+ "HierarchyEntry::first - No hier. name access!" );
+
+ it.dir = xHierNameAccess;
+
+ it.officeDirs = m_xOfficeInstDirs;
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( container::NoSuchElementException const& )
+ {
+ // getByHierarchicalName
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+
+ if ( !it.names.hasElements() )
+ return false;
+
+ it.pos = 0;
+ return true;
+}
+
+
+bool HierarchyEntry::next( iterator& it )
+{
+ if ( it.pos == -1 )
+ return first( it );
+
+ ++it.pos;
+
+ return ( it.pos < it.names.getLength() );
+}
+
+
+OUString HierarchyEntry::createPathFromHierarchyURL(
+ const HierarchyUri& rURI )
+{
+ // Transform path...
+ // folder/subfolder/subsubfolder
+ // --> ['folder']/Children/['subfolder']/Children/['subsubfolder']
+
+ const OUString aPath = rURI.getPath().copy( 1 ); // skip leading slash.
+ sal_Int32 nLen = aPath.getLength();
+
+ if ( nLen )
+ {
+ OUStringBuffer aNewPath;
+ aNewPath.append( "['" );
+
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = aPath.indexOf( '/' );
+
+ do
+ {
+ if ( nEnd == -1 )
+ nEnd = nLen;
+
+ OUString aToken = aPath.copy( nStart, nEnd - nStart );
+ makeXMLName( aToken, aNewPath );
+
+ if ( nEnd != nLen )
+ {
+ aNewPath.append( "']/Children/['" );
+ nStart = nEnd + 1;
+ nEnd = aPath.indexOf( '/', nStart );
+ }
+ else
+ aNewPath.append( "']" );
+ }
+ while ( nEnd != nLen );
+
+ return aNewPath.makeStringAndClear();
+ }
+
+ return aPath;
+}
+
+
+uno::Reference< container::XHierarchicalNameAccess >
+HierarchyEntry::getRootReadAccess()
+{
+ if ( !m_xRootReadAccess.is() )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ if ( !m_xRootReadAccess.is() )
+ {
+ if ( m_bTriedToGetRootReadAccess )
+ {
+ OSL_FAIL( "HierarchyEntry::getRootReadAccess - "
+ "Unable to read any config data! -> #82494#" );
+ return uno::Reference< container::XHierarchicalNameAccess >();
+ }
+
+ try
+ {
+ if ( !m_xConfigProvider.is() )
+ m_xConfigProvider.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(m_aServiceSpecifier, m_xContext),
+ uno::UNO_QUERY );
+
+ if ( m_xConfigProvider.is() )
+ {
+ // Create Root object.
+
+ uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {CFGPROPERTY_NODEPATH, uno::Any(OUString())} // root path
+ }));
+
+ m_bTriedToGetRootReadAccess = true;
+
+ m_xRootReadAccess.set(
+ m_xConfigProvider->createInstanceWithArguments(
+ READ_SERVICE_NAME,
+ aArguments ),
+ uno::UNO_QUERY );
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ // createInstance, createInstanceWithArguments
+
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+ }
+ return m_xRootReadAccess;
+}
+
+
+// HierarchyEntry::iterator Implementation.
+
+
+const HierarchyEntryData& HierarchyEntry::iterator::operator*()
+{
+ if ( ( pos != -1 )
+ && ( dir.is() )
+ && ( pos < names.getLength() ) )
+ {
+ try
+ {
+ OUStringBuffer aKey;
+ aKey.append( "['" );
+ makeXMLName( names.getConstArray()[ pos ], aKey );
+ aKey.append( "']" );
+
+ OUString aTitle = aKey.makeStringAndClear();
+ OUString aTargetURL = aTitle;
+ OUString aType = aTitle;
+
+ aTitle += "/Title";
+ aTargetURL += "/TargetURL";
+ aType += "/Type";
+
+ OUString aValue;
+ dir->getByHierarchicalName( aTitle ) >>= aValue;
+ entry.setTitle( aValue );
+
+ dir->getByHierarchicalName( aTargetURL ) >>= aValue;
+
+ // TargetURL property may contain a reference to the Office
+ // installation directory. To ensure a reloctable office
+ // installation, the path to the office installation directory must
+ // never be stored directly. A placeholder is used instead. Replace
+ // it by actual installation directory.
+ if ( officeDirs.is() && !aValue.isEmpty() )
+ aValue = officeDirs->makeAbsoluteURL( aValue );
+ entry.setTargetURL( aValue );
+
+ if ( dir->hasByHierarchicalName( aType ) )
+ {
+ // Might not be present since it was introduced long
+ // after Title and TargetURL (#82433#)... So not getting
+ // it is not an error.
+
+ // Get Type value.
+ sal_Int32 nType = 0;
+ if ( dir->getByHierarchicalName( aType ) >>= nType )
+ {
+ if ( nType == 0 )
+ {
+ entry.setType( HierarchyEntryData::LINK );
+ }
+ else if ( nType == 1 )
+ {
+ entry.setType( HierarchyEntryData::FOLDER );
+ }
+ else
+ {
+ OSL_FAIL( "HierarchyEntry::getData - "
+ "Unknown Type value!" );
+ }
+ }
+ }
+
+ entry.setName(
+ names.getConstArray()[ pos ] );
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ entry = HierarchyEntryData();
+ }
+ }
+
+ return entry;
+}
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydata.hxx b/ucb/source/ucp/hierarchy/hierarchydata.hxx
new file mode 100644
index 000000000..e81364243
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydata.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 <rtl/ustring.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <mutex>
+
+namespace com::sun::star {
+ namespace container {
+ class XHierarchicalNameAccess;
+ }
+ namespace util {
+ class XOfficeInstallationDirectories;
+ }
+}
+
+namespace hierarchy_ucp
+{
+
+
+class HierarchyEntryData
+{
+public:
+ enum Type { NONE, LINK, FOLDER };
+
+ HierarchyEntryData() : m_aType( NONE ) {}
+ explicit HierarchyEntryData( const Type & rType ) : m_aType( rType ) {}
+
+ const OUString & getName() const { return m_aName; }
+ void setName( const OUString & rName ) { m_aName = rName; }
+
+ const OUString & getTitle() const { return m_aTitle; }
+ void setTitle( const OUString & rTitle ) { m_aTitle = rTitle; }
+
+ const OUString & getTargetURL() const { return m_aTargetURL; }
+ void setTargetURL( const OUString & rURL ) { m_aTargetURL = rURL; }
+
+ Type getType() const
+ { return ( m_aType != NONE ) ? m_aType
+ : m_aTargetURL.getLength()
+ ? LINK
+ : FOLDER; }
+ void setType( const Type & rType ) { m_aType = rType; }
+
+private:
+ OUString m_aName; // Name (language independent)
+ OUString m_aTitle; // Title (language dependent)
+ OUString m_aTargetURL; // Target URL ( links only )
+ Type m_aType; // Type
+};
+
+
+class HierarchyContentProvider;
+class HierarchyUri;
+
+class HierarchyEntry
+{
+ OUString m_aServiceSpecifier;
+ OUString m_aName;
+ OUString m_aPath;
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider;
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ m_xRootReadAccess;
+ css::uno::Reference< css::util::XOfficeInstallationDirectories >
+ m_xOfficeInstDirs;
+ bool m_bTriedToGetRootReadAccess;
+
+private:
+ static OUString createPathFromHierarchyURL( const HierarchyUri & rURI );
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ getRootReadAccess();
+
+public:
+ HierarchyEntry( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ HierarchyContentProvider* pProvider,
+ const OUString& rURL );
+
+ bool hasData();
+
+ bool getData( HierarchyEntryData& rData );
+
+ bool setData( const HierarchyEntryData& rData );
+
+ bool move( const OUString& rNewURL,
+ const HierarchyEntryData& rData );
+
+ bool remove();
+
+ // Iteration.
+
+ class iterator
+ {
+ friend class HierarchyEntry;
+
+ public:
+ iterator() : pos( -1 /* before first */ ) {};
+
+ const HierarchyEntryData& operator*();
+ private:
+ HierarchyEntryData entry;
+ css::uno::Reference< css::container::XHierarchicalNameAccess > dir;
+ css::uno::Reference< css::util::XOfficeInstallationDirectories > officeDirs;
+ css::uno::Sequence< OUString> names;
+ sal_Int32 pos;
+ };
+
+ bool first( iterator& it );
+ bool next ( iterator& it );
+};
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydatasource.cxx b/ucb/source/ucp/hierarchy/hierarchydatasource.cxx
new file mode 100644
index 000000000..9c5fa97ae
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydatasource.cxx
@@ -0,0 +1,864 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ Note: Configuration Management classes do not support XAggregation.
+ So I have to wrap the interesting interfaces manually.
+
+ *************************************************************************/
+#include "hierarchydatasource.hxx"
+#include <osl/diagnose.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <o3tl/string_view.hxx>
+#include <ucbhelper/macros.hxx>
+#include <mutex>
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+// describe path of cfg entry
+constexpr OUStringLiteral CFGPROPERTY_NODEPATH = u"nodepath";
+
+constexpr OUStringLiteral READ_SERVICE_NAME = u"com.sun.star.ucb.HierarchyDataReadAccess";
+constexpr OUStringLiteral READWRITE_SERVICE_NAME = u"com.sun.star.ucb.HierarchyDataReadWriteAccess";
+
+constexpr OUStringLiteral CONFIG_DATA_ROOT_KEY = u"/org.openoffice.ucb.Hierarchy/Root";
+
+
+namespace hcp_impl
+{
+
+
+// HierarchyDataReadAccess Implementation.
+
+namespace {
+
+class HierarchyDataAccess : public cppu::OWeakObject,
+ public lang::XServiceInfo,
+ public lang::XTypeProvider,
+ public lang::XComponent,
+ public lang::XSingleServiceFactory,
+ public container::XHierarchicalNameAccess,
+ public container::XNameContainer,
+ public util::XChangesNotifier,
+ public util::XChangesBatch
+{
+ std::mutex m_aMutex;
+ uno::Reference< uno::XInterface > m_xConfigAccess;
+ uno::Reference< lang::XComponent > m_xCfgC;
+ uno::Reference< lang::XSingleServiceFactory > m_xCfgSSF;
+ uno::Reference< container::XHierarchicalNameAccess > m_xCfgHNA;
+ uno::Reference< container::XNameContainer > m_xCfgNC;
+ uno::Reference< container::XNameReplace > m_xCfgNR;
+ uno::Reference< container::XNameAccess > m_xCfgNA;
+ uno::Reference< container::XElementAccess > m_xCfgEA;
+ uno::Reference< util::XChangesNotifier > m_xCfgCN;
+ uno::Reference< util::XChangesBatch > m_xCfgCB;
+ bool m_bReadOnly;
+
+public:
+ HierarchyDataAccess( const uno::Reference<
+ uno::XInterface > & xConfigAccess,
+ bool bReadOnly );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XComponent
+ virtual void SAL_CALL
+ dispose() override;
+ virtual void SAL_CALL
+ addEventListener( const uno::Reference< lang::XEventListener > & xListener ) override;
+ virtual void SAL_CALL
+ removeEventListener( const uno::Reference<
+ lang::XEventListener > & aListener ) override;
+
+ // XSingleServiceFactory
+ virtual uno::Reference< uno::XInterface > SAL_CALL
+ createInstance() override;
+ virtual uno::Reference< uno::XInterface > SAL_CALL
+ createInstanceWithArguments( const uno::Sequence< uno::Any > & aArguments ) override;
+
+ // XHierarchicalNameAccess
+ virtual uno::Any SAL_CALL
+ getByHierarchicalName( const OUString & aName ) override;
+ virtual sal_Bool SAL_CALL
+ hasByHierarchicalName( const OUString & aName ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL
+ insertByName( const OUString & aName, const uno::Any & aElement ) override;
+ virtual void SAL_CALL
+ removeByName( const OUString & Name ) override;
+
+ // XNameReplace ( base of XNameContainer )
+ virtual void SAL_CALL
+ replaceByName( const OUString & aName, const uno::Any & aElement ) override;
+
+ // XNameAccess ( base of XNameReplace )
+ virtual uno::Any SAL_CALL
+ getByName( const OUString & aName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL
+ getElementNames() override;
+ virtual sal_Bool SAL_CALL
+ hasByName( const OUString & aName ) override;
+
+ // XElementAccess ( base of XNameAccess )
+ virtual uno::Type SAL_CALL
+ getElementType() override;
+ virtual sal_Bool SAL_CALL
+ hasElements() override;
+
+ // XChangesNotifier
+ virtual void SAL_CALL
+ addChangesListener( const uno::Reference<
+ util::XChangesListener > & aListener ) override;
+ virtual void SAL_CALL
+ removeChangesListener( const uno::Reference<
+ util::XChangesListener > & aListener ) override;
+
+ // XChangesBatch
+ virtual void SAL_CALL
+ commitChanges() override;
+ virtual sal_Bool SAL_CALL
+ hasPendingChanges() override;
+ virtual uno::Sequence< util::ElementChange > SAL_CALL
+ getPendingChanges() override;
+private:
+ template<class T>
+ css::uno::Reference<T> ensureOrigInterface(css::uno::Reference<T>& x);
+};
+
+}
+
+} // namespace hcp_impl
+
+using namespace hcp_impl;
+
+
+// HierarchyDataSource Implementation.
+
+
+HierarchyDataSource::HierarchyDataSource(
+ const uno::Reference< uno::XComponentContext > & rxContext )
+: m_xContext( rxContext )
+{
+}
+
+
+// virtual
+HierarchyDataSource::~HierarchyDataSource()
+{
+}
+
+// XServiceInfo methods.
+OUString SAL_CALL HierarchyDataSource::getImplementationName() \
+{
+ return "com.sun.star.comp.ucb.HierarchyDataSource";
+}
+sal_Bool SAL_CALL HierarchyDataSource::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+css::uno::Sequence< OUString > HierarchyDataSource::getSupportedServiceNames()
+{
+ return { "com.sun.star.ucb.DefaultHierarchyDataSource", "com.sun.star.ucb.HierarchyDataSource" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_HierarchyDataSource_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new HierarchyDataSource(context));
+}
+
+
+// XComponent methods.
+
+
+// virtual
+void SAL_CALL HierarchyDataSource::dispose()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_aDisposeEventListeners.getLength(aGuard) )
+ {
+ lang::EventObject aEvt;
+ aEvt.Source = static_cast< lang::XComponent * >( this );
+ m_aDisposeEventListeners.disposeAndClear( aGuard, aEvt );
+ }
+}
+
+
+// virtual
+void SAL_CALL HierarchyDataSource::addEventListener(
+ const uno::Reference< lang::XEventListener > & Listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ m_aDisposeEventListeners.addInterface( aGuard, Listener );
+}
+
+
+// virtual
+void SAL_CALL HierarchyDataSource::removeEventListener(
+ const uno::Reference< lang::XEventListener > & Listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ m_aDisposeEventListeners.removeInterface( aGuard, Listener );
+}
+
+
+// XMultiServiceFactory methods.
+
+
+// virtual
+uno::Reference< uno::XInterface > SAL_CALL
+HierarchyDataSource::createInstance( const OUString & aServiceSpecifier )
+{
+ // Create view to root node.
+
+ beans::PropertyValue aProp = comphelper::makePropertyValue(CFGPROPERTY_NODEPATH,
+ OUString( CONFIG_DATA_ROOT_KEY ));
+
+ uno::Sequence< uno::Any > aArguments{ uno::Any(aProp) };
+
+ return createInstanceWithArguments( aServiceSpecifier, aArguments, false );
+}
+
+
+// virtual
+uno::Reference< uno::XInterface > SAL_CALL
+HierarchyDataSource::createInstanceWithArguments(
+ const OUString & ServiceSpecifier,
+ const uno::Sequence< uno::Any > & Arguments )
+{
+ return createInstanceWithArguments( ServiceSpecifier, Arguments, true );
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL
+HierarchyDataSource::getAvailableServiceNames()
+{
+ return { READ_SERVICE_NAME, READWRITE_SERVICE_NAME };
+}
+
+
+// Non-interface methods
+
+
+uno::Reference< uno::XInterface >
+HierarchyDataSource::createInstanceWithArguments(
+ std::u16string_view ServiceSpecifier,
+ const uno::Sequence< uno::Any > & Arguments,
+ bool bCheckArgs )
+{
+ // Check service specifier.
+ bool bReadOnly = ServiceSpecifier == READ_SERVICE_NAME;
+ bool bReadWrite = !bReadOnly && ServiceSpecifier == READWRITE_SERVICE_NAME;
+
+ if ( !bReadOnly && !bReadWrite )
+ {
+ OSL_FAIL( "HierarchyDataSource::createInstanceWithArguments - "
+ "Unsupported service specifier!" );
+ return uno::Reference< uno::XInterface >();
+ }
+
+ uno::Sequence< uno::Any > aNewArgs( Arguments );
+ auto aNewArgsRange = asNonConstRange(aNewArgs);
+
+ if ( bCheckArgs )
+ {
+ // Check arguments.
+ bool bHasNodePath = false;
+ sal_Int32 nCount = Arguments.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ beans::PropertyValue aProp;
+ if ( Arguments[ n ] >>= aProp )
+ {
+ if ( aProp.Name == CFGPROPERTY_NODEPATH )
+ {
+ OUString aPath;
+ if ( aProp.Value >>= aPath )
+ {
+ bHasNodePath = true;
+
+ // Create path to data inside the configuration.
+ OUString aConfigPath;
+ if ( !createConfigPath( aPath, aConfigPath ) )
+ {
+ OSL_FAIL( "HierarchyDataSource::"
+ "createInstanceWithArguments - "
+ "Invalid node path!" );
+ return uno::Reference< uno::XInterface >();
+ }
+
+ aProp.Value <<= aConfigPath;
+
+ // Set new path in arguments.
+ aNewArgsRange[ n ] <<= aProp;
+
+ break;
+ }
+ else
+ {
+ OSL_FAIL( "HierarchyDataSource::createInstanceWithArguments - "
+ "Invalid type for property 'nodepath'!" );
+ return uno::Reference< uno::XInterface >();
+ }
+ }
+ }
+ }
+
+ if ( !bHasNodePath )
+ {
+ OSL_FAIL( "HierarchyDataSource::createInstanceWithArguments - "
+ "No 'nodepath' property!" );
+ return uno::Reference< uno::XInterface >();
+ }
+ }
+
+ // Create Configuration Provider.
+ uno::Reference< lang::XMultiServiceFactory > xProv = getConfigProvider();
+ if ( !xProv.is() )
+ return uno::Reference< uno::XInterface >();
+
+ uno::Reference< uno::XInterface > xConfigAccess;
+ try
+ {
+ if ( bReadOnly )
+ {
+ // Create configuration read-only access object.
+ xConfigAccess = xProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aNewArgs );
+ }
+ else
+ {
+ // Create configuration read-write access object.
+ xConfigAccess = xProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ aNewArgs );
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ OSL_FAIL( "HierarchyDataSource::createInstanceWithArguments - "
+ "Cannot instantiate configuration access!" );
+ throw;
+ }
+
+ if ( !xConfigAccess.is() )
+ {
+ OSL_FAIL( "HierarchyDataSource::createInstanceWithArguments - "
+ "Cannot instantiate configuration access!" );
+ return xConfigAccess;
+ }
+
+ return uno::Reference< uno::XInterface >(
+ static_cast< cppu::OWeakObject * >(
+ new HierarchyDataAccess( xConfigAccess, bReadOnly ) ) );
+}
+
+
+uno::Reference< lang::XMultiServiceFactory >
+HierarchyDataSource::getConfigProvider()
+{
+ if ( !m_xConfigProvider.is() )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ if ( !m_xConfigProvider.is() )
+ {
+ try
+ {
+ m_xConfigProvider = configuration::theDefaultProvider::get( m_xContext );
+ }
+ catch ( uno::Exception const & )
+ {
+ OSL_FAIL( "HierarchyDataSource::getConfigProvider - "
+ "caught exception!" );
+ }
+ }
+ }
+
+ return m_xConfigProvider;
+}
+
+
+bool HierarchyDataSource::createConfigPath(
+ std::u16string_view rInPath, OUString & rOutPath )
+{
+ if ( !rInPath.empty() )
+ {
+ if ( o3tl::starts_with( rInPath, u"/" ) )
+ {
+ OSL_FAIL( "HierarchyDataSource::createConfigPath - "
+ "Leading slash in node path!" );
+ return false;
+ }
+
+ if ( o3tl::ends_with( rInPath, u"/" ) )
+ {
+ OSL_FAIL( "HierarchyDataSource::createConfigPath - "
+ "Trailing slash in node path!" );
+ return false;
+ }
+
+ rOutPath = CONFIG_DATA_ROOT_KEY + "/" + rInPath;
+ }
+ else
+ {
+ rOutPath = CONFIG_DATA_ROOT_KEY;
+ }
+
+ return true;
+}
+
+
+// HierarchyDataAccess Implementation.
+
+template<class T>
+css::uno::Reference<T> HierarchyDataAccess::ensureOrigInterface(css::uno::Reference<T>& x)
+{
+ if ( x.is() )
+ return x;
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !x.is() )
+ x.set( m_xConfigAccess, uno::UNO_QUERY );
+ return x;
+}
+
+
+HierarchyDataAccess::HierarchyDataAccess( const uno::Reference<
+ uno::XInterface > & xConfigAccess,
+ bool bReadOnly )
+: m_xConfigAccess( xConfigAccess ),
+ m_bReadOnly( bReadOnly )
+{
+}
+
+// XInterface methods.
+void SAL_CALL HierarchyDataAccess::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL HierarchyDataAccess::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+// virtual
+uno::Any SAL_CALL HierarchyDataAccess::queryInterface( const uno::Type & aType )
+{
+ // Interfaces supported in read-only and read-write mode.
+ uno::Any aRet = cppu::queryInterface( aType,
+ static_cast< lang::XTypeProvider * >( this ),
+ static_cast< lang::XServiceInfo * >( this ),
+ static_cast< lang::XComponent * >( this ),
+ static_cast< container::XHierarchicalNameAccess * >( this ),
+ static_cast< container::XNameAccess * >( this ),
+ static_cast< container::XElementAccess * >( this ),
+ static_cast< util::XChangesNotifier * >( this ) );
+
+ // Interfaces supported only in read-write mode.
+ if ( !aRet.hasValue() && !m_bReadOnly )
+ {
+ aRet = cppu::queryInterface( aType,
+ static_cast< lang::XSingleServiceFactory * >( this ),
+ static_cast< container::XNameContainer * >( this ),
+ static_cast< container::XNameReplace * >( this ),
+ static_cast< util::XChangesBatch * >( this ) );
+ }
+
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( aType );
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( HierarchyDataAccess );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL HierarchyDataAccess::getTypes()
+{
+ if ( m_bReadOnly )
+ {
+ static cppu::OTypeCollection s_aReadOnlyTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( container::XHierarchicalNameAccess ),
+ CPPU_TYPE_REF( container::XNameAccess ),
+ CPPU_TYPE_REF( util::XChangesNotifier ) );
+
+ return s_aReadOnlyTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aReadWriteTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( lang::XSingleServiceFactory ),
+ CPPU_TYPE_REF( container::XHierarchicalNameAccess ),
+ CPPU_TYPE_REF( container::XNameContainer ),
+ CPPU_TYPE_REF( util::XChangesBatch ),
+ CPPU_TYPE_REF( util::XChangesNotifier ) );
+
+ return s_aReadWriteTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+OUString SAL_CALL HierarchyDataAccess::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.HierarchyDataAccess";
+}
+
+sal_Bool SAL_CALL HierarchyDataAccess::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+css::uno::Sequence< OUString > SAL_CALL HierarchyDataAccess::getSupportedServiceNames()
+{
+ return { READ_SERVICE_NAME, READWRITE_SERVICE_NAME };
+}
+
+
+// XComponent methods.
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::dispose()
+{
+ uno::Reference< lang::XComponent > xOrig
+ = ensureOrigInterface( m_xCfgC );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XComponent!" );
+ xOrig->dispose();
+}
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener )
+{
+ uno::Reference< lang::XComponent > xOrig
+ = ensureOrigInterface( m_xCfgC );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XComponent!" );
+ xOrig->addEventListener( xListener );
+}
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::removeEventListener(
+ const uno::Reference< lang::XEventListener > & aListener )
+{
+ uno::Reference< lang::XComponent > xOrig
+ = ensureOrigInterface( m_xCfgC );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XComponent!" );
+ xOrig->removeEventListener( aListener );
+}
+
+
+// XHierarchicalNameAccess methods.
+
+
+// virtual
+uno::Any SAL_CALL HierarchyDataAccess::getByHierarchicalName(
+ const OUString & aName )
+{
+ uno::Reference< container::XHierarchicalNameAccess > xOrig
+ = ensureOrigInterface( m_xCfgHNA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : "
+ "Data source is not an XHierarchicalNameAccess!" );
+ return xOrig->getByHierarchicalName( aName );
+}
+
+
+// virtual
+sal_Bool SAL_CALL HierarchyDataAccess::hasByHierarchicalName(
+ const OUString & aName )
+{
+ uno::Reference< container::XHierarchicalNameAccess > xOrig
+ = ensureOrigInterface( m_xCfgHNA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : "
+ "Data source is not an XHierarchicalNameAccess!" );
+ return xOrig->hasByHierarchicalName( aName );
+}
+
+
+// XNameAccess methods.
+
+
+// virtual
+uno::Any SAL_CALL HierarchyDataAccess::getByName( const OUString & aName )
+{
+ uno::Reference< container::XNameAccess > xOrig
+ = ensureOrigInterface( m_xCfgNA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameAccess!" );
+ return xOrig->getByName( aName );
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL HierarchyDataAccess::getElementNames()
+{
+ uno::Reference< container::XNameAccess > xOrig
+ = ensureOrigInterface( m_xCfgNA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameAccess!" );
+ return xOrig->getElementNames();
+}
+
+
+// virtual
+sal_Bool SAL_CALL HierarchyDataAccess::hasByName( const OUString & aName )
+{
+ uno::Reference< container::XNameAccess > xOrig
+ = ensureOrigInterface( m_xCfgNA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameAccess!" );
+ return xOrig->hasByName( aName );
+}
+
+
+// XElementAccess methods.
+
+
+// virtual
+uno::Type SAL_CALL HierarchyDataAccess::getElementType()
+{
+ uno::Reference< container::XElementAccess > xOrig
+ = ensureOrigInterface( m_xCfgEA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XElementAccess!" );
+ return xOrig->getElementType();
+}
+
+
+// virtual
+sal_Bool SAL_CALL HierarchyDataAccess::hasElements()
+{
+ uno::Reference< container::XElementAccess > xOrig
+ = ensureOrigInterface( m_xCfgEA );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XElementAccess!" );
+ return xOrig->hasElements();
+}
+
+
+// XChangesNotifier methods.
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::addChangesListener(
+ const uno::Reference< util::XChangesListener > & aListener )
+{
+ uno::Reference< util::XChangesNotifier > xOrig
+ = ensureOrigInterface( m_xCfgCN );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XChangesNotifier!" );
+ xOrig->addChangesListener( aListener );
+}
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::removeChangesListener(
+ const uno::Reference< util::XChangesListener > & aListener )
+{
+ uno::Reference< util::XChangesNotifier > xOrig
+ = ensureOrigInterface( m_xCfgCN );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XChangesNotifier!" );
+ xOrig->removeChangesListener( aListener );
+}
+
+
+// XSingleServiceFactory methods.
+
+
+// virtual
+uno::Reference< uno::XInterface > SAL_CALL HierarchyDataAccess::createInstance()
+{
+ uno::Reference< lang::XSingleServiceFactory > xOrig
+ = ensureOrigInterface( m_xCfgSSF );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XSingleServiceFactory!" );
+ return xOrig->createInstance();
+}
+
+
+// virtual
+uno::Reference< uno::XInterface > SAL_CALL
+HierarchyDataAccess::createInstanceWithArguments(
+ const uno::Sequence< uno::Any > & aArguments )
+{
+ uno::Reference< lang::XSingleServiceFactory > xOrig
+ = ensureOrigInterface( m_xCfgSSF );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XSingleServiceFactory!" );
+ return xOrig->createInstanceWithArguments( aArguments );
+}
+
+
+// XNameContainer methods.
+
+
+// virtual
+void SAL_CALL
+HierarchyDataAccess::insertByName( const OUString & aName,
+ const uno::Any & aElement )
+{
+ uno::Reference< container::XNameContainer > xOrig
+ = ensureOrigInterface( m_xCfgNC );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameContainer!" );
+ xOrig->insertByName( aName, aElement );
+}
+
+
+// virtual
+void SAL_CALL
+HierarchyDataAccess::removeByName( const OUString & Name )
+{
+ uno::Reference< container::XNameContainer > xOrig
+ = ensureOrigInterface( m_xCfgNC );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameContainer!" );
+ xOrig->removeByName( Name );
+}
+
+
+// XNameReplace methods.
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::replaceByName( const OUString & aName,
+ const uno::Any & aElement )
+{
+ uno::Reference< container::XNameReplace > xOrig
+ = ensureOrigInterface( m_xCfgNR );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XNameReplace!" );
+ xOrig->replaceByName( aName, aElement );
+}
+
+
+// XChangesBatch methods.
+
+
+// virtual
+void SAL_CALL HierarchyDataAccess::commitChanges()
+{
+ uno::Reference< util::XChangesBatch > xOrig
+ = ensureOrigInterface( m_xCfgCB );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XChangesBatch!" );
+ xOrig->commitChanges();
+}
+
+
+// virtual
+sal_Bool SAL_CALL HierarchyDataAccess::hasPendingChanges()
+{
+ uno::Reference< util::XChangesBatch > xOrig
+ = ensureOrigInterface( m_xCfgCB );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XChangesBatch!" );
+ return xOrig->hasPendingChanges();
+}
+
+
+// virtual
+uno::Sequence< util::ElementChange > SAL_CALL
+HierarchyDataAccess::getPendingChanges()
+{
+ uno::Reference< util::XChangesBatch > xOrig
+ = ensureOrigInterface( m_xCfgCB );
+
+ OSL_ENSURE( xOrig.is(),
+ "HierarchyDataAccess : Data source is not an XChangesBatch!" );
+ return xOrig->getPendingChanges();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydatasource.hxx b/ucb/source/ucp/hierarchy/hierarchydatasource.hxx
new file mode 100644
index 000000000..b4bff294d
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydatasource.hxx
@@ -0,0 +1,83 @@
+/* -*- 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/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <memory>
+#include <mutex>
+#include <string_view>
+
+
+namespace hierarchy_ucp {
+
+
+class HierarchyDataSource : public cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XComponent,
+ css::lang::XMultiServiceFactory>
+{
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aDisposeEventListeners;
+
+public:
+ explicit HierarchyDataSource( const css::uno::Reference< css::uno::XComponentContext > & rxContext );
+ virtual ~HierarchyDataSource() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener > & xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener > & aListener ) override;
+
+ // XMultiServiceFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString & aServiceSpecifier ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString & ServiceSpecifier,
+ const css::uno::Sequence<
+ css::uno::Any > & Arguments ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
+
+ // Non-Interface methods
+
+private:
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::uno::XInterface > createInstanceWithArguments( std::u16string_view ServiceSpecifier,
+ const css::uno::Sequence<
+ css::uno::Any > & Arguments,
+ bool bCheckArgs );
+
+ css::uno::Reference< css::lang::XMultiServiceFactory > getConfigProvider();
+
+ static bool createConfigPath( std::u16string_view rInPath, OUString & rOutPath );
+};
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydatasupplier.cxx b/ucb/source/ucp/hierarchy/hierarchydatasupplier.cxx
new file mode 100644
index 000000000..1a2f29f6b
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydatasupplier.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <ucbhelper/contentidentifier.hxx>
+#include "hierarchydatasupplier.hxx"
+#include "hierarchyprovider.hxx"
+#include "hierarchycontent.hxx"
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+
+HierarchyResultSetDataSupplier::HierarchyResultSetDataSupplier(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< HierarchyContent >& rContent,
+ sal_Int32 nOpenMode )
+: m_xContent( rContent ), m_xContext( rxContext ),
+ m_aFolder( rxContext,
+ static_cast< HierarchyContentProvider * >(
+ rContent->getProvider().get() ),
+ rContent->getIdentifier()->getContentIdentifier() ),
+ m_nOpenMode( nOpenMode ), m_bCountFinal( false )
+{
+}
+
+
+// virtual
+HierarchyResultSetDataSupplier::~HierarchyResultSetDataSupplier()
+{
+}
+
+
+// virtual
+OUString HierarchyResultSetDataSupplier::queryContentIdentifierString(
+ sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return queryContentIdentifierStringImpl(aGuard, nIndex);
+}
+
+OUString HierarchyResultSetDataSupplier::queryContentIdentifierStringImpl(
+ std::unique_lock<std::mutex>& rGuard,
+ sal_uInt32 nIndex )
+{
+ if ( nIndex < m_aResults.size() )
+ {
+ OUString aId = m_aResults[ nIndex ]->aId;
+ if ( !aId.isEmpty() )
+ {
+ // Already cached.
+ return aId;
+ }
+ }
+
+ if ( getResultImpl( rGuard, nIndex ) )
+ {
+ OUString aId
+ = m_xContent->getIdentifier()->getContentIdentifier();
+
+ if ( ( aId.lastIndexOf( '/' ) + 1 ) != aId.getLength() )
+ aId += "/";
+
+ aId += m_aResults[ nIndex ]->aData.getName();
+
+ m_aResults[ nIndex ]->aId = aId;
+ return aId;
+ }
+ return OUString();
+}
+
+
+// virtual
+uno::Reference< ucb::XContentIdentifier >
+HierarchyResultSetDataSupplier::queryContentIdentifier( sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = m_aResults[ nIndex ]->xId;
+ if ( xId.is() )
+ {
+ // Already cached.
+ return xId;
+ }
+ }
+
+ OUString aId = queryContentIdentifierStringImpl( aGuard, nIndex );
+ if ( !aId.isEmpty() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aId );
+ m_aResults[ nIndex ]->xId = xId;
+ return xId;
+ }
+ return uno::Reference< ucb::XContentIdentifier >();
+}
+
+
+// virtual
+uno::Reference< ucb::XContent >
+HierarchyResultSetDataSupplier::queryContent( sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_aResults[ nIndex ]->xContent;
+ if ( xContent.is() )
+ {
+ // Already cached.
+ return xContent;
+ }
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = queryContentIdentifier( nIndex );
+ if ( xId.is() )
+ {
+ try
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_xContent->getProvider()->queryContent( xId );
+ m_aResults[ nIndex ]->xContent = xContent;
+ return xContent;
+
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ }
+ }
+ return uno::Reference< ucb::XContent >();
+}
+
+
+// virtual
+bool HierarchyResultSetDataSupplier::getResult( sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return getResultImpl(aGuard, nIndex);
+}
+
+bool HierarchyResultSetDataSupplier::getResultImpl( std::unique_lock<std::mutex>& rGuard, sal_uInt32 nIndex )
+{
+ if ( m_aResults.size() > nIndex )
+ {
+ // Result already present.
+ return true;
+ }
+
+ // Result not (yet) present.
+
+ if ( m_bCountFinal )
+ return false;
+
+ // Try to obtain result...
+
+ sal_uInt32 nOldCount = m_aResults.size();
+ bool bFound = false;
+ sal_uInt32 nPos = nOldCount;
+
+ while ( m_aFolder.next( m_aIterator ) )
+ {
+ const HierarchyEntryData& rResult = *m_aIterator;
+ if ( checkResult( rResult ) )
+ {
+ m_aResults.emplace_back( new ResultListEntry( rResult ) );
+
+ if ( nPos == nIndex )
+ {
+ // Result obtained.
+ bFound = true;
+ break;
+ }
+ }
+ nPos++;
+ }
+
+ if ( !bFound )
+ m_bCountFinal = true;
+
+ rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet();
+ if ( xResultSet.is() )
+ {
+ // Callbacks follow!
+ rGuard.unlock();
+
+ if ( nOldCount < m_aResults.size() )
+ xResultSet->rowCountChanged(
+ nOldCount, m_aResults.size() );
+
+ if ( m_bCountFinal )
+ xResultSet->rowCountFinal();
+
+ rGuard.lock();
+ }
+
+ return bFound;
+}
+
+
+// virtual
+sal_uInt32 HierarchyResultSetDataSupplier::totalCount()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bCountFinal )
+ return m_aResults.size();
+
+ sal_uInt32 nOldCount = m_aResults.size();
+
+ while ( m_aFolder.next( m_aIterator ) )
+ {
+ const HierarchyEntryData& rResult = *m_aIterator;
+ if ( checkResult( rResult ) )
+ m_aResults.emplace_back( new ResultListEntry( rResult ) );
+ }
+
+ m_bCountFinal = true;
+
+ rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet();
+ if ( xResultSet.is() )
+ {
+ // Callbacks follow!
+ aGuard.unlock();
+
+ if ( nOldCount < m_aResults.size() )
+ xResultSet->rowCountChanged(
+ nOldCount, m_aResults.size() );
+
+ xResultSet->rowCountFinal();
+ }
+
+ return m_aResults.size();
+}
+
+
+// virtual
+sal_uInt32 HierarchyResultSetDataSupplier::currentCount()
+{
+ return m_aResults.size();
+}
+
+
+// virtual
+bool HierarchyResultSetDataSupplier::isCountFinal()
+{
+ return m_bCountFinal;
+}
+
+
+// virtual
+uno::Reference< sdbc::XRow >
+HierarchyResultSetDataSupplier::queryPropertyValues( sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< sdbc::XRow > xRow
+ = m_aResults[ nIndex ]->xRow;
+ if ( xRow.is() )
+ {
+ // Already cached.
+ return xRow;
+ }
+ }
+
+ if ( getResultImpl( aGuard, nIndex ) )
+ {
+ HierarchyContentProperties aData(
+ m_aResults[ nIndex ]->aData );
+
+ uno::Reference< sdbc::XRow > xRow
+ = HierarchyContent::getPropertyValues(
+ m_xContext,
+ getResultSet()->getProperties(),
+ aData,
+ static_cast< HierarchyContentProvider * >(
+ m_xContent->getProvider().get() ),
+ queryContentIdentifierStringImpl( aGuard, nIndex ) );
+ m_aResults[ nIndex ]->xRow = xRow;
+ return xRow;
+ }
+
+ return uno::Reference< sdbc::XRow >();
+}
+
+
+// virtual
+void HierarchyResultSetDataSupplier::releasePropertyValues( sal_uInt32 nIndex )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ m_aResults[ nIndex ]->xRow.clear();
+}
+
+
+// virtual
+void HierarchyResultSetDataSupplier::close()
+{
+}
+
+
+// virtual
+void HierarchyResultSetDataSupplier::validate()
+{
+}
+
+
+bool HierarchyResultSetDataSupplier::checkResult(
+ const HierarchyEntryData& rResult )
+{
+ switch ( m_nOpenMode )
+ {
+ case ucb::OpenMode::FOLDERS:
+ if ( rResult.getType() == HierarchyEntryData::LINK )
+ {
+ // Entry is a link.
+ return false;
+ }
+ break;
+
+ case ucb::OpenMode::DOCUMENTS:
+ if ( rResult.getType() == HierarchyEntryData::FOLDER )
+ {
+ // Entry is a folder.
+ return false;
+ }
+ break;
+
+ case ucb::OpenMode::ALL:
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchydatasupplier.hxx b/ucb/source/ucp/hierarchy/hierarchydatasupplier.hxx
new file mode 100644
index 000000000..8bad51dc0
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchydatasupplier.hxx
@@ -0,0 +1,92 @@
+/* -*- 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 <rtl/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+#include <mutex>
+#include <vector>
+#include "hierarchydata.hxx"
+
+namespace hierarchy_ucp {
+
+class HierarchyEntryData;
+class HierarchyContent;
+
+class HierarchyResultSetDataSupplier :
+ public ::ucbhelper::ResultSetDataSupplier
+{
+private:
+ bool checkResult( const HierarchyEntryData& rResult );
+
+public:
+ HierarchyResultSetDataSupplier(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< HierarchyContent >& rContent,
+ sal_Int32 nOpenMode );
+ virtual ~HierarchyResultSetDataSupplier() override;
+
+ virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) final 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 ) final 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:
+ OUString queryContentIdentifierStringImpl( std::unique_lock<std::mutex>&, sal_uInt32 nIndex );
+ bool getResultImpl( std::unique_lock<std::mutex>&, sal_uInt32 nIndex );
+
+ struct ResultListEntry
+ {
+ OUString aId;
+ css::uno::Reference< css::ucb::XContentIdentifier > xId;
+ css::uno::Reference< css::ucb::XContent > xContent;
+ css::uno::Reference< css::sdbc::XRow > xRow;
+ HierarchyEntryData aData;
+
+ explicit ResultListEntry( const HierarchyEntryData& rEntry ) : aData( rEntry ) {}
+ };
+ typedef std::vector< std::unique_ptr<ResultListEntry> > ResultList;
+ std::mutex m_aMutex;
+ ResultList m_aResults;
+ rtl::Reference< HierarchyContent > m_xContent;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ HierarchyEntry m_aFolder;
+ HierarchyEntry::iterator m_aIterator;
+ sal_Int32 m_nOpenMode;
+ bool m_bCountFinal;
+};
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchyprovider.cxx b/ucb/source/ucp/hierarchy/hierarchyprovider.cxx
new file mode 100644
index 000000000..09ac0a2e7
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchyprovider.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - XInitialization::initialize does not work any longer!
+
+ *************************************************************************/
+#include <osl/diagnose.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/weak.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/macros.hxx>
+#include "hierarchyprovider.hxx"
+#include "hierarchycontent.hxx"
+#include "hierarchyuri.hxx"
+
+#include "../inc/urihelper.hxx"
+
+using namespace com::sun::star;
+using namespace hierarchy_ucp;
+
+
+// HierarchyContentProvider Implementation.
+
+
+HierarchyContentProvider::HierarchyContentProvider(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+: ::ucbhelper::ContentProviderImplHelper( rxContext )
+{
+}
+
+
+// virtual
+HierarchyContentProvider::~HierarchyContentProvider()
+{
+}
+
+
+// XInterface methods.
+
+void SAL_CALL HierarchyContentProvider::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL HierarchyContentProvider::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL HierarchyContentProvider::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XTypeProvider* >(this),
+ static_cast< lang::XServiceInfo* >(this),
+ static_cast< ucb::XContentProvider* >(this),
+ static_cast< lang::XInitialization* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_IMPL_4( HierarchyContentProvider,
+ lang::XTypeProvider,
+ lang::XServiceInfo,
+ ucb::XContentProvider,
+ lang::XInitialization );
+
+
+// XServiceInfo methods.
+
+OUString SAL_CALL HierarchyContentProvider::getImplementationName() \
+{
+ return "com.sun.star.comp.ucb.HierarchyContentProvider";
+}
+sal_Bool SAL_CALL HierarchyContentProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+css::uno::Sequence< OUString > HierarchyContentProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.ucb.HierarchyContentProvider" };
+}
+
+// Service factory implementation.
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_HierarchyContentProvider_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new HierarchyContentProvider(context));
+}
+
+// XContentProvider methods.
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+HierarchyContentProvider::queryContent(
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ HierarchyUri aUri( Identifier->getContentIdentifier() );
+ if ( !aUri.isValid() )
+ throw ucb::IllegalIdentifierException();
+
+ // Encode URL and create new Id. This may "correct" user-typed-in URL's.
+ uno::Reference< ucb::XContentIdentifier > xCanonicId
+ = new ::ucbhelper::ContentIdentifier( ::ucb_impl::urihelper::encodeURI( aUri.getUri() ) );
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xCanonicId );
+ if ( xContent.is() )
+ return xContent;
+
+ // Create a new content.
+ xContent = HierarchyContent::create( m_xContext, this, xCanonicId );
+ registerNewContent( xContent );
+
+ if ( xContent.is() && !xContent->getIdentifier().is() )
+ throw ucb::IllegalIdentifierException();
+
+ return xContent;
+}
+
+
+// XInitialization methods.
+
+
+// virtual
+void SAL_CALL HierarchyContentProvider::initialize(
+ const uno::Sequence< uno::Any >& aArguments )
+{
+ if ( aArguments.hasElements() )
+ OSL_FAIL( "HierarchyContentProvider::initialize : not supported!" );
+}
+
+
+// Non-interface methods.
+
+
+uno::Reference< lang::XMultiServiceFactory >
+HierarchyContentProvider::getConfigProvider(
+ const OUString & rServiceSpecifier )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ ConfigProviderMap::iterator it = m_aConfigProviderMap.find(
+ rServiceSpecifier );
+ if ( it == m_aConfigProviderMap.end() )
+ {
+ try
+ {
+ ConfigProviderMapEntry aEntry;
+ aEntry.xConfigProvider.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(rServiceSpecifier, m_xContext),
+ uno::UNO_QUERY );
+
+ if ( aEntry.xConfigProvider.is() )
+ {
+ m_aConfigProviderMap[ rServiceSpecifier ] = aEntry;
+ return aEntry.xConfigProvider;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+// OSL_FAIL( // "HierarchyContentProvider::getConfigProvider - "
+// "caught exception!" );
+ }
+
+ OSL_FAIL( "HierarchyContentProvider::getConfigProvider - "
+ "No config provider!" );
+
+ return uno::Reference< lang::XMultiServiceFactory >();
+ }
+
+ return (*it).second.xConfigProvider;
+}
+
+uno::Reference< container::XHierarchicalNameAccess >
+HierarchyContentProvider::getRootConfigReadNameAccess(
+ const OUString & rServiceSpecifier )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ ConfigProviderMap::iterator it = m_aConfigProviderMap.find(
+ rServiceSpecifier );
+ if (it == m_aConfigProviderMap.end())
+ return uno::Reference< container::XHierarchicalNameAccess >();
+
+ if ( !( (*it).second.xRootReadAccess.is() ) )
+ {
+ if ( (*it).second.bTriedToGetRootReadAccess )
+ {
+ OSL_FAIL( "HierarchyContentProvider::getRootConfigReadNameAccess - "
+ "Unable to read any config data! -> #82494#" );
+ return uno::Reference< container::XHierarchicalNameAccess >();
+ }
+
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xConfigProv
+ = getConfigProvider( rServiceSpecifier );
+
+ if ( xConfigProv.is() )
+ {
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath" ;
+ aProperty.Value <<= OUString(); // root path
+ uno::Sequence< uno::Any > aArguments{ uno::Any(aProperty) };
+
+ (*it).second.bTriedToGetRootReadAccess = true;
+
+ (*it).second.xRootReadAccess.set(
+ xConfigProv->createInstanceWithArguments(
+ "com.sun.star.ucb.HierarchyDataReadAccess",
+ aArguments ),
+ uno::UNO_QUERY );
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ // createInstance, createInstanceWithArguments
+
+ OSL_FAIL( "HierarchyContentProvider::getRootConfigReadNameAccess - "
+ "caught Exception!" );
+ }
+ }
+
+ return (*it).second.xRootReadAccess;
+}
+
+uno::Reference< util::XOfficeInstallationDirectories >
+HierarchyContentProvider::getOfficeInstallationDirectories()
+{
+ if ( !m_xOfficeInstDirs.is() )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_xOfficeInstDirs.is() )
+ {
+ OSL_ENSURE( m_xContext.is(), "No service manager!" );
+
+ m_xOfficeInstDirs = util::theOfficeInstallationDirectories::get(m_xContext);
+ }
+ }
+ return m_xOfficeInstDirs;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchyprovider.hxx b/ucb/source/ucp/hierarchy/hierarchyprovider.hxx
new file mode 100644
index 000000000..cbd2911d4
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchyprovider.hxx
@@ -0,0 +1,112 @@
+/* -*- 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>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <unordered_map>
+
+namespace com::sun::star {
+ namespace container {
+ class XHierarchicalNameAccess;
+ }
+ namespace util {
+ class XOfficeInstallationDirectories;
+ }
+}
+
+namespace hierarchy_ucp {
+
+
+#define HIERARCHY_URL_SCHEME \
+ "vnd.sun.star.hier"
+#define HIERARCHY_URL_SCHEME_LENGTH 17
+
+inline constexpr OUStringLiteral HIERARCHY_FOLDER_CONTENT_TYPE =
+ u"application/" HIERARCHY_URL_SCHEME "-folder";
+inline constexpr OUStringLiteral HIERARCHY_LINK_CONTENT_TYPE =
+ u"application/" HIERARCHY_URL_SCHEME "-link";
+
+struct ConfigProviderMapEntry
+{
+ css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider;
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xRootReadAccess;
+ bool bTriedToGetRootReadAccess;
+
+ ConfigProviderMapEntry() : bTriedToGetRootReadAccess( false ) {}
+};
+
+typedef std::unordered_map
+<
+ OUString, // service specifier
+ ConfigProviderMapEntry
+>
+ConfigProviderMap;
+
+class HierarchyContentProvider : public ::ucbhelper::ContentProviderImplHelper,
+ public css::lang::XInitialization
+{
+ ConfigProviderMap m_aConfigProviderMap;
+ css::uno::Reference< css::util::XOfficeInstallationDirectories > m_xOfficeInstDirs;
+
+public:
+ explicit HierarchyContentProvider(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~HierarchyContentProvider() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) 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;
+
+ // XInitialization
+ virtual void SAL_CALL
+ initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // Non-Interface methods
+ css::uno::Reference< css::lang::XMultiServiceFactory >
+ getConfigProvider( const OUString & rServiceSpecifier );
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ getRootConfigReadNameAccess( const OUString & rServiceSpecifier );
+
+ // Note: may return an empty reference.
+ css::uno::Reference< css::util::XOfficeInstallationDirectories >
+ getOfficeInstallationDirectories();
+};
+
+} // namespace hierarchy_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchyuri.cxx b/ucb/source/ucp/hierarchy/hierarchyuri.cxx
new file mode 100644
index 000000000..618a64022
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchyuri.cxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include "hierarchyuri.hxx"
+
+using namespace hierarchy_ucp;
+
+constexpr OUStringLiteral HIERARCHY_URL_SCHEME = u"vnd.sun.star.hier";
+constexpr OUStringLiteral DEFAULT_DATA_SOURCE_SERVICE =
+ u"com.sun.star.ucb.DefaultHierarchyDataSource";
+
+
+// HierarchyUri Implementation.
+
+
+void HierarchyUri::init() const
+{
+ // Already inited?
+ if ( m_aUri.isEmpty() || !m_aPath.isEmpty() )
+ return;
+
+ // Note: Maybe it's a re-init, setUri only resets m_aPath!
+ m_aService.clear();
+ m_aParentUri.clear();
+
+ // URI must match at least: <scheme>:
+ if ( m_aUri.getLength() < HIERARCHY_URL_SCHEME.getLength() + 1 )
+ {
+ // error, but remember that we did an init().
+ m_aPath = "/";
+ return;
+ }
+
+ // Scheme is case insensitive.
+ OUString aScheme
+ = m_aUri.copy( 0, HIERARCHY_URL_SCHEME.getLength() ).toAsciiLowerCase();
+ if ( aScheme == HIERARCHY_URL_SCHEME )
+ {
+ m_aUri = m_aUri.replaceAt( 0, aScheme.getLength(), aScheme );
+
+ sal_Int32 nPos = 0;
+
+ // If the URI has no service specifier, insert default service.
+ // This is for backward compatibility and for convenience.
+
+ if ( m_aUri.getLength() == HIERARCHY_URL_SCHEME.getLength() + 1 )
+ {
+ // root folder URI without path and service specifier.
+ m_aUri += "//" + DEFAULT_DATA_SOURCE_SERVICE + "/";
+ m_aService = DEFAULT_DATA_SOURCE_SERVICE ;
+
+ nPos = m_aUri.getLength() - 1;
+ }
+ else if ( ( m_aUri.getLength() == HIERARCHY_URL_SCHEME.getLength() + 2 )
+ &&
+ ( m_aUri[ HIERARCHY_URL_SCHEME.getLength() + 1 ] == '/' ) )
+ {
+ // root folder URI without service specifier.
+ m_aUri += "/" + DEFAULT_DATA_SOURCE_SERVICE + "/";
+ m_aService = DEFAULT_DATA_SOURCE_SERVICE;
+
+ nPos = m_aUri.getLength() - 1;
+ }
+ else if ( ( m_aUri.getLength() > HIERARCHY_URL_SCHEME.getLength() + 2 )
+ &&
+ ( m_aUri[ HIERARCHY_URL_SCHEME.getLength() + 2 ] != '/' ) )
+ {
+ // other (no root folder) URI without service specifier.
+ m_aUri = m_aUri.replaceAt(
+ HIERARCHY_URL_SCHEME.getLength() + 2,
+ 0,
+ rtl::OUStringConcatenation("/" + DEFAULT_DATA_SOURCE_SERVICE + "/") );
+ m_aService = DEFAULT_DATA_SOURCE_SERVICE;
+
+ nPos
+ = HIERARCHY_URL_SCHEME.getLength() + 3 + m_aService.getLength();
+ }
+ else
+ {
+ // URI with service specifier.
+ sal_Int32 nStart = HIERARCHY_URL_SCHEME.getLength() + 3;
+
+ // Here: - m_aUri has at least the form "<scheme>://"
+ // - nStart points to char after <scheme>:
+
+ // Only <scheme>:// ?
+ if ( nStart == m_aUri.getLength() )
+ {
+ // error, but remember that we did an init().
+ m_aPath = "/";
+ return;
+ }
+
+ // Empty path segments?
+ if ( m_aUri.indexOf("//", nStart) != -1 )
+ {
+ // error, but remember that we did an init().
+ m_aPath = "/";
+ return;
+ }
+
+ sal_Int32 nEnd = m_aUri.indexOf( '/', nStart );
+
+ // Only <scheme>:/// ?
+ if ( nEnd == nStart )
+ {
+ // error, but remember that we did an init().
+ m_aPath = "/";
+ return;
+ }
+
+ if ( nEnd == -1 )
+ {
+ // Trailing slash missing.
+ nEnd = m_aUri.getLength();
+ m_aUri += "/";
+ }
+
+ m_aService = m_aUri.copy( nStart, nEnd - nStart );
+
+ nPos = nEnd;
+ }
+
+ // Here: - m_aUri has at least the form "<scheme>://<service>/"
+ // - m_aService was set
+ // - m_aPath, m_aParentPath, m_aName not yet set
+ // - nPos points to slash after service specifier
+
+ // Remove trailing slash, if not a root folder URI.
+ sal_Int32 nEnd = m_aUri.lastIndexOf( '/' );
+ if ( ( nEnd > nPos ) && ( nEnd == ( m_aUri.getLength() - 1 ) ) )
+ m_aUri = m_aUri.copy( 0, nEnd );
+
+ // Path (includes leading slash)
+ m_aPath = m_aUri.copy( nPos );
+
+ // parent URI + name
+ sal_Int32 nLastSlash = m_aUri.lastIndexOf( '/' );
+ if ( ( nLastSlash != -1 ) &&
+ ( nLastSlash != m_aUri.getLength() - 1 ) ) // root
+ {
+ m_aParentUri = m_aUri.copy( 0, nLastSlash );
+ }
+
+ // success
+ m_bValid = true;
+ }
+ else
+ {
+ // error, but remember that we did an init().
+ m_aPath = "/";
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/hierarchyuri.hxx b/ucb/source/ucp/hierarchy/hierarchyuri.hxx
new file mode 100644
index 000000000..c3089ef70
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/hierarchyuri.hxx
@@ -0,0 +1,67 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+namespace hierarchy_ucp {
+
+
+class HierarchyUri
+{
+ mutable OUString m_aUri;
+ mutable OUString m_aParentUri;
+ mutable OUString m_aService;
+ mutable OUString m_aPath;
+ mutable bool m_bValid;
+
+private:
+ void init() const;
+
+public:
+ explicit HierarchyUri( const OUString & rUri )
+ : m_aUri( rUri ), m_bValid( false ) {}
+
+ bool isValid() const
+ { init(); return m_bValid; }
+
+ const OUString & getUri() const
+ { init(); return m_aUri; }
+
+ const OUString & getParentUri() const
+ { init(); return m_aParentUri; }
+
+ const OUString & getService() const
+ { init(); return m_aService; }
+
+ const OUString & getPath() const
+ { init(); return m_aPath; }
+
+ inline bool isRootFolder() const;
+};
+
+inline bool HierarchyUri::isRootFolder() const
+{
+ init();
+ return m_aPath == "/";
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/hierarchy/ucphier1.component b/ucb/source/ucp/hierarchy/ucphier1.component
new file mode 100644
index 000000000..e403857c7
--- /dev/null
+++ b/ucb/source/ucp/hierarchy/ucphier1.component
@@ -0,0 +1,31 @@
+<?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="com.sun.star.comp.ucb.HierarchyContentProvider"
+ constructor="ucb_HierarchyContentProvider_get_implementation" single-instance="true">
+ <service name="com.sun.star.ucb.HierarchyContentProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.ucb.HierarchyDataSource"
+ constructor="ucb_HierarchyDataSource_get_implementation" single-instance="true">
+ <service name="com.sun.star.ucb.DefaultHierarchyDataSource"/>
+ <service name="com.sun.star.ucb.HierarchyDataSource"/>
+ </implementation>
+</component>