The browse node factory had a wrapper using XAggregation that wraps every XBrowseNode. As far as I can tell this was used to replace the implementation of getChildNodes with one that checks for exceptions from the underlying provider and then sorts all of the resulting nodes by name. The trouble with this is that it prevents the script providers from detecting when a node is one from the same script provider and thus makes it difficult to implement the XCopyableBrowseNode interface. The documentation for XAggregation mentions this problem and also states that the interface is deprecated. To avoid this overhead, this patch replaces the aggregation with a helper function in comphelper that should be used to retrieve children from a browse node. That takes care of catching exceptions and sorting the results. Change-Id: I319888fee1b84ddcef397317d2cf7e72bd2867cd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208160 Tested-by: Jenkins Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
501 lines
14 KiB
C++
501 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <unotools/mediadescriptor.hxx>
|
|
|
|
#include <com/sun/star/document/XEmbeddedScripts.hpp>
|
|
#include <com/sun/star/frame/XModel.hpp>
|
|
#include <com/sun/star/reflection/ProxyFactory.hpp>
|
|
|
|
#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
|
|
#include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
|
|
#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
|
|
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <comphelper/sequenceashashmap.hxx>
|
|
|
|
#include "BrowseNodeFactoryImpl.hxx"
|
|
#include <util/MiscUtils.hxx>
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string_view>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::script;
|
|
using namespace ::sf_misc;
|
|
|
|
namespace browsenodefactory
|
|
{
|
|
namespace {
|
|
class BrowseNodeAggregator :
|
|
public ::cppu::WeakImplHelper< browse::XBrowseNode >
|
|
{
|
|
private:
|
|
OUString m_Name;
|
|
std::vector< Reference< browse::XBrowseNode > > m_Nodes;
|
|
|
|
public:
|
|
|
|
explicit BrowseNodeAggregator( const Reference< browse::XBrowseNode >& node )
|
|
: m_Name(node->getName())
|
|
{
|
|
m_Nodes.resize( 1 );
|
|
m_Nodes[ 0 ] = node;
|
|
}
|
|
|
|
void addBrowseNode( const Reference< browse::XBrowseNode>& node )
|
|
{
|
|
m_Nodes.push_back( node );
|
|
}
|
|
|
|
virtual OUString
|
|
SAL_CALL getName() override
|
|
{
|
|
return m_Name;
|
|
}
|
|
|
|
virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
|
|
getChildNodes() override
|
|
{
|
|
std::vector< Sequence< Reference < browse::XBrowseNode > > > seqs;
|
|
seqs.reserve( m_Nodes.size() );
|
|
|
|
sal_Int32 numChildren = 0;
|
|
|
|
for (Reference<XBrowseNode> & xNode : m_Nodes)
|
|
{
|
|
Sequence< Reference < browse::XBrowseNode > > children;
|
|
try
|
|
{
|
|
children = xNode->getChildNodes();
|
|
seqs.push_back( children );
|
|
numChildren += children.getLength();
|
|
}
|
|
catch ( Exception& )
|
|
{
|
|
// some form of exception getting child nodes so they
|
|
// won't be displayed
|
|
}
|
|
}
|
|
|
|
Sequence< Reference < browse::XBrowseNode > > result( numChildren );
|
|
sal_Int32 index = 0;
|
|
for ( const Sequence< Reference < browse::XBrowseNode > >& children : seqs )
|
|
{
|
|
std::copy(children.begin(), children.end(), std::next(result.getArray(), index));
|
|
index += children.getLength();
|
|
|
|
if (index >= numChildren)
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
virtual sal_Bool SAL_CALL
|
|
hasChildNodes() override
|
|
{
|
|
for (Reference<XBrowseNode> & xNode : m_Nodes)
|
|
{
|
|
try
|
|
{
|
|
if ( xNode->hasChildNodes() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
catch ( Exception& )
|
|
{
|
|
// some form of exception getting child nodes so move
|
|
// on to the next one
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual sal_Int16 SAL_CALL getType() override
|
|
{
|
|
return browse::BrowseNodeTypes::CONTAINER;
|
|
}
|
|
};
|
|
|
|
struct alphaSort
|
|
{
|
|
bool operator()( std::u16string_view a, std::u16string_view b )
|
|
{
|
|
return a.compare( b ) < 0;
|
|
}
|
|
};
|
|
class LocationBrowseNode :
|
|
public ::cppu::WeakImplHelper< browse::XBrowseNode >
|
|
{
|
|
private:
|
|
std::optional<std::unordered_map< OUString, Reference< browse::XBrowseNode > >> m_hBNA;
|
|
std::vector< OUString > m_vStr;
|
|
OUString m_sNodeName;
|
|
Reference< browse::XBrowseNode > m_origNode;
|
|
|
|
public:
|
|
|
|
explicit LocationBrowseNode( const Reference< browse::XBrowseNode >& node )
|
|
: m_sNodeName(node->getName())
|
|
{
|
|
m_origNode.set( node );
|
|
}
|
|
|
|
|
|
// XBrowseNode
|
|
|
|
virtual OUString SAL_CALL getName() override
|
|
{
|
|
return m_sNodeName;
|
|
}
|
|
|
|
virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
|
|
getChildNodes() override
|
|
{
|
|
if ( !m_hBNA )
|
|
{
|
|
loadChildNodes();
|
|
}
|
|
|
|
Sequence< Reference< browse::XBrowseNode > > children( m_hBNA->size() );
|
|
auto childrenRange = asNonConstRange(children);
|
|
sal_Int32 index = 0;
|
|
|
|
for ( const auto& str : m_vStr )
|
|
{
|
|
childrenRange[ index ].set( m_hBNA->find( str )->second );
|
|
++index;
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
virtual sal_Bool SAL_CALL hasChildNodes() override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual sal_Int16 SAL_CALL getType() override
|
|
{
|
|
return browse::BrowseNodeTypes::CONTAINER;
|
|
}
|
|
|
|
private:
|
|
|
|
void loadChildNodes()
|
|
{
|
|
m_hBNA.emplace();
|
|
|
|
const Sequence< Reference< browse::XBrowseNode > > langNodes =
|
|
m_origNode->getChildNodes();
|
|
|
|
for ( const auto& rLangNode : langNodes )
|
|
{
|
|
Reference< browse::XBrowseNode > xbn;
|
|
if ( rLangNode->getName() == "uno_packages" )
|
|
{
|
|
xbn.set( new LocationBrowseNode( rLangNode ) );
|
|
}
|
|
else
|
|
{
|
|
xbn.set( rLangNode );
|
|
}
|
|
|
|
const Sequence< Reference< browse::XBrowseNode > > grandchildren =
|
|
xbn->getChildNodes();
|
|
|
|
for ( const Reference< browse::XBrowseNode >& grandchild : grandchildren )
|
|
{
|
|
auto h_it =
|
|
m_hBNA->find( grandchild->getName() );
|
|
|
|
if ( h_it != m_hBNA->end() )
|
|
{
|
|
BrowseNodeAggregator* bna = static_cast< BrowseNodeAggregator* >( h_it->second.get() );
|
|
bna->addBrowseNode( grandchild );
|
|
}
|
|
else
|
|
{
|
|
Reference< browse::XBrowseNode > bna(
|
|
new BrowseNodeAggregator( grandchild ) );
|
|
(*m_hBNA)[ grandchild->getName() ].set( bna );
|
|
m_vStr.push_back( grandchild->getName() );
|
|
}
|
|
}
|
|
}
|
|
// sort children alphabetically
|
|
::std::sort( m_vStr.begin(), m_vStr.end(), alphaSort() );
|
|
}
|
|
};
|
|
|
|
std::vector< Reference< browse::XBrowseNode > > getAllBrowseNodes( const Reference< XComponentContext >& xCtx )
|
|
{
|
|
const Sequence< OUString > openDocs =
|
|
MiscUtils::allOpenTDocUrls( xCtx );
|
|
|
|
Reference< provider::XScriptProviderFactory > xFac;
|
|
sal_Int32 initialSize = openDocs.getLength() + 2;
|
|
sal_Int32 mspIndex = 0;
|
|
|
|
std::vector< Reference < browse::XBrowseNode > > locnBNs( initialSize );
|
|
try
|
|
{
|
|
xFac = provider::theMasterScriptProviderFactory::get( xCtx );
|
|
|
|
locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( u"user"_ustr ) ), UNO_QUERY_THROW );
|
|
locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( u"share"_ustr ) ), UNO_QUERY_THROW );
|
|
}
|
|
// TODO proper exception handling, should throw
|
|
catch( const Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("scripting", "Caught" );
|
|
locnBNs.resize( mspIndex );
|
|
return locnBNs;
|
|
}
|
|
|
|
for ( const auto& rDoc : openDocs )
|
|
{
|
|
try
|
|
{
|
|
Reference< frame::XModel > model( MiscUtils::tDocUrlToModel( rDoc ), UNO_SET_THROW );
|
|
|
|
// #i44599 Check if it's a real document or something special like Hidden/Preview
|
|
css::uno::Reference< css::frame::XController > xCurrentController = model->getCurrentController();
|
|
if( xCurrentController.is() )
|
|
{
|
|
comphelper::SequenceAsHashMap aMD(model->getArgs());
|
|
bool bDefault = false;
|
|
bool bHidden = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_HIDDEN, bDefault );
|
|
bool bPreview = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_PREVIEW, bDefault );
|
|
if( !bHidden && !bPreview )
|
|
{
|
|
Reference< document::XEmbeddedScripts > xScripts( model, UNO_QUERY );
|
|
if ( xScripts.is() )
|
|
locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( model ) ), UNO_QUERY_THROW );
|
|
}
|
|
}
|
|
}
|
|
catch( const Exception& )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("scripting");
|
|
}
|
|
|
|
}
|
|
|
|
std::vector< Reference < browse::XBrowseNode > > locnBNs_Return( mspIndex );
|
|
for ( sal_Int32 j = 0; j < mspIndex; j++ )
|
|
locnBNs_Return[j] = locnBNs[j];
|
|
|
|
return locnBNs_Return;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
typedef ::std::vector< Reference< browse::XBrowseNode > > vXBrowseNodes;
|
|
|
|
namespace {
|
|
|
|
class DefaultRootBrowseNode :
|
|
public ::cppu::WeakImplHelper< browse::XBrowseNode >
|
|
{
|
|
|
|
private:
|
|
vXBrowseNodes m_vNodes;
|
|
OUString m_Name;
|
|
|
|
public:
|
|
explicit DefaultRootBrowseNode( const Reference< XComponentContext >& xCtx )
|
|
{
|
|
std::vector< Reference< browse::XBrowseNode > > nodes =
|
|
getAllBrowseNodes( xCtx );
|
|
|
|
for (Reference< browse::XBrowseNode > & xNode : nodes)
|
|
{
|
|
m_vNodes.emplace_back(xNode);
|
|
}
|
|
m_Name = "Root";
|
|
}
|
|
|
|
virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
|
|
getChildNodes() override
|
|
{
|
|
// no need to sort user, share, doc1...docN
|
|
//::std::sort( m_vNodes.begin(), m_vNodes.end(), alphaSortForBNodes() );
|
|
Sequence < Reference< browse::XBrowseNode > > children( m_vNodes.size() );
|
|
auto childrenRange = asNonConstRange(children);
|
|
sal_Int32 i = 0;
|
|
for ( const auto& rxNode : m_vNodes )
|
|
{
|
|
childrenRange[ i ].set( rxNode );
|
|
i++;
|
|
}
|
|
return children;
|
|
}
|
|
|
|
virtual sal_Int16 SAL_CALL getType() override
|
|
{
|
|
return browse::BrowseNodeTypes::ROOT;
|
|
}
|
|
|
|
virtual OUString
|
|
SAL_CALL getName() override
|
|
{
|
|
return m_Name;
|
|
}
|
|
|
|
virtual sal_Bool SAL_CALL
|
|
hasChildNodes() override
|
|
{
|
|
bool result = true;
|
|
if ( m_vNodes.empty() )
|
|
{
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
|
|
class SelectorBrowseNode :
|
|
public ::cppu::WeakImplHelper< browse::XBrowseNode >
|
|
{
|
|
private:
|
|
Reference< XComponentContext > m_xComponentContext;
|
|
|
|
public:
|
|
explicit SelectorBrowseNode( const Reference< XComponentContext >& xContext )
|
|
: m_xComponentContext( xContext )
|
|
{
|
|
}
|
|
|
|
virtual OUString SAL_CALL getName() override
|
|
{
|
|
return u"Root"_ustr;
|
|
}
|
|
|
|
virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
|
|
getChildNodes() override
|
|
{
|
|
|
|
std::vector< Reference < browse::XBrowseNode > > locnBNs = getAllBrowseNodes( m_xComponentContext );
|
|
|
|
Sequence< Reference< browse::XBrowseNode > > children(
|
|
locnBNs.size() );
|
|
auto childrenRange = asNonConstRange(children);
|
|
|
|
for ( size_t j = 0; j < locnBNs.size(); j++ )
|
|
{
|
|
childrenRange[j] = new LocationBrowseNode( locnBNs[j] );
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
virtual sal_Bool SAL_CALL hasChildNodes() override
|
|
{
|
|
return true; // will always be user and share
|
|
}
|
|
|
|
virtual sal_Int16 SAL_CALL getType() override
|
|
{
|
|
return browse::BrowseNodeTypes::CONTAINER;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
BrowseNodeFactoryImpl::BrowseNodeFactoryImpl(
|
|
Reference< XComponentContext > const & xComponentContext )
|
|
: m_xComponentContext( xComponentContext )
|
|
{
|
|
}
|
|
|
|
BrowseNodeFactoryImpl::~BrowseNodeFactoryImpl()
|
|
{
|
|
}
|
|
|
|
|
|
// Implementation of XBrowseNodeFactory
|
|
|
|
|
|
/*
|
|
* The selector hierarchy is the standard hierarchy for organizers with the
|
|
* language nodes removed.
|
|
*/
|
|
Reference< browse::XBrowseNode > SAL_CALL
|
|
BrowseNodeFactoryImpl::createView( sal_Int16 viewType )
|
|
{
|
|
switch( viewType )
|
|
{
|
|
case browse::BrowseNodeFactoryViewTypes::MACROSELECTOR:
|
|
return new SelectorBrowseNode( m_xComponentContext );
|
|
case browse::BrowseNodeFactoryViewTypes::MACROORGANIZER:
|
|
return getOrganizerHierarchy();
|
|
default:
|
|
throw RuntimeException( u"Unknown view type"_ustr );
|
|
}
|
|
}
|
|
|
|
Reference< browse::XBrowseNode >
|
|
BrowseNodeFactoryImpl::getOrganizerHierarchy() const
|
|
{
|
|
Reference< browse::XBrowseNode > xRet = new DefaultRootBrowseNode( m_xComponentContext );
|
|
return xRet;
|
|
}
|
|
|
|
// Implementation of XServiceInfo
|
|
|
|
|
|
OUString SAL_CALL
|
|
BrowseNodeFactoryImpl::getImplementationName()
|
|
{
|
|
return u"com.sun.star.script.browse.BrowseNodeFactory"_ustr;
|
|
}
|
|
|
|
Sequence< OUString > SAL_CALL
|
|
BrowseNodeFactoryImpl::getSupportedServiceNames()
|
|
{
|
|
return { u"com.sun.star.script.browse.BrowseNodeFactory"_ustr };
|
|
}
|
|
|
|
sal_Bool BrowseNodeFactoryImpl::supportsService(OUString const & serviceName )
|
|
{
|
|
return cppu::supportsService(this, serviceName);
|
|
}
|
|
|
|
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
|
|
scripting_BrowseNodeFactoryImpl_get_implementation(
|
|
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
|
|
{
|
|
return cppu::acquire(new BrowseNodeFactoryImpl(context));
|
|
}
|
|
|
|
} // namespace browsenodefactory
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|