summaryrefslogtreecommitdiffstats
path: root/xmlhelp/source/cxxhelp/provider
diff options
context:
space:
mode:
Diffstat (limited to 'xmlhelp/source/cxxhelp/provider')
-rw-r--r--xmlhelp/source/cxxhelp/provider/content.cxx441
-rw-r--r--xmlhelp/source/cxxhelp/provider/content.hxx99
-rw-r--r--xmlhelp/source/cxxhelp/provider/contentcaps.cxx199
-rw-r--r--xmlhelp/source/cxxhelp/provider/databases.cxx1876
-rw-r--r--xmlhelp/source/cxxhelp/provider/databases.hxx449
-rw-r--r--xmlhelp/source/cxxhelp/provider/db.cxx264
-rw-r--r--xmlhelp/source/cxxhelp/provider/db.hxx96
-rw-r--r--xmlhelp/source/cxxhelp/provider/inputstream.cxx185
-rw-r--r--xmlhelp/source/cxxhelp/provider/inputstream.hxx96
-rw-r--r--xmlhelp/source/cxxhelp/provider/provider.cxx198
-rw-r--r--xmlhelp/source/cxxhelp/provider/provider.hxx106
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultset.cxx61
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultset.hxx48
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetbase.cxx485
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetbase.hxx400
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx42
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx331
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx47
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx69
-rw-r--r--xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx47
-rw-r--r--xmlhelp/source/cxxhelp/provider/urlparameter.cxx983
-rw-r--r--xmlhelp/source/cxxhelp/provider/urlparameter.hxx223
22 files changed, 6745 insertions, 0 deletions
diff --git a/xmlhelp/source/cxxhelp/provider/content.cxx b/xmlhelp/source/cxxhelp/provider/content.cxx
new file mode 100644
index 0000000000..6d12041de9
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/content.cxx
@@ -0,0 +1,441 @@
+/* -*- 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/beans/PropertyValue.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/macros.hxx>
+#include <utility>
+#include "content.hxx"
+#include "provider.hxx"
+#include "resultset.hxx"
+#include "databases.hxx"
+#include "resultsetfactory.hxx"
+#include "resultsetbase.hxx"
+#include "resultsetforroot.hxx"
+#include "resultsetforquery.hxx"
+
+using namespace com::sun::star;
+using namespace chelp;
+
+// Content Implementation.
+
+Content::Content( const uno::Reference< uno::XComponentContext >& rxContext,
+ ::ucbhelper::ContentProviderImplHelper* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >&
+ Identifier,
+ Databases* pDatabases )
+ : ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_aURLParameter( Identifier->getContentIdentifier(),pDatabases ),
+ m_pDatabases( pDatabases ) // not owner
+{
+}
+
+// virtual
+Content::~Content()
+{
+}
+
+// virtual
+uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
+{
+ uno::Any aRet;
+ return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+XTYPEPROVIDER_COMMON_IMPL( Content );
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
+{
+ static cppu::OTypeCollection ourTypeCollection(
+ 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 ourTypeCollection.getTypes();
+}
+
+// XServiceInfo methods.
+
+// virtual
+OUString SAL_CALL Content::getImplementationName()
+{
+ return "CHelpContent";
+}
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+{
+ return { "com.sun.star.ucb.CHelpContent" };
+}
+
+// XContent methods.
+
+// virtual
+OUString SAL_CALL Content::getContentType()
+{
+ return MYUCP_CONTENT_TYPE;
+}
+
+// XCommandProcessor methods.
+
+//virtual
+void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
+{
+}
+
+namespace {
+
+class ResultSetForRootFactory
+ : public ResultSetFactory
+{
+private:
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< ucb::XContentProvider > m_xProvider;
+ uno::Sequence< beans::Property > m_seq;
+ URLParameter m_aURLParameter;
+ Databases* m_pDatabases;
+
+public:
+
+ ResultSetForRootFactory(
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< ucb::XContentProvider > xProvider,
+ const uno::Sequence< beans::Property >& seq,
+ const URLParameter& rURLParameter,
+ Databases* pDatabases )
+ : m_xContext(std::move( xContext )),
+ m_xProvider(std::move( xProvider )),
+ m_seq( seq ),
+ m_aURLParameter(rURLParameter),
+ m_pDatabases( pDatabases )
+ {
+ }
+
+ rtl::Reference<ResultSetBase> createResultSet() override
+ {
+ return new ResultSetForRoot( m_xContext,
+ m_xProvider,
+ m_seq,
+ m_aURLParameter,
+ m_pDatabases );
+ }
+};
+
+class ResultSetForQueryFactory
+ : public ResultSetFactory
+{
+private:
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< ucb::XContentProvider > m_xProvider;
+ uno::Sequence< beans::Property > m_seq;
+ URLParameter m_aURLParameter;
+ Databases* m_pDatabases;
+
+public:
+
+ ResultSetForQueryFactory(
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< ucb::XContentProvider > xProvider,
+ const uno::Sequence< beans::Property >& seq,
+ const URLParameter& rURLParameter,
+ Databases* pDatabases )
+ : m_xContext(std::move( xContext )),
+ m_xProvider(std::move( xProvider )),
+ m_seq( seq ),
+ m_aURLParameter(rURLParameter),
+ m_pDatabases( pDatabases )
+ {
+ }
+
+ rtl::Reference<ResultSetBase> createResultSet() override
+ {
+ return new ResultSetForQuery( m_xContext,
+ m_xProvider,
+ m_seq,
+ m_aURLParameter,
+ m_pDatabases );
+ }
+};
+
+}
+
+// virtual
+uno::Any SAL_CALL Content::execute(
+ const ucb::Command& aCommand,
+ sal_Int32,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ aRet <<= lang::IllegalArgumentException();
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ aRet <<= getPropertyValues( Properties );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+ uno::Sequence<beans::PropertyValue> propertyValues;
+
+ if( ! ( aCommand.Argument >>= propertyValues ) ) {
+ aRet <<= lang::IllegalArgumentException();
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ uno::Sequence< uno::Any > ret(propertyValues.getLength());
+ const uno::Sequence< beans::Property > props(getProperties(Environment));
+ // No properties can be set
+ std::transform(std::cbegin(propertyValues), std::cend(propertyValues), ret.getArray(),
+ [&props](const beans::PropertyValue& rPropVal) {
+ if (std::any_of(props.begin(), props.end(),
+ [&rPropVal](const beans::Property& rProp) { return rProp.Name == rPropVal.Name; }))
+ return css::uno::toAny(lang::IllegalAccessException());
+ return css::uno::toAny(beans::UnknownPropertyException());
+ });
+
+ aRet <<= ret;
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+ // Note: Implemented by base class.
+ aRet <<= getPropertySetInfo( Environment );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+ // Note: Implemented by base class.
+ aRet <<= getCommandInfo( Environment );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+ ucb::OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ aRet <<= lang::IllegalArgumentException();
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ uno::Reference< io::XActiveDataSink > xActiveDataSink(
+ aOpenCommand.Sink, uno::UNO_QUERY);
+
+ if(xActiveDataSink.is())
+ m_aURLParameter.open(xActiveDataSink);
+
+ uno::Reference< io::XActiveDataStreamer > xActiveDataStreamer(
+ aOpenCommand.Sink, uno::UNO_QUERY);
+
+ if(xActiveDataStreamer.is()) {
+ aRet <<= ucb::UnsupportedDataSinkException();
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ uno::Reference< io::XOutputStream > xOutputStream(
+ aOpenCommand.Sink, uno::UNO_QUERY);
+
+ if(xOutputStream.is() )
+ m_aURLParameter.open(xOutputStream);
+
+ if( m_aURLParameter.isRoot() )
+ {
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet(
+ m_xContext,
+ aOpenCommand,
+ std::make_unique<ResultSetForRootFactory>(
+ m_xContext,
+ m_xProvider.get(),
+ aOpenCommand.Properties,
+ m_aURLParameter,
+ m_pDatabases));
+ aRet <<= xSet;
+ }
+ else if( m_aURLParameter.isQuery() )
+ {
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet(
+ m_xContext,
+ aOpenCommand,
+ std::make_unique<ResultSetForQueryFactory>(
+ m_xContext,
+ m_xProvider.get(),
+ aOpenCommand.Properties,
+ m_aURLParameter,
+ m_pDatabases ) );
+ aRet <<= xSet;
+ }
+ }
+ else
+ {
+ // Unsupported command
+ aRet <<= ucb::UnsupportedCommandException();
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ return aRet;
+}
+
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow =
+ new ::ucbhelper::PropertyValueSet( m_xContext );
+
+ for ( const beans::Property& rProp : rProperties )
+ {
+ if ( rProp.Name == "ContentType" )
+ xRow->appendString(
+ rProp,
+ OUString(
+ "application/vnd.sun.star.help" ) );
+ else if ( rProp.Name == "Title" )
+ xRow->appendString ( rProp,m_aURLParameter.get_title() );
+ else if ( rProp.Name == "IsReadOnly" )
+ xRow->appendBoolean( rProp,true );
+ else if ( rProp.Name == "IsDocument" )
+ xRow->appendBoolean(
+ rProp,
+ m_aURLParameter.isFile() || m_aURLParameter.isRoot() );
+ else if ( rProp.Name == "IsFolder" )
+ xRow->appendBoolean(
+ rProp,
+ ! m_aURLParameter.isFile() || m_aURLParameter.isRoot() );
+ else if ( rProp.Name == "IsErrorDocument" )
+ xRow->appendBoolean( rProp, m_aURLParameter.isErrorDocument() );
+ else if ( rProp.Name == "MediaType" )
+ if( m_aURLParameter.isActive() )
+ xRow->appendString(
+ rProp,
+ OUString( "text/plain" ) );
+ else if( m_aURLParameter.isFile() )
+ xRow->appendString(
+ rProp,OUString( "text/html" ) );
+ else if( m_aURLParameter.isRoot() )
+ xRow->appendString(
+ rProp,
+ OUString( "text/css" ) );
+ else
+ xRow->appendVoid( rProp );
+ else if( m_aURLParameter.isModule() )
+ if ( rProp.Name == "KeywordList" )
+ {
+ KeywordInfo *inf =
+ m_pDatabases->getKeyword( m_aURLParameter.get_module(),
+ m_aURLParameter.get_language() );
+
+ uno::Any aAny;
+ if( inf )
+ aAny <<= inf->getKeywordList();
+ xRow->appendObject( rProp,aAny );
+ }
+ else if ( rProp.Name == "KeywordRef" )
+ {
+ KeywordInfo *inf =
+ m_pDatabases->getKeyword( m_aURLParameter.get_module(),
+ m_aURLParameter.get_language() );
+
+ uno::Any aAny;
+ if( inf )
+ aAny <<= inf->getIdList();
+ xRow->appendObject( rProp,aAny );
+ }
+ else if ( rProp.Name == "KeywordAnchorForRef" )
+ {
+ KeywordInfo *inf =
+ m_pDatabases->getKeyword( m_aURLParameter.get_module(),
+ m_aURLParameter.get_language() );
+
+ uno::Any aAny;
+ if( inf )
+ aAny <<= inf->getAnchorList();
+ xRow->appendObject( rProp,aAny );
+ }
+ else if ( rProp.Name == "KeywordTitleForRef" )
+ {
+ KeywordInfo *inf =
+ m_pDatabases->getKeyword( m_aURLParameter.get_module(),
+ m_aURLParameter.get_language() );
+
+ uno::Any aAny;
+ if( inf )
+ aAny <<= inf->getTitleList();
+ xRow->appendObject( rProp,aAny );
+ }
+ else if ( rProp.Name == "SearchScopes" )
+ {
+ uno::Sequence< OUString > seq{ "Heading", "FullText" };
+ xRow->appendObject( rProp, uno::Any(seq) );
+ }
+ else if ( rProp.Name == "Order" )
+ {
+ StaticModuleInformation *inf =
+ m_pDatabases->getStaticInformationForModule(
+ m_aURLParameter.get_module(),
+ m_aURLParameter.get_language() );
+
+ uno::Any aAny;
+ if( inf )
+ aAny <<= sal_Int32( inf->get_order() );
+ xRow->appendObject( rProp,aAny );
+ }
+ else
+ xRow->appendVoid( rProp );
+ else if( "AnchorName" == rProp.Name &&
+ m_aURLParameter.isFile() )
+ xRow->appendString( rProp,m_aURLParameter.get_tag() );
+ else
+ xRow->appendVoid( rProp );
+ }
+
+ return xRow;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/content.hxx b/xmlhelp/source/cxxhelp/provider/content.hxx
new file mode 100644
index 0000000000..362797a419
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/content.hxx
@@ -0,0 +1,99 @@
+/* -*- 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/contenthelper.hxx>
+
+#include "urlparameter.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+ struct PropertyValue;
+}
+namespace com::sun::star::sdbc {
+ class XRow;
+}
+
+namespace chelp
+{
+
+ class Databases;
+
+ class Content : public ::ucbhelper::ContentImplHelper
+ {
+ public:
+
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ::ucbhelper::ContentProviderImplHelper* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ Databases* pDatabases );
+
+ virtual ~Content() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) 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;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL
+ execute( const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override;
+
+ virtual void SAL_CALL
+ abort( sal_Int32 CommandId ) override;
+
+ private:
+
+ // private members;
+
+ URLParameter m_aURLParameter;
+ Databases* m_pDatabases;
+
+ // private methods
+
+ 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 { return OUString(); }
+
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties );
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/contentcaps.cxx b/xmlhelp/source/cxxhelp/provider/contentcaps.cxx
new file mode 100644
index 0000000000..b18061e434
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/contentcaps.cxx
@@ -0,0 +1,199 @@
+/* -*- 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 <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/uno/Sequence.hxx>
+
+#include "content.hxx"
+
+using namespace com::sun::star;
+
+using namespace chelp;
+
+// virtual
+uno::Sequence< beans::Property > Content::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ bool withMediaType = m_aURLParameter.isFile() || m_aURLParameter.isRoot();
+ bool isModule = m_aURLParameter.isModule();
+ bool isFile = m_aURLParameter.isFile();
+
+ sal_Int32 num = withMediaType ? 7 : 6;
+ if( isModule ) num+=6;
+ if( isFile ) num++;
+
+ uno::Sequence< beans::Property > props(num);
+ auto pprops = props.getArray();
+
+ sal_Int32 idx = 0;
+ pprops[idx++] =
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "IsReadOnly",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "IsErrorDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ if( withMediaType )
+ pprops[idx++] =
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ if( isModule )
+ {
+ pprops[idx++] =
+ beans::Property(
+ "Order",
+ -1,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "KeywordList",
+ -1,
+ cppu::UnoType<uno::Sequence< OUString >>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "KeywordRef",
+ -1,
+ cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "KeywordTitleForRef",
+ -1,
+ cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "KeywordAnchorForRef",
+ -1,
+ cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+
+ pprops[idx++] =
+ beans::Property(
+ "SearchScopes",
+ -1,
+ cppu::UnoType<uno::Sequence< OUString >>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+ }
+
+ if( isFile )
+ {
+ pprops[idx++] =
+ beans::Property(
+ "AnchorName",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY );
+ }
+
+ return props;
+}
+
+// virtual
+uno::Sequence< ucb::CommandInfo > Content::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ // Supported commands
+
+#define COMMAND_COUNT 5
+
+ static const ucb::CommandInfo aCommandInfoTable[] =
+ {
+ // 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()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+ };
+
+ return uno::Sequence< ucb::CommandInfo >(
+ aCommandInfoTable, COMMAND_COUNT );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/databases.cxx b/xmlhelp/source/cxxhelp/provider/databases.cxx
new file mode 100644
index 0000000000..1969c1d390
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/databases.cxx
@@ -0,0 +1,1876 @@
+/* -*- 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 <memory>
+#include "db.hxx"
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <rtl/character.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/i18n/Collator.hpp>
+#include <comphelper/propertysequence.hxx>
+#include "inputstream.hxx"
+#include <algorithm>
+#include <cassert>
+#include <string.h>
+#include <string_view>
+
+#include <helpcompiler/HelpIndexer.hxx>
+
+// Extensible help
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <utility>
+
+#include "databases.hxx"
+#include "urlparameter.hxx"
+
+#ifdef _WIN32
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+
+using namespace chelp;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using namespace com::sun::star::container;
+using namespace com::sun::star::i18n;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::deployment;
+using namespace com::sun::star::beans;
+
+OUString Databases::expandURL( const OUString& aURL )
+{
+ std::unique_lock aGuard(m_aMutex);
+ return expandURL(aGuard, aURL);
+}
+
+OUString Databases::expandURL( std::unique_lock<std::mutex>& /*rGuard*/, const OUString& aURL )
+{
+ OUString aRetURL = expandURL( aURL, m_xContext );
+ return aRetURL;
+}
+
+OUString Databases::expandURL( const OUString& aURL, const Reference< uno::XComponentContext >& xContext )
+{
+ static Reference< util::XMacroExpander > xMacroExpander;
+ static Reference< uri::XUriReferenceFactory > xFac;
+
+ if( !xMacroExpander.is() || !xFac.is() )
+ {
+ xFac = uri::UriReferenceFactory::create( xContext );
+
+ xMacroExpander = util::theMacroExpander::get(xContext);
+ }
+
+ OUString aRetURL = aURL;
+ if( xMacroExpander.is() )
+ {
+ Reference< uri::XUriReference > uriRef;
+ for (;;)
+ {
+ uriRef = xFac->parse( aRetURL );
+ if ( uriRef.is() )
+ {
+ Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
+ if( !sxUri.is() )
+ break;
+
+ aRetURL = sxUri->expand( xMacroExpander );
+ }
+ }
+ }
+ return aRetURL;
+}
+
+const char vendVersion[] = "%VENDORVERSION";
+const char vendName[] = "%VENDORNAME";
+const char prodVersion[] = "%PRODUCTVERSION";
+const char vendShort[] = "%VENDORSHORT";
+const char prodName[] = "%PRODUCTNAME";
+const char newProdVersion[] = "$[officeversion]";
+const char newProdName[] = "$[officename]";
+
+Databases::Databases( bool showBasic,
+ const OUString& instPath,
+ const OUString& productName,
+ const OUString& productVersion,
+ const OUString& styleSheet,
+ Reference< uno::XComponentContext > const & xContext )
+ : m_xContext( xContext ),
+ m_bShowBasic(showBasic),
+ m_aCSS(styleSheet.toAsciiLowerCase())
+{
+ m_xSMgr = m_xContext->getServiceManager();
+
+ m_vAdd[0] = 12;
+ m_vAdd[1] = 15;
+ m_vAdd[2] = 11;
+ m_vAdd[3] = 14;
+ m_vAdd[4] = 12;
+ m_vAdd[5] = 13;
+ m_vAdd[6] = 16;
+
+ m_vReplacement[0] = productName;
+ m_vReplacement[1] = productVersion;
+ // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
+ m_vReplacement[5] = productName;
+ m_vReplacement[6] = productVersion;
+
+ setInstallPath( instPath );
+
+ m_xSFA = ucb::SimpleFileAccess::create(m_xContext);
+}
+
+Databases::~Databases()
+{
+ // unload the databases
+
+ // DatabasesTable
+ m_aDatabases.clear();
+
+ // ModInfoTable
+ m_aModInfo.clear();
+
+ // KeywordInfoTable
+ m_aKeywordInfo.clear();
+}
+
+OString Databases::getImageTheme() const
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider =
+ configuration::theDefaultProvider::get(m_xContext);
+
+ // set root path
+ uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("org.openoffice.Office.Common"))}
+ }));
+
+ // open it
+ uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ lParams) );
+
+ uno::Reference< container::XHierarchicalNameAccess > xAccess(xCFG, uno::UNO_QUERY_THROW);
+ uno::Any aResult = xAccess->getByHierarchicalName("Misc/SymbolStyle");
+ OUString aSymbolsStyleName;
+ aResult >>= aSymbolsStyleName;
+
+ if ( aSymbolsStyleName.isEmpty() || aSymbolsStyleName == "auto" )
+ {
+ aSymbolsStyleName = "colibre";
+ }
+ return aSymbolsStyleName.toUtf8();
+}
+
+void Databases::replaceName( OUString& oustring ) const
+{
+ sal_Int32 idx = -1,idx1 = -1,idx2 = -1,k = 0,off;
+ bool cap = false;
+ OUStringBuffer aStrBuf( 0 );
+
+ while( true )
+ {
+ ++idx;
+ idx1 = oustring.indexOf( '%', idx);
+ idx2 = oustring.indexOf( '$', idx);
+
+ if(idx1 == -1 && idx2 == -1)
+ break;
+
+ if(idx1 == -1)
+ idx = idx2;
+ else if(idx2 == -1)
+ idx = idx1;
+ else {
+ // no index is zero
+ if(idx1 < idx2)
+ idx = idx1;
+ else if(idx2 < idx1 )
+ idx = idx2;
+ }
+
+ if( oustring.indexOf( prodName,idx ) == idx )
+ off = PRODUCTNAME;
+ else if( oustring.indexOf( prodVersion,idx ) == idx )
+ off = PRODUCTVERSION;
+ else if( oustring.indexOf( vendName,idx ) == idx )
+ off = VENDORNAME;
+ else if( oustring.indexOf( vendVersion,idx ) == idx )
+ off = VENDORVERSION;
+ else if( oustring.indexOf( vendShort,idx ) == idx )
+ off = VENDORSHORT;
+ else if( oustring.indexOf( newProdName,idx ) == idx )
+ off = NEWPRODUCTNAME;
+ else if( oustring.indexOf( newProdVersion,idx ) == idx )
+ off = NEWPRODUCTVERSION;
+ else
+ off = -1;
+
+ if( off != -1 )
+ {
+ if( ! cap )
+ {
+ cap = true;
+ aStrBuf.ensureCapacity( 256 );
+ }
+
+ aStrBuf.append( &oustring.getStr()[k],idx - k );
+ aStrBuf.append( m_vReplacement[off] );
+ k = idx + m_vAdd[off];
+ }
+ }
+
+ if( cap )
+ {
+ if( k < oustring.getLength() )
+ aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
+ oustring = aStrBuf.makeStringAndClear();
+ }
+}
+
+OUString Databases::getInstallPathAsURL()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ return m_aInstallDirectory;
+}
+
+OUString Databases::getInstallPathAsURL(std::unique_lock<std::mutex>& )
+{
+ return m_aInstallDirectory;
+}
+
+const std::vector< OUString >& Databases::getModuleList( const OUString& Language )
+{
+ if( m_avModules.empty() )
+ {
+ OUString fileName,dirName = getInstallPathAsURL() + processLang( Language );
+ osl::Directory dirFile( dirName );
+
+ osl::DirectoryItem aDirItem;
+ osl::FileStatus aStatus( osl_FileStatus_Mask_FileName );
+
+ if( osl::FileBase::E_None != dirFile.open() )
+ return m_avModules;
+
+ while( dirFile.getNextItem( aDirItem ) == osl::FileBase::E_None &&
+ aDirItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
+ {
+ if( ! aStatus.isValid( osl_FileStatus_Mask_FileName ) )
+ continue;
+
+ fileName = aStatus.getFileName();
+
+ // Check, whether fileName is of the form *.cfg
+ if (!fileName.endsWithIgnoreAsciiCase(".cfg", &fileName)) {
+ continue;
+ }
+ fileName = fileName.toAsciiLowerCase();
+ if (fileName == "picture"
+ || (!m_bShowBasic && fileName == "sbasic"))
+ {
+ continue;
+ }
+
+ m_avModules.push_back( fileName );
+ }
+ }
+ return m_avModules;
+}
+
+StaticModuleInformation* Databases::getStaticInformationForModule( std::u16string_view Module,
+ const OUString& Language )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ OUString key = processLang(aGuard, Language) + "/" + Module;
+
+ std::pair< ModInfoTable::iterator,bool > aPair =
+ m_aModInfo.emplace(key,nullptr);
+
+ ModInfoTable::iterator it = aPair.first;
+
+ if( aPair.second && ! it->second )
+ {
+ osl::File cfgFile( m_aInstallDirectory + key + ".cfg" );
+
+ if( osl::FileBase::E_None != cfgFile.open( osl_File_OpenFlag_Read ) )
+ it->second = nullptr;
+ else
+ {
+ sal_uInt32 pos = 0;
+ sal_uInt64 nRead;
+ char buffer[2048];
+ sal_Unicode lineBuffer[1028];
+ OUStringBuffer fileContent;
+
+ while( osl::FileBase::E_None == cfgFile.read( &buffer,2048,nRead ) && nRead )
+ fileContent.append(OUString( buffer,sal_Int32( nRead ),RTL_TEXTENCODING_UTF8 ));
+
+ cfgFile.close();
+
+ const sal_Unicode* str = fileContent.getStr();
+ OUString current,program,startid,title;
+ OUString order( "1" );
+
+ for( sal_Int32 i = 0;i < fileContent.getLength();i++ )
+ {
+ sal_Unicode ch = str[ i ];
+ if( ch == '\n' || ch == '\r' )
+ {
+ if( pos )
+ {
+ current = OUString( lineBuffer,pos );
+
+ if( current.startsWith("Title") )
+ {
+ title = current.copy( current.indexOf( '=' ) + 1 );
+ }
+ else if( current.startsWith("Start") )
+ {
+ startid = current.copy( current.indexOf('=') + 1 );
+ }
+ else if( current.startsWith("Program") )
+ {
+ program = current.copy( current.indexOf('=') + 1 );
+ }
+ else if( current.startsWith("Order") )
+ {
+ order = current.copy( current.indexOf('=') + 1 );
+ }
+ }
+ pos = 0;
+ }
+ else
+ lineBuffer[ pos++ ] = ch;
+ }
+ replaceName( title );
+ it->second.reset(new StaticModuleInformation( title,
+ startid,
+ program,
+ order ));
+ }
+ }
+
+ return it->second.get();
+}
+
+OUString Databases::processLang( const OUString& Language )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return processLang(aGuard, Language);
+}
+
+OUString Databases::processLang( std::unique_lock<std::mutex>& /*rGuard*/, const OUString& Language )
+{
+ OUString ret;
+ LangSetTable::iterator it = m_aLangSet.find( Language );
+
+ if( it == m_aLangSet.end() )
+ {
+ // XXX the old code looked for '-' and '_' as separator between
+ // language and country, no idea if '_' actually still can happen
+ // (probably not), but play safe and keep that and transform to proper
+ // BCP47.
+ const OUString aBcp47( Language.replaceAll( "_", "-"));
+
+ // Try if language tag or fallbacks are installed.
+ osl::DirectoryItem aDirItem;
+ std::vector<OUString> aFallbacks( LanguageTag( aBcp47).getFallbackStrings(true));
+ for (auto const & rFB : aFallbacks)
+ {
+ if (osl::FileBase::E_None == osl::DirectoryItem::get( m_aInstallDirectory + rFB, aDirItem))
+ {
+ ret = rFB;
+ m_aLangSet[ Language ] = ret;
+ break; // for
+ }
+ }
+ }
+ else
+ ret = it->second;
+
+ return ret;
+}
+
+helpdatafileproxy::Hdf* Databases::getHelpDataFile(std::u16string_view Database,
+ const OUString& Language, bool helpText,
+ const OUString* pExtensionPath )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ return getHelpDataFile(aGuard, Database, Language, helpText, pExtensionPath);
+}
+
+helpdatafileproxy::Hdf* Databases::getHelpDataFile(std::unique_lock<std::mutex>& rGuard,
+ std::u16string_view Database,
+ const OUString& Language, bool helpText,
+ const OUString* pExtensionPath )
+
+{
+ if( Database.empty() || Language.isEmpty() )
+ return nullptr;
+
+ OUString aFileExt( helpText ? OUString(".ht") : OUString(".db") );
+ OUString dbFileName = OUString::Concat("/") + Database + aFileExt;
+ OUString key;
+ if( pExtensionPath == nullptr )
+ key = processLang( rGuard, Language ) + dbFileName;
+ else
+ key = *pExtensionPath + Language + dbFileName; // make unique, don't change language
+
+ std::pair< DatabasesTable::iterator,bool > aPair =
+ m_aDatabases.emplace( key, nullptr);
+
+ DatabasesTable::iterator it = aPair.first;
+
+ if( aPair.second && ! it->second )
+ {
+ std::unique_ptr<helpdatafileproxy::Hdf> pHdf;
+
+ OUString fileURL;
+ if( pExtensionPath )
+ fileURL = expandURL(rGuard, *pExtensionPath) + Language + dbFileName;
+ else
+ fileURL = m_aInstallDirectory + key;
+
+ OUString fileNameHDFHelp( fileURL );
+ //Extensions always use the new format
+ if( pExtensionPath != nullptr )
+ fileNameHDFHelp += "_";
+ //SimpleFileAccess takes file URLs as arguments!!! Using filenames works accidentally but
+ //fails for example when using long path names on Windows (starting with \\?\)
+ if( m_xSFA->exists( fileNameHDFHelp ) )
+ {
+ pHdf.reset(new helpdatafileproxy::Hdf( fileNameHDFHelp, m_xSFA ));
+ }
+
+ it->second = std::move(pHdf);
+ }
+
+ return it->second.get();
+}
+
+Reference< XCollator >
+Databases::getCollator(std::unique_lock<std::mutex>&, const OUString& Language)
+{
+ OUString key = Language;
+
+ CollatorTable::iterator it =
+ m_aCollatorTable.emplace( key, Reference< XCollator >() ).first;
+
+ if( ! it->second.is() )
+ {
+ it->second = Collator::create(m_xContext);
+ LanguageTag aLanguageTag( Language);
+ OUString countryStr = aLanguageTag.getCountry();
+ if( countryStr.isEmpty() )
+ {
+ const OUString langStr = aLanguageTag.getLanguage();
+ if( langStr == "de" )
+ countryStr = "DE";
+ else if( langStr == "en" )
+ countryStr = "US";
+ else if( langStr == "es" )
+ countryStr = "ES";
+ else if( langStr == "it" )
+ countryStr = "IT";
+ else if( langStr == "fr" )
+ countryStr = "FR";
+ else if( langStr == "sv" )
+ countryStr = "SE";
+ else if( langStr == "ja" )
+ countryStr = "JP";
+ else if( langStr == "ko" )
+ countryStr = "KR";
+
+ // XXX NOTE: there are no complex language tags involved in those
+ // "add country" cases, only because of this we can use this
+ // simplified construction.
+ if (!countryStr.isEmpty())
+ aLanguageTag.reset( langStr + "-" + countryStr);
+ }
+ it->second->loadDefaultCollator( aLanguageTag.getLocale(), 0);
+ }
+
+ return it->second;
+}
+
+namespace chelp {
+
+ struct KeywordElementComparator
+ {
+ explicit KeywordElementComparator( const Reference< XCollator >& xCollator )
+ : m_xCollator( xCollator )
+ { }
+
+ bool operator()( const KeywordInfo::KeywordElement& la,
+ const KeywordInfo::KeywordElement& ra ) const
+ {
+ const OUString& l = la.key;
+ const OUString& r = ra.key;
+
+ bool ret;
+
+ if( m_xCollator.is() )
+ {
+ sal_Int32 l1 = l.indexOf( ';' );
+ sal_Int32 l3 = ( l1 == -1 ? l.getLength() : l1 );
+
+ sal_Int32 r1 = r.indexOf( ';' );
+ sal_Int32 r3 = ( r1 == -1 ? r.getLength() : r1 );
+
+ sal_Int32 c1 = m_xCollator->compareSubstring( l,0,l3,r,0,r3 );
+
+ if( c1 == +1 )
+ ret = false;
+ else if( c1 == 0 )
+ {
+ sal_Int32 l2 = l.getLength() - l1 - 1;
+ sal_Int32 r2 = r.getLength() - r1 - 1;
+ ret = ( m_xCollator->compareSubstring( l,1+l1,l2,r,1+r1,r2 ) < 0 );
+ }
+ else
+ ret = true;
+ }
+ else
+ ret = l < r;
+
+ return ret;
+ }
+
+ Reference< XCollator > m_xCollator;
+ }; // end struct KeywordElementComparator
+
+}
+
+KeywordInfo::KeywordElement::KeywordElement( Databases const *pDatabases,
+ helpdatafileproxy::Hdf* pHdf,
+ OUString ky,
+ std::u16string_view data )
+ : key(std::move( ky ))
+{
+ pDatabases->replaceName( key );
+ init( pDatabases,pHdf,data );
+}
+
+void KeywordInfo::KeywordElement::init( Databases const *pDatabases,helpdatafileproxy::Hdf* pHdf, std::u16string_view ids )
+{
+ std::vector< OUString > id,anchor;
+ size_t idx = std::u16string_view::npos;
+ size_t k = 0;
+ for (;;)
+ {
+ idx = ids.find( ';', k );
+ if( idx == std::u16string_view::npos )
+ break;
+ size_t h = ids.find( '#', k );
+ if( h == std::u16string_view::npos || h < idx )
+ {
+ // found an anchor
+ id.push_back( OUString(ids.substr( k, h-k )) );
+ anchor.push_back( OUString(ids.substr( h+1, idx-h-1 )) );
+ }
+ else
+ {
+ id.push_back( OUString(ids.substr( k, idx-k )) );
+ anchor.emplace_back( );
+ }
+ k = ++idx;
+ }
+
+ listId.realloc( id.size() );
+ auto plistId = listId.getArray();
+ listAnchor.realloc( id.size() );
+ auto plistAnchor = listAnchor.getArray();
+ listTitle.realloc( id.size() );
+ auto plistTitle = listTitle.getArray();
+
+ for( size_t i = 0; i < id.size(); ++i )
+ {
+ plistId[i] = id[i];
+ plistAnchor[i] = anchor[i];
+
+ helpdatafileproxy::HDFData aHDFData;
+ const char* pData = nullptr;
+
+ if( pHdf )
+ {
+ OString idi = OUStringToOString( id[i], RTL_TEXTENCODING_UTF8 );
+ bool bSuccess = pHdf->getValueForKey( idi, aHDFData );
+ if( bSuccess )
+ pData = aHDFData.getData();
+ }
+
+ DbtToStringConverter converter( pData );
+
+ OUString title = converter.getTitle();
+ pDatabases->replaceName( title );
+ plistTitle[i] = title;
+ }
+}
+
+KeywordInfo::KeywordInfo( const std::vector< KeywordElement >& aVec )
+ : listKey( aVec.size() ),
+ listId( aVec.size() ),
+ listAnchor( aVec.size() ),
+ listTitle( aVec.size() )
+{
+ auto listKeyRange = asNonConstRange(listKey);
+ auto listIdRange = asNonConstRange(listId);
+ auto listAnchorRange = asNonConstRange(listAnchor);
+ auto listTitleRange = asNonConstRange(listTitle);
+ for( size_t i = 0; i < aVec.size(); ++i )
+ {
+ listKeyRange[i] = aVec[i].key;
+ listIdRange[i] = aVec[i].listId;
+ listAnchorRange[i] = aVec[i].listAnchor;
+ listTitleRange[i] = aVec[i].listTitle;
+ }
+}
+
+bool Databases::checkModuleMatchForExtension
+ ( std::u16string_view Database, const OUString& doclist )
+{
+ bool bBelongsToDatabase = true;
+
+ // Analyse doclist string to find module assignments
+ bool bFoundAtLeastOneModule = false;
+ bool bModuleMatch = false;
+ sal_Int32 nLen = doclist.getLength();
+ sal_Int32 nLastFound = doclist.lastIndexOf( ';' );
+ if( nLastFound == -1 )
+ nLastFound = nLen;
+ const sal_Unicode* pStr = doclist.getStr();
+ sal_Int32 nFound = doclist.lastIndexOf( '_' );
+ while( nFound != -1 )
+ {
+ // Simple optimization, stop if '_' is followed by "id"
+ if( nLen - nFound > 2 )
+ {
+ if( pStr[ nFound + 1 ] == 'i' &&
+ pStr[ nFound + 2 ] == 'd' )
+ break;
+ }
+
+ OUString aModule = doclist.copy( nFound + 1, nLastFound - nFound - 1 );
+ std::vector< OUString >::iterator result = std::find( m_avModules.begin(), m_avModules.end(), aModule );
+ if( result != m_avModules.end() )
+ {
+ bFoundAtLeastOneModule = true;
+ if( Database == aModule )
+ {
+ bModuleMatch = true;
+ break;
+ }
+ }
+
+ nLastFound = nFound;
+ if( nLastFound == 0 )
+ break;
+ nFound = doclist.lastIndexOf( '_', nLastFound - 1 );
+ }
+
+ if( bFoundAtLeastOneModule && !bModuleMatch )
+ bBelongsToDatabase = false;
+
+ return bBelongsToDatabase;
+}
+
+KeywordInfo* Databases::getKeyword( const OUString& Database,
+ const OUString& Language )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ OUString key = processLang(aGuard, Language) + "/" + Database;
+
+ std::pair< KeywordInfoTable::iterator,bool > aPair =
+ m_aKeywordInfo.emplace( key,nullptr );
+
+ KeywordInfoTable::iterator it = aPair.first;
+
+ if( aPair.second && ! it->second )
+ {
+ std::vector<KeywordInfo::KeywordElement> aVector;
+
+ KeyDataBaseFileIterator aDbFileIt( m_xContext, *this, Database, Language );
+ OUString fileURL;
+ bool bExtension = false;
+ for (;;)
+ {
+ fileURL = aDbFileIt.nextDbFile(aGuard, bExtension);
+ if( fileURL.isEmpty() )
+ break;
+ OUString fileNameHDFHelp( fileURL );
+ if( bExtension )
+ fileNameHDFHelp += "_";
+ if( m_xSFA->exists( fileNameHDFHelp ) )
+ {
+ helpdatafileproxy::Hdf aHdf( fileNameHDFHelp, m_xSFA );
+ helpdatafileproxy::HDFData aKey;
+ helpdatafileproxy::HDFData aValue;
+ if( aHdf.startIteration() )
+ {
+ helpdatafileproxy::Hdf* pHdf = getHelpDataFile(aGuard, Database,Language );
+ if( pHdf != nullptr )
+ {
+ pHdf->releaseHashMap();
+ pHdf->createHashMap( true/*bOptimizeForPerformance*/ );
+ }
+
+ while( aHdf.getNextKeyAndValue( aKey, aValue ) )
+ {
+ OUString keyword( aKey.getData(), aKey.getSize(),
+ RTL_TEXTENCODING_UTF8 );
+ OUString doclist( aValue.getData(), aValue.getSize(),
+ RTL_TEXTENCODING_UTF8 );
+
+ bool bBelongsToDatabase = true;
+ if( bExtension )
+ bBelongsToDatabase = checkModuleMatchForExtension( Database, doclist );
+
+ if( !bBelongsToDatabase )
+ continue;
+
+ aVector.emplace_back( this,
+ pHdf,
+ keyword,
+ doclist );
+ }
+ aHdf.stopIteration();
+
+ if( pHdf != nullptr )
+ pHdf->releaseHashMap();
+ }
+ }
+ }
+
+ // sorting
+ Reference<XCollator> xCollator = getCollator(aGuard, Language);
+ KeywordElementComparator aComparator( xCollator );
+ std::sort(aVector.begin(),aVector.end(),aComparator);
+
+ it->second.reset(new KeywordInfo( aVector ));
+ }
+
+ return it->second.get();
+}
+
+Reference< XHierarchicalNameAccess > Databases::jarFile(
+ std::unique_lock<std::mutex>& rGuard, std::u16string_view jar,
+ const OUString& Language )
+{
+ if( jar.empty() || Language.isEmpty() )
+ {
+ return Reference< XHierarchicalNameAccess >( nullptr );
+ }
+
+ OUString key = processLang(rGuard, Language) + "/" + jar;
+
+ ZipFileTable::iterator it =
+ m_aZipFileTable.emplace( key,Reference< XHierarchicalNameAccess >(nullptr) ).first;
+
+ if( ! it->second.is() )
+ {
+ try
+ {
+ OUString zipFile;
+ // Extension jar file? Search for ?
+ size_t nQuestionMark1 = jar.find( '?' );
+ size_t nQuestionMark2 = jar.rfind( '?' );
+ if( nQuestionMark1 != std::u16string_view::npos && nQuestionMark2 != std::u16string_view::npos && nQuestionMark1 != nQuestionMark2 )
+ {
+ std::u16string_view aExtensionPath = jar.substr( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
+ std::u16string_view aPureJar = jar.substr( nQuestionMark2 + 1 );
+
+ zipFile = expandURL(rGuard, OUString::Concat(aExtensionPath) + "/" + aPureJar);
+ }
+ else
+ {
+ zipFile = m_aInstallDirectory + key;
+ }
+
+ Sequence< Any > aArguments( 2 );
+ auto pArguments = aArguments.getArray();
+
+ rtl::Reference<XInputStream_impl> p(new XInputStream_impl( zipFile ));
+ if( p->CtorSuccess() )
+ {
+ pArguments[ 0 ] <<= Reference< XInputStream >( p );
+ }
+ else
+ {
+ p.clear();
+ pArguments[ 0 ] <<= zipFile;
+ }
+
+ // let ZipPackage be used ( no manifest.xml is required )
+ beans::NamedValue aArg;
+ aArg.Name = "StorageFormat";
+ aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
+ pArguments[ 1 ] <<= aArg;
+
+ Reference< XInterface > xIfc
+ = m_xSMgr->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.comp.ZipPackage",
+ aArguments, m_xContext );
+
+ if ( xIfc.is() )
+ {
+ it->second.set( xIfc, UNO_QUERY );
+
+ OSL_ENSURE( it->second.is(),
+ "ContentProvider::createPackage - "
+ "Got no hierarchical name access!" );
+
+ }
+ }
+ catch ( RuntimeException & )
+ {
+ }
+ catch ( Exception & )
+ {
+ }
+ }
+
+ return it->second;
+}
+
+Reference< XHierarchicalNameAccess > Databases::findJarFileForPath
+ ( const OUString& jar, const OUString& Language,
+ const OUString& path, OUString* o_pExtensionPath,
+ OUString* o_pExtensionRegistryPath )
+{
+ Reference< XHierarchicalNameAccess > xNA;
+ if( jar.isEmpty() || Language.isEmpty() )
+ {
+ return xNA;
+ }
+
+ ::std::unique_lock aGuard(m_aMutex);
+
+ JarFileIterator aJarFileIt( m_xContext, *this, jar, Language );
+ Reference< XHierarchicalNameAccess > xTestNA;
+ Reference< deployment::XPackage > xParentPackageBundle;
+ for (;;)
+ {
+ xTestNA = aJarFileIt.nextJarFile(aGuard, xParentPackageBundle, o_pExtensionPath, o_pExtensionRegistryPath);
+ if( !xTestNA.is() )
+ break;
+ if( xTestNA.is() && xTestNA->hasByHierarchicalName( path ) )
+ {
+ bool bSuccess = true;
+ if( xParentPackageBundle.is() )
+ {
+ OUString aIdentifierInPath;
+ sal_Int32 nFindSlash = path.indexOf( '/' );
+ if( nFindSlash != -1 )
+ aIdentifierInPath = path.copy( 0, nFindSlash );
+
+ beans::Optional<OUString> aIdentifierOptional = xParentPackageBundle->getIdentifier();
+ if( !aIdentifierInPath.isEmpty() && aIdentifierOptional.IsPresent )
+ {
+ OUString aUnencodedIdentifier = aIdentifierOptional.Value;
+ OUString aIdentifier = rtl::Uri::encode( aUnencodedIdentifier,
+ rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 );
+
+ if( aIdentifierInPath != aIdentifier )
+ {
+ // path does not start with extension identifier -> ignore
+ bSuccess = false;
+ }
+ }
+ else
+ {
+ // No identifier -> ignore
+ bSuccess = false;
+ }
+ }
+
+ if( bSuccess )
+ {
+ xNA = xTestNA;
+ break;
+ }
+ }
+ }
+
+ return xNA;
+}
+
+void Databases::changeCSS(const OUString& newStyleSheet)
+{
+ m_aCSS = newStyleSheet.toAsciiLowerCase();
+ m_vCustomCSSDoc.clear();
+}
+
+void Databases::cascadingStylesheet( const OUString& Language,
+ OStringBuffer& buffer )
+{
+ if( m_vCustomCSSDoc.empty() )
+ {
+ int retry = 2;
+ bool error = true;
+ OUString fileURL;
+
+ bool bHighContrastMode = false;
+ OUString aCSS( m_aCSS );
+ if ( aCSS == "default" )
+ {
+ // #i50760: "default" needs to adapt HC mode
+ uno::Reference< awt::XToolkit2 > xToolkit =
+ awt::Toolkit::create( ::comphelper::getProcessComponentContext() );
+ uno::Reference< awt::XTopWindow > xTopWindow = xToolkit->getActiveTopWindow();
+ if ( xTopWindow.is() )
+ {
+ uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( xTopWindow, uno::UNO_QUERY );
+ if ( xVclWindowPeer.is() )
+ {
+ uno::Any aHCMode = xVclWindowPeer->getProperty( "HighContrastMode" );
+ if ( ( aHCMode >>= bHighContrastMode ) && bHighContrastMode )
+ {
+ aCSS = "highcontrastblack";
+ #ifdef _WIN32
+ HKEY hKey = nullptr;
+ LONG lResult = RegOpenKeyExW( HKEY_CURRENT_USER, L"Control Panel\\Accessibility\\HighContrast", 0, KEY_QUERY_VALUE, &hKey );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer[1024];
+ DWORD nSize = sizeof( szBuffer );
+ lResult = RegQueryValueExW( hKey, L"High Contrast Scheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize );
+ if ( ERROR_SUCCESS == lResult && nSize > 0 )
+ {
+ szBuffer[nSize] = '\0';
+ if ( wcscmp( szBuffer, L"High Contrast #1" ) == 0 )
+ aCSS = "highcontrast1";
+ if ( wcscmp( szBuffer, L"High Contrast #2" ) == 0 )
+ aCSS = "highcontrast2";
+ if ( wcscmp( szBuffer, L"High Contrast White" ) == 0 )
+ aCSS = "highcontrastwhite";
+ }
+ RegCloseKey( hKey );
+ }
+ #endif
+ }
+ }
+ }
+ }
+
+ while( error && retry )
+ {
+
+ if( retry == 2 )
+ fileURL =
+ getInstallPathAsURL() +
+ processLang( Language ) +
+ "/" +
+ aCSS +
+ ".css";
+ else if( retry == 1 )
+ fileURL =
+ getInstallPathAsURL() +
+ aCSS +
+ ".css";
+
+ osl::DirectoryItem aDirItem;
+ osl::File aFile( fileURL );
+ osl::FileStatus aStatus( osl_FileStatus_Mask_FileSize );
+
+ if( osl::FileBase::E_None == osl::DirectoryItem::get( fileURL,aDirItem ) &&
+ osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) &&
+ osl::FileBase::E_None == aDirItem.getFileStatus( aStatus ) )
+ {
+ sal_uInt64 nSize;
+ aFile.getSize( nSize );
+ m_vCustomCSSDoc.resize( nSize + 1);
+ m_vCustomCSSDoc[nSize] = 0;
+ sal_uInt64 a = nSize,b = nSize;
+ aFile.read( m_vCustomCSSDoc.data(), a, b );
+ aFile.close();
+ error = false;
+ }
+
+ --retry;
+ if ( !retry && error && bHighContrastMode )
+ {
+ // fall back to default css
+ aCSS = "default";
+ retry = 2;
+ bHighContrastMode = false;
+ }
+ }
+
+ if( error )
+ {
+ m_vCustomCSSDoc.clear();
+ }
+ }
+
+ if (!m_vCustomCSSDoc.empty())
+ buffer.append( m_vCustomCSSDoc.data(), m_vCustomCSSDoc.size() - 1 );
+}
+
+void Databases::setActiveText( const OUString& Module,
+ const OUString& Language,
+ std::u16string_view Id,
+ OStringBuffer& buffer )
+{
+ DataBaseIterator aDbIt( m_xContext, *this, Module, Language, true );
+
+ // #i84550 Cache information about failed ids
+ OString id = OUStringToOString( Id, RTL_TEXTENCODING_UTF8 );
+ EmptyActiveTextSet::iterator it = m_aEmptyActiveTextSet.find( id );
+ bool bFoundAsEmpty = ( it != m_aEmptyActiveTextSet.end() );
+ helpdatafileproxy::HDFData aHDFData;
+
+ int nSize = 0;
+ const char* pData = nullptr;
+
+ bool bSuccess = false;
+ if( !bFoundAsEmpty )
+ {
+ while( !bSuccess )
+ {
+ helpdatafileproxy::Hdf* pHdf = aDbIt.nextHdf();
+ if( !pHdf )
+ break;
+ bSuccess = pHdf->getValueForKey( id, aHDFData );
+ nSize = aHDFData.getSize();
+ pData = aHDFData.getData();
+ }
+ }
+
+ if( bSuccess )
+ {
+ // ensure existence of tmp after for
+ OString tmp;
+ for( int i = 0; i < nSize; ++i )
+ if( pData[i] == '%' || pData[i] == '$' )
+ {
+ // need of replacement
+ OUString temp( pData, nSize, RTL_TEXTENCODING_UTF8 );
+ replaceName( temp );
+ tmp = OString( temp.getStr(),
+ temp.getLength(),
+ RTL_TEXTENCODING_UTF8 );
+ nSize = tmp.getLength();
+ pData = tmp.getStr();
+ break;
+ }
+
+ buffer.append( pData, nSize );
+ }
+ else
+ {
+ if( !bFoundAsEmpty )
+ m_aEmptyActiveTextSet.insert( id );
+ }
+}
+
+void Databases::setInstallPath( const OUString& aInstDir )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ osl::FileBase::getFileURLFromSystemPath( aInstDir,m_aInstallDirectory );
+ //TODO: check returned error code
+
+ if( !m_aInstallDirectory.endsWith( "/" ) )
+ m_aInstallDirectory += "/";
+}
+
+
+ExtensionHelpExistenceMap ExtensionIteratorBase::aHelpExistenceMap;
+
+ExtensionIteratorBase::ExtensionIteratorBase( Reference< XComponentContext > const & xContext,
+ Databases& rDatabases, OUString aInitialModule, OUString aLanguage )
+ : m_xContext( xContext )
+ , m_rDatabases( rDatabases )
+ , m_eState( IteratorState::InitialModule )
+ , m_aInitialModule(std::move( aInitialModule ))
+ , m_aLanguage(std::move( aLanguage ))
+{
+ assert( m_xContext.is() );
+ init();
+}
+
+ExtensionIteratorBase::ExtensionIteratorBase( Databases& rDatabases,
+ OUString aInitialModule, OUString aLanguage )
+ : m_xContext( comphelper::getProcessComponentContext() )
+ , m_rDatabases( rDatabases )
+ , m_eState( IteratorState::InitialModule )
+ , m_aInitialModule(std::move( aInitialModule ))
+ , m_aLanguage(std::move( aLanguage ))
+{
+ init();
+}
+
+void ExtensionIteratorBase::init()
+{
+ m_xSFA = ucb::SimpleFileAccess::create(m_xContext);
+
+ m_bUserPackagesLoaded = false;
+ m_bSharedPackagesLoaded = false;
+ m_bBundledPackagesLoaded = false;
+ m_iUserPackage = 0;
+ m_iSharedPackage = 0;
+ m_iBundledPackage = 0;
+}
+
+Reference< deployment::XPackage > ExtensionIteratorBase::implGetHelpPackageFromPackage
+ ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ o_xParentPackageBundle.clear();
+
+ Reference< deployment::XPackage > xHelpPackage;
+ if( !xPackage.is() )
+ return xHelpPackage;
+
+ // #i84550 Cache information about help content in extension
+ OUString aExtensionPath = xPackage->getURL();
+ ExtensionHelpExistenceMap::iterator it = aHelpExistenceMap.find( aExtensionPath );
+ bool bFound = ( it != aHelpExistenceMap.end() );
+ bool bHasHelp = bFound && it->second;
+ if( bFound && !bHasHelp )
+ return xHelpPackage;
+
+ // Check if parent package is registered
+ beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered
+ ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) );
+ bool bRegistered = false;
+ if( option.IsPresent )
+ {
+ beans::Ambiguous<sal_Bool> const & reg = option.Value;
+ if( !reg.IsAmbiguous && reg.Value )
+ bRegistered = true;
+ }
+ if( bRegistered )
+ {
+ OUString aHelpMediaType( "application/vnd.sun.star.help" );
+ if( xPackage->isBundle() )
+ {
+ const Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle
+ ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() );
+ auto pSubPkg = std::find_if(aPkgSeq.begin(), aPkgSeq.end(),
+ [&aHelpMediaType](const Reference< deployment::XPackage >& xSubPkg) {
+ const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType();
+ OUString aMediaType = xPackageTypeInfo->getMediaType();
+ return aMediaType == aHelpMediaType;
+ });
+ if (pSubPkg != aPkgSeq.end())
+ {
+ xHelpPackage = *pSubPkg;
+ o_xParentPackageBundle = xPackage;
+ }
+ }
+ else
+ {
+ const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
+ OUString aMediaType = xPackageTypeInfo->getMediaType();
+ if( aMediaType == aHelpMediaType )
+ xHelpPackage = xPackage;
+ }
+ }
+
+ if( !bFound )
+ aHelpExistenceMap[ aExtensionPath ] = xHelpPackage.is();
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextUserHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bUserPackagesLoaded )
+ {
+ Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
+ m_aUserPackagesSeq = xExtensionManager->getDeployedExtensions
+ ( "user", Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
+ m_bUserPackagesLoaded = true;
+ }
+
+ if( m_iUserPackage == m_aUserPackagesSeq.getLength() )
+ {
+ m_eState = IteratorState::SharedExtensions; // Later: SHARED_MODULE
+ }
+ else
+ {
+ const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray();
+ Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ];
+ OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextUserHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextSharedHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bSharedPackagesLoaded )
+ {
+ Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
+ m_aSharedPackagesSeq = xExtensionManager->getDeployedExtensions
+ ( "shared", Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
+ m_bSharedPackagesLoaded = true;
+ }
+
+ if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() )
+ {
+ m_eState = IteratorState::BundledExtensions;
+ }
+ else
+ {
+ const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray();
+ Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ];
+ OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextSharedHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextBundledHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bBundledPackagesLoaded )
+ {
+ Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
+ m_aBundledPackagesSeq = xExtensionManager->getDeployedExtensions
+ ( "bundled", Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
+ m_bBundledPackagesLoaded = true;
+ }
+
+ if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() )
+ {
+ m_eState = IteratorState::EndReached;
+ }
+ else
+ {
+ const Reference< deployment::XPackage >* pBundledPackages =
+ m_aBundledPackagesSeq.getConstArray();
+ Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ];
+ OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextBundledHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+OUString ExtensionIteratorBase::implGetFileFromPackage(
+ std::unique_lock<std::mutex> & rGuard,
+ std::u16string_view rFileExtension, const Reference< deployment::XPackage >& xPackage )
+{
+ // No extension -> search for pure language folder
+ bool bLangFolderOnly = rFileExtension.empty();
+
+ OUString aFile;
+ OUString aLanguage = m_aLanguage;
+ for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
+ {
+ OUString aStr = xPackage->getRegistrationDataURL().Value + "/" + aLanguage;
+ if( !bLangFolderOnly )
+ {
+ aStr += OUString::Concat("/help") + rFileExtension;
+ }
+
+ aFile = m_rDatabases.expandURL(rGuard, aStr);
+ if( iPass == 0 )
+ {
+ if( m_xSFA->exists( aFile ) )
+ break;
+
+ ::std::vector< OUString > av;
+ implGetLanguageVectorFromPackage( av, xPackage );
+ ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage );
+ if( pFound != av.end() )
+ aLanguage = *pFound;
+ }
+ }
+ return aFile;
+}
+
+
+OUString ExtensionIteratorBase::implGetFileFromPackage(
+ std::u16string_view rFileExtension, const Reference< deployment::XPackage >& xPackage )
+{
+ // No extension -> search for pure language folder
+ bool bLangFolderOnly = rFileExtension.empty();
+
+ OUString aFile;
+ OUString aLanguage = m_aLanguage;
+ for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
+ {
+ OUString aStr = xPackage->getRegistrationDataURL().Value + "/" + aLanguage;
+ if( !bLangFolderOnly )
+ {
+ aStr += OUString::Concat("/help") + rFileExtension;
+ }
+
+ aFile = m_rDatabases.expandURL( aStr );
+ if( iPass == 0 )
+ {
+ if( m_xSFA->exists( aFile ) )
+ break;
+
+ ::std::vector< OUString > av;
+ implGetLanguageVectorFromPackage( av, xPackage );
+ ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage );
+ if( pFound != av.end() )
+ aLanguage = *pFound;
+ }
+ }
+ return aFile;
+}
+
+static bool isLetter( sal_Unicode c )
+{
+ return rtl::isAsciiAlpha(c);
+}
+
+void ExtensionIteratorBase::implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage )
+{
+ rv.clear();
+ OUString aExtensionPath = xPackage->getURL();
+ const Sequence< OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true );
+
+ for( const OUString& aEntry : aEntrySeq )
+ {
+ if( m_xSFA->isFolder( aEntry ) )
+ {
+ sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ {
+ OUString aPureEntry = aEntry.copy( nLastSlash + 1 );
+
+ // Check language scheme
+ int nLen = aPureEntry.getLength();
+ const sal_Unicode* pc = aPureEntry.getStr();
+ bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) );
+ bool bIsLanguage = bStartCanBeLanguage &&
+ ( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) );
+ if( bIsLanguage )
+ rv.push_back( aPureEntry );
+ }
+ }
+ }
+}
+
+
+helpdatafileproxy::Hdf* DataBaseIterator::nextHdf( OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath )
+{
+ helpdatafileproxy::Hdf* pRetHdf = nullptr;
+
+ while( !pRetHdf && m_eState != IteratorState::EndReached )
+ {
+ switch( m_eState )
+ {
+ case IteratorState::InitialModule:
+ pRetHdf = m_rDatabases.getHelpDataFile( m_aInitialModule, m_aLanguage, m_bHelpText );
+ m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE
+ break;
+
+ // Later:
+ //case SHARED_MODULE
+
+
+ case IteratorState::UserExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+ pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
+ break;
+ }
+
+ case IteratorState::SharedExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
+ break;
+ }
+
+ case IteratorState::BundledExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
+ break;
+ }
+
+ case IteratorState::EndReached:
+ OSL_FAIL( "DataBaseIterator::nextDb(): Invalid case IteratorState::EndReached" );
+ break;
+ }
+ }
+
+ return pRetHdf;
+}
+
+helpdatafileproxy::Hdf* DataBaseIterator::implGetHdfFromPackage( const Reference< deployment::XPackage >& xPackage,
+ OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath )
+{
+
+ beans::Optional< OUString> optRegData;
+ try
+ {
+ optRegData = xPackage->getRegistrationDataURL();
+ }
+ catch ( deployment::ExtensionRemovedException&)
+ {
+ return nullptr;
+ }
+
+ helpdatafileproxy::Hdf* pRetHdf = nullptr;
+ if (optRegData.IsPresent && !optRegData.Value.isEmpty())
+ {
+ OUString aRegDataUrl = optRegData.Value + "/";
+
+ OUString aHelpFilesBaseName("help");
+
+ OUString aUsedLanguage = m_aLanguage;
+ pRetHdf = m_rDatabases.getHelpDataFile(
+ aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl);
+
+ // Language fallback
+ if( !pRetHdf )
+ {
+ ::std::vector< OUString > av;
+ implGetLanguageVectorFromPackage( av, xPackage );
+ ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage );
+ if( pFound != av.end() )
+ {
+ aUsedLanguage = *pFound;
+ pRetHdf = m_rDatabases.getHelpDataFile(
+ aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl);
+ }
+ }
+
+ if( o_pExtensionPath )
+ *o_pExtensionPath = aRegDataUrl + aUsedLanguage;
+
+ if( o_pExtensionRegistryPath )
+ *o_pExtensionRegistryPath = xPackage->getURL() + "/" + aUsedLanguage;
+ }
+
+ return pRetHdf;
+}
+
+
+//returns a file URL
+OUString KeyDataBaseFileIterator::nextDbFile(std::unique_lock<std::mutex>& rGuard, bool& o_rbExtension)
+{
+ OUString aRetFile;
+
+ while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached )
+ {
+ switch( m_eState )
+ {
+ case IteratorState::InitialModule:
+ aRetFile = m_rDatabases.getInstallPathAsURL(rGuard) +
+ m_rDatabases.processLang(rGuard, m_aLanguage) +
+ "/" +
+ m_aInitialModule + ".key";
+
+ o_rbExtension = false;
+
+ m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE
+ break;
+
+ // Later:
+ //case SHARED_MODULE
+
+
+ case IteratorState::UserExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage);
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::SharedExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage);
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::BundledExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage);
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::EndReached:
+ OSL_FAIL( "DataBaseIterator::nextDbFile(): Invalid case IteratorState::EndReached" );
+ break;
+ }
+ }
+
+ return aRetFile;
+}
+
+//Returns a file URL, that does not contain macros
+OUString KeyDataBaseFileIterator::implGetDbFileFromPackage(
+ std::unique_lock<std::mutex>& rGuard,
+ const Reference<deployment::XPackage>& xPackage)
+{
+ OUString aExpandedURL =
+ implGetFileFromPackage(rGuard, u".key", xPackage);
+
+ return aExpandedURL;
+}
+
+
+Reference<XHierarchicalNameAccess> JarFileIterator::nextJarFile(
+ std::unique_lock<std::mutex>& rGuard,
+ Reference< deployment::XPackage >& o_xParentPackageBundle,
+ OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath )
+{
+ Reference< XHierarchicalNameAccess > xNA;
+
+ while( !xNA.is() && m_eState != IteratorState::EndReached )
+ {
+ switch( m_eState )
+ {
+ case IteratorState::InitialModule:
+ xNA = m_rDatabases.jarFile(rGuard, m_aInitialModule, m_aLanguage);
+ m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE
+ break;
+
+ // Later:
+ //case SHARED_MODULE
+
+
+ case IteratorState::UserExtensions:
+ {
+ Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( o_xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath);
+ break;
+ }
+
+ case IteratorState::SharedExtensions:
+ {
+ Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( o_xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath);
+ break;
+ }
+
+ case IteratorState::BundledExtensions:
+ {
+ Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( o_xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath);
+ break;
+ }
+
+ case IteratorState::EndReached:
+ OSL_FAIL( "JarFileIterator::nextJarFile(): Invalid case IteratorState::EndReached" );
+ break;
+ }
+ }
+
+ return xNA;
+}
+
+Reference< XHierarchicalNameAccess > JarFileIterator::implGetJarFromPackage(
+ std::unique_lock<std::mutex>& rGuard,
+ const Reference<deployment::XPackage>& xPackage, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath)
+{
+ Reference< XHierarchicalNameAccess > xNA;
+
+ OUString zipFile =
+ implGetFileFromPackage(rGuard, u".jar", xPackage);
+
+ try
+ {
+ Sequence< Any > aArguments{
+ Any(zipFile),
+ // let ZipPackage be used ( no manifest.xml is required )
+ Any(comphelper::makePropertyValue("StorageFormat",
+ ZIP_STORAGE_FORMAT_STRING))
+ };
+
+ Reference< XMultiComponentFactory >xSMgr = m_xContext->getServiceManager();
+ Reference< XInterface > xIfc
+ = xSMgr->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.comp.ZipPackage",
+ aArguments, m_xContext );
+
+ if ( xIfc.is() )
+ {
+ xNA.set( xIfc, UNO_QUERY );
+
+ OSL_ENSURE( xNA.is(),
+ "JarFileIterator::implGetJarFromPackage() - "
+ "Got no hierarchical name access!" );
+ }
+ }
+ catch ( RuntimeException & )
+ {}
+ catch ( Exception & )
+ {}
+
+ if( xNA.is() && o_pExtensionPath != nullptr )
+ {
+ // Extract path including language from file name
+ sal_Int32 nLastSlash = zipFile.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ *o_pExtensionPath = zipFile.copy( 0, nLastSlash );
+
+ if( o_pExtensionRegistryPath != nullptr )
+ {
+ OUString& rPath = *o_pExtensionPath;
+ sal_Int32 nLastSlashInPath = rPath.lastIndexOf( '/', rPath.getLength() - 1 );
+
+ *o_pExtensionRegistryPath = xPackage->getURL();
+ *o_pExtensionRegistryPath += rPath.subView( nLastSlashInPath);
+ }
+ }
+
+ return xNA;
+}
+
+
+OUString IndexFolderIterator::nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary )
+{
+ OUString aIndexFolder;
+
+ while( aIndexFolder.isEmpty() && m_eState != IteratorState::EndReached )
+ {
+ switch( m_eState )
+ {
+ case IteratorState::InitialModule:
+ aIndexFolder = m_rDatabases.getInstallPathAsURL()
+ + m_rDatabases.processLang(m_aLanguage) + "/"
+ + m_aInitialModule + ".idxl";
+
+ o_rbTemporary = false;
+ o_rbExtension = false;
+
+ m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE
+ break;
+
+ // Later:
+ //case SHARED_MODULE
+
+
+ case IteratorState::UserExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::SharedExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::BundledExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
+ o_rbExtension = true;
+ break;
+ }
+
+ case IteratorState::EndReached:
+ OSL_FAIL( "IndexFolderIterator::nextIndexFolder(): Invalid case IteratorState::EndReached" );
+ break;
+ }
+ }
+
+ return aIndexFolder;
+}
+
+OUString IndexFolderIterator::implGetIndexFolderFromPackage( bool& o_rbTemporary, const Reference< deployment::XPackage >& xPackage )
+{
+ OUString aIndexFolder =
+ implGetFileFromPackage( u".idxl", xPackage );
+
+ o_rbTemporary = false;
+ if( !m_xSFA->isFolder( aIndexFolder ) )
+ {
+ // i98680: Missing index? Try to generate now
+ OUString aLangURL = implGetFileFromPackage( std::u16string_view(), xPackage );
+ if( m_xSFA->isFolder( aLangURL ) )
+ {
+ // Test write access (shared extension may be read only)
+ bool bIsWriteAccess = false;
+ try
+ {
+ OUString aCreateTestFolder = aLangURL + "CreateTestFolder";
+ m_xSFA->createFolder( aCreateTestFolder );
+ if( m_xSFA->isFolder( aCreateTestFolder ) )
+ bIsWriteAccess = true;
+
+ m_xSFA->kill( aCreateTestFolder );
+ }
+ catch (const Exception &)
+ {
+ }
+
+ // TEST
+ //bIsWriteAccess = false;
+
+ try
+ {
+ OUString aLang;
+ sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ aLang = aLangURL.copy( nLastSlash + 1 );
+ else
+ aLang = "en";
+
+ OUString aZipDir = aLangURL;
+ if( !bIsWriteAccess )
+ {
+ OUString aTempFileURL;
+ ::osl::FileBase::RC eErr = ::osl::File::createTempFile( nullptr, nullptr, &aTempFileURL );
+ if( eErr == ::osl::FileBase::E_None )
+ {
+ try
+ {
+ m_xSFA->kill( aTempFileURL );
+ }
+ catch (const Exception &)
+ {
+ }
+ m_xSFA->createFolder( aTempFileURL );
+
+ aZipDir = aTempFileURL;
+ o_rbTemporary = true;
+ }
+ }
+
+ HelpIndexer aIndexer(aLang, "help", aLangURL, aZipDir);
+ aIndexer.indexDocuments();
+
+ if( bIsWriteAccess )
+ aIndexFolder = implGetFileFromPackage( u".idxl", xPackage );
+ else
+ aIndexFolder = aZipDir + "/help.idxl";
+ }
+ catch (const Exception &)
+ {
+ }
+ }
+ }
+
+ return aIndexFolder;
+}
+
+void IndexFolderIterator::deleteTempIndexFolder( std::u16string_view aIndexFolder )
+{
+ size_t nLastSlash = aIndexFolder.rfind( '/' );
+ if( nLastSlash != std::u16string_view::npos )
+ {
+ OUString aTmpFolder( aIndexFolder.substr( 0, nLastSlash ) );
+ try
+ {
+ m_xSFA->kill( aTmpFolder );
+ }
+ catch (const Exception &)
+ {
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/databases.hxx b/xmlhelp/source/cxxhelp/provider/databases.hxx
new file mode 100644
index 0000000000..448d1f92cd
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/databases.hxx
@@ -0,0 +1,449 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+#include <mutex>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <rtl/strbuf.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/i18n/XCollator.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+
+// Forward declaration
+
+namespace helpdatafileproxy {
+
+ class Hdf;
+
+}
+
+namespace chelp {
+
+ class Databases;
+ class URLParameter;
+
+ class StaticModuleInformation
+ {
+ private:
+
+ OUString m_aStartId;
+ OUString m_aProgramSwitch;
+ OUString m_aTitle;
+ int m_nOrder;
+
+ public:
+
+ StaticModuleInformation( OUString aTitle,
+ OUString aStartId,
+ OUString aProgramSwitch,
+ std::u16string_view aOrder )
+ : m_aStartId(std::move( aStartId )),
+ m_aProgramSwitch(std::move( aProgramSwitch )),
+ m_aTitle(std::move( aTitle )),
+ m_nOrder( o3tl::toInt32(aOrder) )
+ {
+ }
+
+ const OUString& get_title() const { return m_aTitle; }
+ const OUString& get_id() const { return m_aStartId; }
+ const OUString& get_program() const { return m_aProgramSwitch; }
+ int get_order() const { return m_nOrder; }
+ }; // end class StaticModuleInformation
+
+ class KeywordInfo
+ {
+ public:
+
+ class KeywordElement
+ {
+ friend struct KeywordElementComparator;
+ friend class KeywordInfo;
+
+ public:
+
+ KeywordElement( Databases const * pDatabases,
+ helpdatafileproxy::Hdf* pHdf,
+ OUString key,
+ std::u16string_view ids );
+
+ private:
+
+ OUString key;
+ css::uno::Sequence< OUString > listId;
+ css::uno::Sequence< OUString > listAnchor;
+ css::uno::Sequence< OUString > listTitle;
+
+ void init( Databases const *pDatabases,helpdatafileproxy::Hdf* pHdf, std::u16string_view ids );
+ };
+
+ explicit KeywordInfo( const std::vector< KeywordElement >& aVector );
+
+ css::uno::Sequence< OUString >&
+ getKeywordList() { return listKey; }
+
+ css::uno::Sequence< css::uno::Sequence< OUString > >&
+ getIdList() { return listId; }
+
+ css::uno::Sequence< css::uno::Sequence< OUString > >&
+ getAnchorList() { return listAnchor; }
+
+ css::uno::Sequence< css::uno::Sequence< OUString > >&
+ getTitleList() { return listTitle; }
+
+ private:
+
+ css::uno::Sequence< OUString > listKey;
+ css::uno::Sequence< css::uno::Sequence< OUString > > listId,listAnchor,listTitle;
+ }; // end class KeywordInfo
+
+ class Databases
+ {
+ public:
+
+ /**
+ * Input is the installdirectory in system dependent notation
+ */
+
+ Databases( bool showBasic,
+ const OUString& instPath,
+ const OUString& productName,
+ const OUString& productVersion,
+ const OUString& styleSheet,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+ ~Databases();
+
+ OString getImageTheme() const;
+
+ OUString getInstallPathAsURL();
+ OUString getInstallPathAsURL(std::unique_lock<std::mutex>& rGuard);
+
+ const std::vector< OUString >& getModuleList( const OUString& Language );
+
+ StaticModuleInformation* getStaticInformationForModule( std::u16string_view Module,
+ const OUString& Language );
+
+ bool checkModuleMatchForExtension( std::u16string_view Database, const OUString& doclist );
+ KeywordInfo* getKeyword( const OUString& Module,
+ const OUString& Language );
+
+ helpdatafileproxy::Hdf* getHelpDataFile( std::u16string_view Module,
+ const OUString& Language, bool helpText = false,
+ const OUString* pExtensionPath = nullptr );
+ helpdatafileproxy::Hdf* getHelpDataFile(std::unique_lock<std::mutex>& rGuard,
+ std::u16string_view Module,
+ const OUString& Language, bool helpText = false,
+ const OUString* pExtensionPath = nullptr );
+
+
+ /**
+ * The following method returns the Collator for the given language-country combination
+ */
+ css::uno::Reference< css::i18n::XCollator >
+ getCollator(std::unique_lock<std::mutex>& rGuard, const OUString& Language);
+
+ /**
+ * Returns the cascading style sheet used to format the HTML-output.
+ * First try is language directory, second try is main installation directory.
+ */
+
+ void cascadingStylesheet( const OUString& Language,
+ OStringBuffer& buffer );
+
+ /**
+ * Changes the stylesheet for further reads.
+ */
+
+ void changeCSS(const OUString& newStyleSheet);
+
+ /**
+ * Returns the active help text for the given module, language and id.
+ */
+
+ void setActiveText( const OUString& Module,
+ const OUString& Language,
+ std::u16string_view Id,
+ OStringBuffer& buffer );
+
+ /**
+ * Has the purpose of forcing the jarfile to stay open
+ */
+
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ jarFile(std::unique_lock<std::mutex>& rGuard, std::u16string_view jar,
+ const OUString& Language );
+
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ findJarFileForPath( const OUString& jar, const OUString& Language,
+ const OUString& path, OUString* o_pExtensionPath = nullptr,
+ OUString* o_pExtensionRegistryPath = nullptr );
+
+ /**
+ * Maps a given language-locale combination to language or locale.
+ */
+ OUString processLang( const OUString& Language );
+ OUString processLang( std::unique_lock<std::mutex>& rGuard, const OUString& Language );
+
+ void replaceName( OUString& oustring ) const;
+
+ const OUString& getProductName() const { return m_vReplacement[0]; }
+ const OUString& getProductVersion() const { return m_vReplacement[1]; }
+
+ OUString expandURL( const OUString& aURL );
+ OUString expandURL( std::unique_lock<std::mutex>& rGuard, const OUString& aURL );
+
+ static OUString expandURL( const OUString& aURL,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ private:
+
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiComponentFactory > m_xSMgr;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFA;
+
+ bool m_bShowBasic;
+
+ std::vector<char> m_vCustomCSSDoc;
+ OUString m_aCSS;
+
+ enum {
+ PRODUCTNAME = 0,
+ PRODUCTVERSION,
+ VENDORNAME,
+ VENDORVERSION,
+ VENDORSHORT,
+ NEWPRODUCTNAME,
+ NEWPRODUCTVERSION
+ };
+
+ int m_vAdd[7];
+ OUString m_vReplacement[7];
+
+ OUString m_aInstallDirectory; // Installation directory
+
+ std::vector< OUString > m_avModules;
+
+ typedef std::unordered_map< OUString, std::unique_ptr<helpdatafileproxy::Hdf> > DatabasesTable;
+ DatabasesTable m_aDatabases; // Language and module dependent databases
+
+ typedef std::unordered_map< OUString,OUString > LangSetTable;
+ LangSetTable m_aLangSet; // Mapping to of lang-country to lang
+
+ typedef std::unordered_map< OUString, std::unique_ptr<StaticModuleInformation> > ModInfoTable;
+ ModInfoTable m_aModInfo; // Module information
+
+ typedef std::unordered_map< OUString, std::unique_ptr<KeywordInfo> > KeywordInfoTable;
+ KeywordInfoTable m_aKeywordInfo; // Module information
+
+ typedef
+ std::unordered_map<
+ OUString,
+ css::uno::Reference< css::container::XHierarchicalNameAccess > > ZipFileTable;
+ ZipFileTable m_aZipFileTable; // No closing of an once opened jarfile
+
+ typedef
+ std::unordered_map<
+ OUString,
+ css::uno::Reference< css::i18n::XCollator > > CollatorTable;
+ CollatorTable m_aCollatorTable;
+
+
+ typedef
+ std::unordered_set<
+ OString > EmptyActiveTextSet;
+ EmptyActiveTextSet m_aEmptyActiveTextSet;
+
+ // methods
+
+ void setInstallPath( const OUString& aInstallDirectory );
+
+ }; // end class Databases
+
+ enum class IteratorState
+ {
+ InitialModule,
+ //SHARED_MODULE, // Later, avoids redundancies in help compiling
+ UserExtensions,
+ SharedExtensions,
+ BundledExtensions,
+ EndReached
+ };
+
+ // Hashtable to cache extension help status
+ typedef std::unordered_map
+ <
+ OUString,
+ bool
+ >
+ ExtensionHelpExistenceMap;
+
+ class ExtensionIteratorBase
+ {
+ static ExtensionHelpExistenceMap aHelpExistenceMap;
+
+ public:
+ ExtensionIteratorBase( css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ Databases& rDatabases, OUString aInitialModule, OUString aLanguage );
+ ExtensionIteratorBase( Databases& rDatabases, OUString aInitialModule,
+ OUString aLanguage );
+ void init();
+
+ private:
+ static css::uno::Reference< css::deployment::XPackage > implGetHelpPackageFromPackage
+ ( const css::uno::Reference< css::deployment::XPackage >& xPackage,
+ css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle );
+
+ protected:
+ css::uno::Reference< css::deployment::XPackage > implGetNextUserHelpPackage
+ ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle );
+ css::uno::Reference< css::deployment::XPackage > implGetNextSharedHelpPackage
+ ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle );
+ css::uno::Reference< css::deployment::XPackage > implGetNextBundledHelpPackage
+ ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle );
+ OUString implGetFileFromPackage( std::u16string_view rFileExtension,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+ OUString implGetFileFromPackage(std::unique_lock<std::mutex>& rGuard,
+ std::u16string_view rFileExtension,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+ void implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFA;
+ Databases& m_rDatabases;
+
+ IteratorState m_eState;
+
+ OUString m_aInitialModule;
+ OUString m_aLanguage;
+
+ css::uno::Sequence< css::uno::Reference
+ < css::deployment::XPackage > > m_aUserPackagesSeq;
+ bool m_bUserPackagesLoaded;
+
+ css::uno::Sequence< css::uno::Reference
+ < css::deployment::XPackage > > m_aSharedPackagesSeq;
+ bool m_bSharedPackagesLoaded;
+
+ css::uno::Sequence< css::uno::Reference
+ < css::deployment::XPackage > > m_aBundledPackagesSeq;
+ bool m_bBundledPackagesLoaded;
+
+ int m_iUserPackage;
+ int m_iSharedPackage;
+ int m_iBundledPackage;
+
+ }; // end class ExtensionIteratorBase
+
+ class DataBaseIterator : public ExtensionIteratorBase
+ {
+ public:
+ DataBaseIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage, bool bHelpText )
+ : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage )
+ , m_bHelpText( bHelpText )
+ {}
+ DataBaseIterator( Databases& rDatabases, const OUString& aInitialModule,
+ const OUString& aLanguage, bool bHelpText )
+ : ExtensionIteratorBase( rDatabases, aInitialModule, aLanguage )
+ , m_bHelpText( bHelpText )
+ {}
+
+ helpdatafileproxy::Hdf* nextHdf( OUString* o_pExtensionPath = nullptr, OUString* o_pExtensionRegistryPath = nullptr );
+
+ private:
+ helpdatafileproxy::Hdf* implGetHdfFromPackage(
+ const css::uno::Reference< css::deployment::XPackage >& xPackage,
+ OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath );
+
+ bool m_bHelpText;
+
+ }; // end class DataBaseIterator
+
+ class KeyDataBaseFileIterator : public ExtensionIteratorBase
+ {
+ public:
+ KeyDataBaseFileIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage )
+ : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage )
+ {}
+ //Returns a file URL
+ OUString nextDbFile(std::unique_lock<std::mutex>& rGuard, bool& o_rbExtension);
+
+ private:
+ OUString implGetDbFileFromPackage(std::unique_lock<std::mutex>& rGuard,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+
+ }; // end class KeyDataBaseFileIterator
+
+ class JarFileIterator : public ExtensionIteratorBase
+ {
+ public:
+ JarFileIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage )
+ : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage )
+ {}
+
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ nextJarFile(std::unique_lock<std::mutex>& rGuard,
+ css::uno::Reference<css::deployment::XPackage>& o_xParentPackageBundle,
+ OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath );
+
+ private:
+ css::uno::Reference< css::container::XHierarchicalNameAccess >
+ implGetJarFromPackage(std::unique_lock<std::mutex>& rGuard,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage,
+ OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath );
+
+ }; // end class JarFileIterator
+
+ class IndexFolderIterator : public ExtensionIteratorBase
+ {
+ public:
+ IndexFolderIterator( Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage )
+ : ExtensionIteratorBase( rDatabases, aInitialModule, aLanguage )
+ {}
+
+ OUString nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary );
+ void deleteTempIndexFolder( std::u16string_view aIndexFolder );
+
+ private:
+ OUString implGetIndexFolderFromPackage( bool& o_rbTemporary,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+
+ }; // end class KeyDataBaseFileIterator
+
+} // end namespace chelp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/db.cxx b/xmlhelp/source/cxxhelp/provider/db.cxx
new file mode 100644
index 0000000000..d60bff78ee
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/db.cxx
@@ -0,0 +1,264 @@
+/* -*- 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 "db.hxx"
+
+#include <algorithm>
+#include <charconv>
+#include <cstring>
+#include <system_error>
+#include <utility>
+
+#include <com/sun/star/io/XSeekable.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+
+namespace {
+
+std::pair<sal_Int32, char const *> readInt32(char const * begin, char const * end) {
+ sal_Int32 n = 0;
+ auto const [ptr, ec] = std::from_chars(begin, end, n, 16);
+ return {std::max(n, sal_Int32(0)), ec == std::errc{} && n >= 0 ? ptr : begin};
+}
+
+}
+
+namespace helpdatafileproxy {
+
+void HDFData::copyToBuffer( const char* pSrcData, int nSize )
+{
+ m_nSize = nSize;
+ m_pBuffer.reset( new char[m_nSize+1] );
+ memcpy( m_pBuffer.get(), pSrcData, m_nSize );
+ m_pBuffer[m_nSize] = 0;
+}
+
+
+// Hdf
+
+bool Hdf::implReadLenAndData( const char* pData, char const * end, int& riPos, HDFData& rValue )
+{
+ bool bSuccess = false;
+
+ // Read key len
+ const char* pStartPtr = pData + riPos;
+ auto [nKeyLen, pEndPtr] = readInt32(pStartPtr, end);
+ if( pEndPtr == pStartPtr )
+ return bSuccess;
+ riPos += (pEndPtr - pStartPtr) + 1;
+
+ const char* pKeySrc = pData + riPos;
+ rValue.copyToBuffer( pKeySrc, nKeyLen );
+ riPos += nKeyLen + 1;
+
+ bSuccess = true;
+ return bSuccess;
+}
+
+void Hdf::createHashMap( bool bOptimizeForPerformance )
+{
+ releaseHashMap();
+ if( bOptimizeForPerformance )
+ {
+ if( m_pStringToDataMap != nullptr )
+ return;
+ m_pStringToDataMap.reset(new StringToDataMap);
+ }
+ else
+ {
+ if( m_pStringToValPosMap != nullptr )
+ return;
+ m_pStringToValPosMap.reset(new StringToValPosMap);
+ }
+
+ Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
+ if( !xIn.is() )
+ return;
+
+ Sequence< sal_Int8 > aData;
+ sal_Int32 nSize = m_xSFA->getSize( m_aFileURL );
+ sal_Int32 nRead = xIn->readBytes( aData, nSize );
+
+ const char* pData = reinterpret_cast<const char*>(aData.getConstArray());
+ auto const end = pData + nRead;
+ int iPos = 0;
+ while( iPos < nRead )
+ {
+ HDFData aDBKey;
+ if( !implReadLenAndData( pData, end, iPos, aDBKey ) )
+ break;
+
+ OString aOKeyStr = aDBKey.getData();
+
+ // Read val len
+ const char* pStartPtr = pData + iPos;
+ auto [nValLen, pEndPtr] = readInt32(pStartPtr, end);
+ if( pEndPtr == pStartPtr )
+ break;
+
+ iPos += (pEndPtr - pStartPtr) + 1;
+
+ if( bOptimizeForPerformance )
+ {
+ const char* pValSrc = pData + iPos;
+ OString aValStr( pValSrc, nValLen );
+ (*m_pStringToDataMap)[aOKeyStr] = aValStr;
+ }
+ else
+ {
+ // store value start position
+ (*m_pStringToValPosMap)[aOKeyStr] = std::pair<int,int>( iPos, nValLen );
+ }
+ iPos += nValLen + 1;
+ }
+
+ xIn->closeInput();
+}
+
+void Hdf::releaseHashMap()
+{
+ m_pStringToDataMap.reset();
+ m_pStringToValPosMap.reset();
+}
+
+
+Hdf::~Hdf()
+{
+}
+
+bool Hdf::getValueForKey( const OString& rKey, HDFData& rValue )
+{
+ bool bSuccess = false;
+ if( !m_xSFA.is() )
+ return bSuccess;
+
+ try
+ {
+
+ if( m_pStringToDataMap == nullptr && m_pStringToValPosMap == nullptr )
+ {
+ createHashMap( false/*bOptimizeForPerformance*/ );
+ }
+
+ if( m_pStringToValPosMap != nullptr )
+ {
+ StringToValPosMap::const_iterator it = m_pStringToValPosMap->find( rKey );
+ if( it != m_pStringToValPosMap->end() )
+ {
+ const std::pair<int,int>& rValPair = it->second;
+ int iValuePos = rValPair.first;
+ int nValueLen = rValPair.second;
+
+ Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
+ if( xIn.is() )
+ {
+ Reference< XSeekable > xXSeekable( xIn, UNO_QUERY );
+ if( xXSeekable.is() )
+ {
+ xXSeekable->seek( iValuePos );
+
+ Sequence< sal_Int8 > aData;
+ sal_Int32 nRead = xIn->readBytes( aData, nValueLen );
+ if( nRead == nValueLen )
+ {
+ const char* pData = reinterpret_cast<const char*>(aData.getConstArray());
+ rValue.copyToBuffer( pData, nValueLen );
+ bSuccess = true;
+ }
+ }
+ xIn->closeInput();
+ }
+ }
+ }
+
+ else if( m_pStringToDataMap != nullptr )
+ {
+ StringToDataMap::const_iterator it = m_pStringToDataMap->find( rKey );
+ if( it != m_pStringToDataMap->end() )
+ {
+ const OString& rValueStr = it->second;
+ int nValueLen = rValueStr.getLength();
+ const char* pData = rValueStr.getStr();
+ rValue.copyToBuffer( pData, nValueLen );
+ bSuccess = true;
+ }
+ }
+
+ }
+ catch( Exception & )
+ {
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+bool Hdf::startIteration()
+{
+ bool bSuccess = false;
+
+ sal_Int32 nSize = m_xSFA->getSize( m_aFileURL );
+
+ Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL );
+ if( xIn.is() )
+ {
+ m_nItRead = xIn->readBytes( m_aItData, nSize );
+ if( m_nItRead == nSize )
+ {
+ bSuccess = true;
+ m_iItPos = 0;
+ }
+ else
+ {
+ stopIteration();
+ }
+ }
+
+ return bSuccess;
+}
+
+bool Hdf::getNextKeyAndValue( HDFData& rKey, HDFData& rValue )
+{
+ bool bSuccess = false;
+
+ if( m_iItPos < m_nItRead )
+ {
+ auto const p = reinterpret_cast<const char*>(m_aItData.getConstArray());
+ if( implReadLenAndData( p, p + m_aItData.size(), m_iItPos, rKey ) )
+ {
+ if( implReadLenAndData( p, p + m_aItData.size(), m_iItPos, rValue ) )
+ bSuccess = true;
+ }
+ }
+
+ return bSuccess;
+}
+
+void Hdf::stopIteration()
+{
+ m_aItData = Sequence<sal_Int8>();
+ m_nItRead = -1;
+ m_iItPos = -1;
+}
+
+} // end of namespace helpdatafileproxy
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/db.hxx b/xmlhelp/source/cxxhelp/provider/db.hxx
new file mode 100644
index 0000000000..9a63c8f098
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/db.hxx
@@ -0,0 +1,96 @@
+/* -*- 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/ucb/XSimpleFileAccess3.hpp>
+#include <comphelper/fileurl.hxx>
+#include <osl/diagnose.h>
+#include <rtl/string.hxx>
+#include <memory>
+#include <unordered_map>
+
+namespace helpdatafileproxy {
+
+ class HDFData
+ {
+ friend class Hdf;
+
+ int m_nSize;
+ std::unique_ptr<char[]> m_pBuffer;
+
+ void copyToBuffer( const char* pSrcData, int nSize );
+
+ public:
+ HDFData() : m_nSize( 0 ) {}
+
+ int getSize() const
+ { return m_nSize; }
+ const char* getData() const
+ { return m_pBuffer.get(); }
+ };
+
+ typedef std::unordered_map< OString,std::pair<int,int> > StringToValPosMap;
+ typedef std::unordered_map< OString,OString > StringToDataMap;
+
+ class Hdf
+ {
+ OUString m_aFileURL;
+ std::unique_ptr<StringToDataMap> m_pStringToDataMap;
+ std::unique_ptr<StringToValPosMap> m_pStringToValPosMap;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 >
+ m_xSFA;
+
+ css::uno::Sequence< sal_Int8 >
+ m_aItData;
+ int m_nItRead;
+ int m_iItPos;
+
+ static bool implReadLenAndData(
+ const char* pData, char const * end, int& riPos, HDFData& rValue );
+
+ public:
+ //HDFHelp must get a fileURL which can then directly be used by simple file access.
+ //SimpleFileAccess requires file URLs as arguments. Passing file path may work but fails
+ //for example when using long file paths on Windows, which start with "\\?\"
+ Hdf( OUString aFileURL,
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > xSFA )
+ : m_aFileURL( std::move(aFileURL) )
+ , m_xSFA( std::move(xSFA) )
+ , m_nItRead( -1 )
+ , m_iItPos( -1 )
+ {
+ OSL_ASSERT(comphelper::isFileUrl(m_aFileURL));
+ }
+ ~Hdf();
+
+ void createHashMap( bool bOptimizeForPerformance );
+ void releaseHashMap();
+
+ bool getValueForKey( const OString& rKey, HDFData& rValue );
+
+ bool startIteration();
+ bool getNextKeyAndValue( HDFData& rKey, HDFData& rValue );
+ void stopIteration();
+ Hdf(const Hdf&) = delete;
+ void operator=(const Hdf&) = delete;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/inputstream.cxx b/xmlhelp/source/cxxhelp/provider/inputstream.cxx
new file mode 100644
index 0000000000..59c87cef40
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/inputstream.cxx
@@ -0,0 +1,185 @@
+/* -*- 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 "inputstream.hxx"
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <cppuhelper/queryinterface.hxx>
+
+
+using namespace chelp;
+using namespace com::sun::star;
+
+
+XInputStream_impl::XInputStream_impl( const OUString& aUncPath )
+ : m_bIsOpen( false ),
+ m_aFile( aUncPath )
+{
+ m_bIsOpen = ( osl::FileBase::E_None == m_aFile.open( osl_File_OpenFlag_Read ) );
+}
+
+XInputStream_impl::~XInputStream_impl()
+{
+ if (m_bIsOpen)
+ m_aFile.close();
+}
+
+uno::Any SAL_CALL
+XInputStream_impl::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< io::XInputStream* >(this),
+ static_cast< io::XSeekable* >(this) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+
+void SAL_CALL
+XInputStream_impl::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL
+XInputStream_impl::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+sal_Int32 SAL_CALL
+XInputStream_impl::readBytes(
+ uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead )
+{
+ if( ! m_bIsOpen )
+ throw io::IOException();
+
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+ //TODO! translate memory exhaustion (if it were detectable...) into
+ // io::BufferSizeExceededException
+
+ sal_uInt64 nBytesRead;
+ m_aFile.read( aData.getArray(), sal_uInt64(nBytesToRead), nBytesRead );
+
+ // Shrink aData in case we read less than nBytesToRead (XInputStream
+ // documentation does not tell whether this is required, and I do not know
+ // if any code relies on this, so be conservative---SB):
+ if (nBytesRead != sal::static_int_cast<sal_uInt64>(nBytesToRead) )
+ aData.realloc(sal_Int32(nBytesRead));
+ return static_cast<sal_Int32>(nBytesRead);
+}
+
+sal_Int32 SAL_CALL
+XInputStream_impl::readSomeBytes(
+ uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nMaxBytesToRead )
+{
+ return readBytes( aData,nMaxBytesToRead );
+}
+
+
+void SAL_CALL
+XInputStream_impl::skipBytes(
+ sal_Int32 nBytesToSkip )
+{
+ if (m_aFile.setPos(osl_Pos_Current, sal_uInt64(nBytesToSkip)) != osl::FileBase::E_None)
+ {
+ throw io::IOException("XInputStream_impl::skipBytes failed seek");
+ }
+}
+
+
+sal_Int32 SAL_CALL
+XInputStream_impl::available()
+{
+ sal_uInt64 uPos;
+ if( osl::FileBase::E_None != m_aFile.getPos( uPos ) )
+ throw io::IOException();
+ sal_uInt64 uSize;
+ if( osl::FileBase::E_None != m_aFile.getSize( uSize ) )
+ throw io::IOException();
+ return std::min<sal_uInt64>(SAL_MAX_INT32, uSize - uPos);
+}
+
+
+void SAL_CALL
+XInputStream_impl::closeInput()
+{
+ if( m_bIsOpen )
+ {
+ osl::FileBase::RC err = m_aFile.close();
+ if( err != osl::FileBase::E_None )
+ throw io::IOException();
+ m_bIsOpen = false;
+ }
+}
+
+
+void SAL_CALL
+XInputStream_impl::seek( sal_Int64 location )
+{
+ if( location < 0 )
+ throw lang::IllegalArgumentException();
+ if( osl::FileBase::E_None != m_aFile.setPos( osl_Pos_Absolut, sal_uInt64( location ) ) )
+ throw io::IOException();
+}
+
+
+sal_Int64 SAL_CALL
+XInputStream_impl::getPosition()
+{
+ sal_uInt64 uPos;
+ if( osl::FileBase::E_None != m_aFile.getPos( uPos ) )
+ throw io::IOException();
+ return sal_Int64( uPos );
+}
+
+sal_Int64 SAL_CALL
+XInputStream_impl::getLength()
+{
+ osl::FileBase::RC err;
+ sal_uInt64 uCurrentPos, uEndPos;
+
+ err = m_aFile.getPos( uCurrentPos );
+ if( err != osl::FileBase::E_None )
+ throw io::IOException();
+
+ err = m_aFile.setPos( osl_Pos_End, 0 );
+ if( err != osl::FileBase::E_None )
+ throw io::IOException();
+
+ err = m_aFile.getPos( uEndPos );
+ if( err != osl::FileBase::E_None )
+ throw io::IOException();
+
+ err = m_aFile.setPos( osl_Pos_Absolut, uCurrentPos );
+ if( err != osl::FileBase::E_None )
+ throw io::IOException();
+
+ return sal_Int64( uEndPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/inputstream.hxx b/xmlhelp/source/cxxhelp/provider/inputstream.hxx
new file mode 100644
index 0000000000..5f9146293b
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/inputstream.hxx
@@ -0,0 +1,96 @@
+/* -*- 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 <osl/file.hxx>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+
+namespace chelp {
+
+ class XInputStream_impl
+ : public cppu::OWeakObject,
+ public css::io::XInputStream,
+ public css::io::XSeekable
+ {
+
+ public:
+ explicit XInputStream_impl( const OUString& aUncPath );
+
+ virtual ~XInputStream_impl() override;
+
+ /**
+ * Returns an error code as given by filerror.hxx
+ */
+
+ bool CtorSuccess() { return m_bIsOpen;}
+
+ 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;
+
+ virtual sal_Int32 SAL_CALL
+ readBytes(
+ css::uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead ) override;
+
+ virtual sal_Int32 SAL_CALL
+ readSomeBytes(
+ css::uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nMaxBytesToRead ) override;
+
+ virtual void SAL_CALL
+ skipBytes( sal_Int32 nBytesToSkip ) override;
+
+ virtual sal_Int32 SAL_CALL
+ available() override;
+
+ virtual void SAL_CALL
+ closeInput() override;
+
+ virtual void SAL_CALL
+ seek( sal_Int64 location ) override;
+
+ virtual sal_Int64 SAL_CALL
+ getPosition() override;
+
+ virtual sal_Int64 SAL_CALL
+ getLength() override;
+
+ private:
+ bool m_bIsOpen;
+ osl::File m_aFile;
+ };
+
+
+} // end namespace XInputStream_impl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/provider.cxx b/xmlhelp/source/cxxhelp/provider/provider.cxx
new file mode 100644
index 0000000000..eb28f5a9ca
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/provider.cxx
@@ -0,0 +1,198 @@
+/* -*- 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 <config_folders.h>
+
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Setup.hxx>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/factory.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+
+#include "databases.hxx"
+#include "provider.hxx"
+#include "content.hxx"
+
+using namespace com::sun::star;
+using namespace chelp;
+
+
+// ContentProvider Implementation.
+
+ContentProvider::ContentProvider( const uno::Reference< uno::XComponentContext >& rxContext )
+ : ContentProvider_Base( rxContext )
+ , isInitialized( false )
+{
+}
+
+// virtual
+ContentProvider::~ContentProvider()
+{
+}
+
+// XServiceInfo methods.
+
+OUString SAL_CALL ContentProvider::getImplementationName()
+{
+ return "CHelpContentProvider";
+}
+
+sal_Bool SAL_CALL
+ContentProvider::supportsService(const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+ContentProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.help.XMLHelp", "com.sun.star.ucb.HelpContentProvider" };
+}
+
+// XContentProvider methods.
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+ContentProvider::queryContent(
+ const uno::Reference< ucb::XContentIdentifier >& xCanonicId )
+{
+ if ( !xCanonicId->getContentProviderScheme()
+ .equalsIgnoreAsciiCase( MYUCP_URL_SCHEME ) )
+ { // Wrong URL-scheme
+ throw ucb::IllegalIdentifierException();
+ }
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if( !isInitialized )
+ init();
+ }
+
+ if( !m_pDatabases )
+ throw uno::RuntimeException();
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xCanonicId );
+ if ( xContent.is() )
+ return xContent;
+
+ xContent = new Content( m_xContext, this, xCanonicId, m_pDatabases.get() );
+
+ // register new content
+ registerNewContent( xContent );
+
+ // Further checks
+
+ if ( !xContent->getIdentifier().is() )
+ throw ucb::IllegalIdentifierException();
+
+ return xContent;
+}
+
+void SAL_CALL
+ContentProvider::dispose()
+{
+ if(m_xContainer.is())
+ {
+ m_xContainer->removeContainerListener(this);
+ m_xContainer.clear();
+ }
+}
+
+void SAL_CALL
+ContentProvider::elementReplaced(const container::ContainerEvent& Event)
+{
+ if(!m_pDatabases)
+ return;
+
+ OUString accessor;
+ Event.Accessor >>= accessor;
+ if(accessor != "HelpStyleSheet")
+ return;
+
+ OUString replacedElement,element;
+ Event.ReplacedElement >>= replacedElement;
+ Event.Element >>= element;
+
+ if(replacedElement == element)
+ return;
+
+ m_pDatabases->changeCSS(element);
+}
+
+void ContentProvider::init()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ isInitialized = true;
+
+ OUString instPath(
+ officecfg::Office::Common::Path::Current::Help::get());
+ if( instPath.isEmpty() )
+ // try to determine path from default
+ instPath = "$(instpath)/" LIBO_SHARE_HELP_FOLDER;
+ // replace anything like $(instpath);
+ subst( instPath );
+
+ OUString stylesheet(
+ officecfg::Office::Common::Help::HelpStyleSheet::get());
+
+ // now adding as configuration change listener for the stylesheet
+ m_xContainer.set(
+ officecfg::Office::Common::Help::get(),
+ css::uno::UNO_QUERY_THROW);
+ m_xContainer->addContainerListener( this );
+
+ OUString setupversion(
+ officecfg::Setup::Product::ooSetupVersion::get());
+ OUString setupextension(
+ officecfg::Setup::Product::ooSetupExtension::get());
+ OUString productversion( setupversion + " " + setupextension );
+
+ bool showBasic = officecfg::Office::Common::Help::ShowBasic::get();
+ m_pDatabases.reset( new Databases( showBasic,
+ instPath,
+ utl::ConfigManager::getProductName(),
+ productversion,
+ stylesheet,
+ m_xContext ) );
+}
+
+void ContentProvider::subst( OUString& instpath )
+{
+ SvtPathOptions aOptions;
+ instpath = aOptions.SubstituteVariable( instpath );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+CHelpContentProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ContentProvider(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/provider.hxx b/xmlhelp/source/cxxhelp/provider/provider.hxx
new file mode 100644
index 0000000000..3c1c8eec50
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/provider.hxx
@@ -0,0 +1,106 @@
+/* -*- 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 <memory>
+#include <rtl/ustring.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+namespace chelp {
+
+// URL scheme. This is the scheme the provider will be able to create
+// contents for. The UCB will select the provider ( i.e. in order to create
+// contents ) according to this scheme.
+
+#define MYUCP_URL_SCHEME "vnd.sun.star.help"
+inline constexpr OUString MYUCP_CONTENT_TYPE = u"application/vnd.sun.star.xmlhelp"_ustr; // UCB Content Type.
+
+ class Databases;
+
+ typedef cppu::ImplInheritanceHelper< ::ucbhelper::ContentProviderImplHelper, css::container::XContainerListener, css::lang::XComponent> ContentProvider_Base;
+ class ContentProvider : public ContentProvider_Base
+ {
+ public:
+ explicit ContentProvider(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual ~ContentProvider() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual 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;
+
+ // Additional interfaces
+
+ // XComponent
+
+ virtual void SAL_CALL
+ dispose( ) override;
+
+ virtual void SAL_CALL
+ addEventListener( const css::uno::Reference< css::lang::XEventListener >& ) override {}
+
+ virtual void SAL_CALL
+ removeEventListener( const css::uno::Reference< css::lang::XEventListener >& ) override {}
+
+ // XContainerListener ( derive from XEventListener )
+
+ virtual void SAL_CALL
+ disposing( const css::lang::EventObject& /*Source*/ ) override
+ {
+ m_xContainer.clear();
+ }
+
+ virtual void SAL_CALL
+ elementInserted( const css::container::ContainerEvent& ) override {}
+
+ virtual void SAL_CALL
+ elementRemoved( const css::container::ContainerEvent& ) override {}
+
+ virtual void SAL_CALL
+ elementReplaced( const css::container::ContainerEvent& Event ) override;
+
+ // Non-interface methods.
+
+ private:
+ bool isInitialized;
+ std::unique_ptr<Databases> m_pDatabases;
+ css::uno::Reference<css::container::XContainer> m_xContainer;
+
+ // private methods
+
+ void init();
+
+ static void subst( OUString& instpath );
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultset.cxx b/xmlhelp/source/cxxhelp/provider/resultset.cxx
new file mode 100644
index 0000000000..23d7dcbf79
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultset.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+
+#include "resultset.hxx"
+#include "resultsetfactory.hxx"
+
+using namespace com::sun::star::lang;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::uno;
+
+using namespace chelp;
+
+// DynamicResultSet Implementation.
+
+DynamicResultSet::DynamicResultSet(
+ const Reference< XComponentContext >& rxContext,
+ const OpenCommandArgument2& rCommand,
+ std::unique_ptr<ResultSetFactory> pFactory )
+ : ResultSetImplHelper( rxContext, rCommand ),
+ m_pFactory( std::move(pFactory) )
+{
+}
+
+DynamicResultSet::~DynamicResultSet()
+{
+}
+
+// Non-interface methods.
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1.set( m_pFactory->createResultSet() );
+}
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1.set( m_pFactory->createResultSet() );
+
+ m_xResultSet2 = m_xResultSet1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultset.hxx b/xmlhelp/source/cxxhelp/provider/resultset.hxx
new file mode 100644
index 0000000000..81a7bb0b3a
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultset.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 <memory>
+#include <ucbhelper/resultsethelper.hxx>
+
+namespace chelp {
+
+ class ResultSetFactory;
+
+ class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+ {
+ std::unique_ptr<ResultSetFactory> m_pFactory;
+
+ private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+ public:
+ DynamicResultSet(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::ucb::OpenCommandArgument2& rCommand,
+ std::unique_ptr<ResultSetFactory> pFactory );
+
+ virtual ~DynamicResultSet() override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx b/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx
new file mode 100644
index 0000000000..58d8e70c5b
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx
@@ -0,0 +1,485 @@
+/* -*- 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 <ucbhelper/contentidentifier.hxx>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <ucbhelper/resultsetmetadata.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <utility>
+
+#include "resultsetbase.hxx"
+
+using namespace chelp;
+using namespace com::sun::star;
+
+ResultSetBase::ResultSetBase( uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< ucb::XContentProvider > xProvider,
+ const uno::Sequence< beans::Property >& seq )
+ : m_xContext(std::move( xContext )),
+ m_xProvider(std::move( xProvider )),
+ m_nRow( -1 ),
+ m_nWasNull( true ),
+ m_sProperty( seq )
+{
+}
+
+ResultSetBase::~ResultSetBase()
+{
+}
+
+
+// XInterface
+
+void SAL_CALL
+ResultSetBase::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL
+ResultSetBase::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+uno::Any SAL_CALL
+ResultSetBase::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XComponent* >(this),
+ static_cast< sdbc::XRow* >(this),
+ static_cast< sdbc::XResultSet* >(this),
+ static_cast< sdbc::XResultSetMetaDataSupplier* >(this),
+ static_cast< beans::XPropertySet* >(this),
+ static_cast< ucb::XContentAccess* >(this) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+
+// XComponent
+
+
+void SAL_CALL
+ResultSetBase::addEventListener(
+ const uno::Reference< lang::XEventListener >& Listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ m_aDisposeEventListeners.addInterface( aGuard, Listener );
+}
+
+
+void SAL_CALL
+ResultSetBase::removeEventListener(
+ const uno::Reference< lang::XEventListener >& Listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ m_aDisposeEventListeners.removeInterface( aGuard, Listener );
+}
+
+
+void SAL_CALL
+ResultSetBase::dispose()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ lang::EventObject aEvt;
+ aEvt.Source = static_cast< lang::XComponent * >( this );
+
+ if ( m_aDisposeEventListeners.getLength(aGuard) )
+ {
+ m_aDisposeEventListeners.disposeAndClear( aGuard, aEvt );
+ }
+ if( m_aRowCountListeners.getLength(aGuard) )
+ {
+ m_aRowCountListeners.disposeAndClear( aGuard, aEvt );
+ }
+ if( m_aIsFinalListeners.getLength(aGuard) )
+ {
+ m_aIsFinalListeners.disposeAndClear( aGuard, aEvt );
+ }
+}
+
+
+// XResultSet
+
+sal_Bool SAL_CALL
+ResultSetBase::next()
+{
+ m_nRow++;
+ return sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isBeforeFirst()
+{
+ return m_nRow == -1;
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isAfterLast()
+{
+ return sal::static_int_cast<sal_uInt32>( m_nRow ) >= m_aItems.size(); // Cannot happen, if m_aFolder.isOpen()
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isFirst()
+{
+ return m_nRow == 0;
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isLast()
+{
+ if( sal::static_int_cast<sal_uInt32>( m_nRow ) == m_aItems.size() - 1 )
+ return true;
+ else
+ return false;
+}
+
+
+void SAL_CALL
+ResultSetBase::beforeFirst()
+{
+ m_nRow = -1;
+}
+
+
+void SAL_CALL
+ResultSetBase::afterLast()
+{
+ m_nRow = m_aItems.size();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::first()
+{
+ m_nRow = -1;
+ return next();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::last()
+{
+ m_nRow = m_aItems.size() - 1;
+ return true;
+}
+
+
+sal_Int32 SAL_CALL
+ResultSetBase::getRow()
+{
+ // Test, whether behind last row
+ if( -1 == m_nRow || sal::static_int_cast<sal_uInt32>( m_nRow ) >= m_aItems.size() )
+ return 0;
+ else
+ return m_nRow+1;
+}
+
+
+sal_Bool SAL_CALL ResultSetBase::absolute( sal_Int32 row )
+{
+ if( row >= 0 )
+ m_nRow = row - 1;
+ else
+ {
+ last();
+ m_nRow += ( row + 1 );
+ if( m_nRow < -1 )
+ m_nRow = -1;
+ }
+
+ return 0<= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::relative( sal_Int32 row )
+{
+ if( isAfterLast() || isBeforeFirst() )
+ throw sdbc::SQLException();
+
+ if( row > 0 )
+ while( row-- )
+ next();
+ else if( row < 0 )
+ while( row++ && m_nRow > -1 )
+ previous();
+
+ return 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::previous()
+{
+ if( sal::static_int_cast<sal_uInt32>( m_nRow ) > m_aItems.size() )
+ m_nRow = m_aItems.size(); // Correct Handling of afterLast
+ if( 0 <= m_nRow ) -- m_nRow;
+
+ return 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size();
+}
+
+
+void SAL_CALL
+ResultSetBase::refreshRow()
+{
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::rowUpdated()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL
+ResultSetBase::rowInserted()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL
+ResultSetBase::rowDeleted()
+{
+ return false;
+}
+
+
+uno::Reference< uno::XInterface > SAL_CALL
+ResultSetBase::getStatement()
+{
+ return uno::Reference< uno::XInterface >();
+}
+
+
+// XCloseable
+
+void SAL_CALL
+ResultSetBase::close()
+{
+}
+
+
+OUString SAL_CALL
+ResultSetBase::queryContentIdentifierString()
+{
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aPath[m_nRow];
+ else
+ return OUString();
+}
+
+
+uno::Reference< ucb::XContentIdentifier > SAL_CALL
+ResultSetBase::queryContentIdentifier()
+{
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ {
+ OUString url = queryContentIdentifierString();
+ if( ! m_aIdents[m_nRow].is() && !url.isEmpty() )
+ m_aIdents[m_nRow].set( new ::ucbhelper::ContentIdentifier( url ) );
+ return m_aIdents[m_nRow];
+ }
+
+ return uno::Reference< ucb::XContentIdentifier >();
+}
+
+
+uno::Reference< ucb::XContent > SAL_CALL
+ResultSetBase::queryContent()
+{
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_xProvider->queryContent( queryContentIdentifier() );
+ else
+ return uno::Reference< ucb::XContent >();
+}
+
+namespace {
+
+class XPropertySetInfoImpl
+ : public cppu::OWeakObject,
+ public beans::XPropertySetInfo
+{
+public:
+
+ explicit XPropertySetInfoImpl( const uno::Sequence< beans::Property >& aSeq )
+ : m_aSeq( aSeq )
+ {
+ }
+
+ void SAL_CALL acquire()
+ noexcept override
+ {
+ OWeakObject::acquire();
+ }
+
+
+ void SAL_CALL release()
+ noexcept override
+ {
+ OWeakObject::release();
+ }
+
+ uno::Any SAL_CALL queryInterface( const uno::Type& rType ) override
+ {
+ uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< beans::XPropertySetInfo* >(this) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+ }
+
+ uno::Sequence< beans::Property > SAL_CALL getProperties() override
+ {
+ return m_aSeq;
+ }
+
+ beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override
+ {
+ auto pProp = std::find_if(std::cbegin(m_aSeq), std::cend(m_aSeq),
+ [&aName](const beans::Property& rProp) { return aName == rProp.Name; });
+ if (pProp != std::cend(m_aSeq))
+ return *pProp;
+ throw beans::UnknownPropertyException(aName);
+ }
+
+ sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override
+ {
+ return std::any_of(std::cbegin(m_aSeq), std::cend(m_aSeq),
+ [&Name](const beans::Property& rProp) { return Name == rProp.Name; });
+ }
+
+private:
+
+ uno::Sequence< beans::Property > m_aSeq;
+};
+
+}
+
+// XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+ResultSetBase::getPropertySetInfo()
+{
+ uno::Sequence< beans::Property > seq
+ {
+ { "RowCount", -1, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY },
+ { "IsRowCountFinal", -1, cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY }
+ };
+
+ //t
+ return uno::Reference< beans::XPropertySetInfo > ( new XPropertySetInfoImpl( seq ) );
+}
+
+
+void SAL_CALL ResultSetBase::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& )
+{
+ if( aPropertyName == "IsRowCountFinal" ||
+ aPropertyName == "RowCount" )
+ return;
+
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+uno::Any SAL_CALL ResultSetBase::getPropertyValue(
+ const OUString& PropertyName )
+{
+ if( PropertyName == "IsRowCountFinal" )
+ {
+ return uno::Any(true);
+ }
+ else if ( PropertyName == "RowCount" )
+ {
+ sal_Int32 count = m_aItems.size();
+ return uno::Any(count);
+ }
+ else
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::addPropertyChangeListener(
+ const OUString& aPropertyName,
+ const uno::Reference< beans::XPropertyChangeListener >& xListener )
+{
+ if( aPropertyName == "IsRowCountFinal" )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aIsFinalListeners.addInterface( aGuard, xListener );
+ }
+ else if ( aPropertyName == "RowCount" )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aRowCountListeners.addInterface( aGuard, xListener );
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::removePropertyChangeListener(
+ const OUString& aPropertyName,
+ const uno::Reference< beans::XPropertyChangeListener >& aListener )
+{
+ if( aPropertyName == "IsRowCountFinal" )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aIsFinalListeners.removeInterface( aGuard, aListener );
+ }
+ else if ( aPropertyName == "RowCount" )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aRowCountListeners.removeInterface( aGuard, aListener );
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::addVetoableChangeListener(
+ const OUString&,
+ const uno::Reference< beans::XVetoableChangeListener >& )
+{}
+
+
+void SAL_CALL ResultSetBase::removeVetoableChangeListener(
+ const OUString&,
+ const uno::Reference< beans::XVetoableChangeListener >& )
+{}
+
+
+// XResultSetMetaDataSupplier
+uno::Reference< sdbc::XResultSetMetaData > SAL_CALL
+ResultSetBase::getMetaData()
+{
+ return new ::ucbhelper::ResultSetMetaData( m_xContext, m_sProperty );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx b/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx
new file mode 100644
index 0000000000..4327d3e972
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx
@@ -0,0 +1,400 @@
+/* -*- 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 <memory>
+#include <cppuhelper/weak.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+
+namespace chelp {
+
+ class ResultSetBase
+ : public cppu::OWeakObject,
+ public css::lang::XComponent,
+ public css::sdbc::XRow,
+ public css::sdbc::XResultSet,
+ public css::sdbc::XCloseable,
+ public css::sdbc::XResultSetMetaDataSupplier,
+ public css::beans::XPropertySet,
+ public css::ucb::XContentAccess
+ {
+ public:
+
+ ResultSetBase( css::uno::Reference< css::uno::XComponentContext > xContext,
+ css::uno::Reference< css::ucb::XContentProvider > xProvider,
+ const css::uno::Sequence< css::beans::Property >& seq );
+
+ virtual ~ResultSetBase() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL
+ queryInterface( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL
+ acquire()
+ noexcept override;
+
+ virtual void SAL_CALL
+ release()
+ noexcept 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;
+
+
+ // XRow
+ virtual sal_Bool SAL_CALL
+ wasNull() override
+ {
+ if( 0<= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ m_nWasNull = m_aItems[m_nRow]->wasNull();
+ else
+ m_nWasNull = true;
+ return m_nWasNull;
+ }
+
+ virtual OUString SAL_CALL
+ getString( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getString( columnIndex );
+ else
+ return OUString();
+ }
+
+ virtual sal_Bool SAL_CALL
+ getBoolean( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getBoolean( columnIndex );
+ else
+ return false;
+ }
+
+ virtual sal_Int8 SAL_CALL
+ getByte( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getByte( columnIndex );
+ else
+ return sal_Int8( 0 );
+ }
+
+ virtual sal_Int16 SAL_CALL
+ getShort( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getShort( columnIndex );
+ else
+ return sal_Int16( 0 );
+ }
+
+ virtual sal_Int32 SAL_CALL
+ getInt( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getInt( columnIndex );
+ else
+ return 0;
+ }
+
+ virtual sal_Int64 SAL_CALL
+ getLong( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getLong( columnIndex );
+ else
+ return sal_Int64( 0 );
+ }
+
+ virtual float SAL_CALL
+ getFloat( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getFloat( columnIndex );
+ else
+ return float( 0 );
+ }
+
+ virtual double SAL_CALL
+ getDouble( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getDouble( columnIndex );
+ else
+ return double( 0 );
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getBytes( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getBytes( columnIndex );
+ else
+ return css::uno::Sequence< sal_Int8 >();
+ }
+
+ virtual css::util::Date SAL_CALL
+ getDate( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getDate( columnIndex );
+ else
+ return css::util::Date();
+ }
+
+ virtual css::util::Time SAL_CALL
+ getTime( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getTime( columnIndex );
+ else
+ return css::util::Time();
+ }
+
+ virtual css::util::DateTime SAL_CALL
+ getTimestamp( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getTimestamp( columnIndex );
+ else
+ return css::util::DateTime();
+ }
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getBinaryStream( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getBinaryStream( columnIndex );
+ else
+ return css::uno::Reference< css::io::XInputStream >();
+ }
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getCharacterStream( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getCharacterStream( columnIndex );
+ else
+ return css::uno::Reference< css::io::XInputStream >();
+ }
+
+ virtual css::uno::Any SAL_CALL
+ getObject( sal_Int32 columnIndex,
+ const css::uno::Reference< css::container::XNameAccess >& typeMap ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getObject( columnIndex,typeMap );
+ else
+ return css::uno::Any();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL
+ getRef( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getRef( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XRef >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL
+ getBlob( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getBlob( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XBlob >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL
+ getClob( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getClob( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XClob >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL
+ getArray( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() )
+ return m_aItems[m_nRow]->getArray( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XArray >();
+ }
+
+
+ // XResultSet
+
+ virtual sal_Bool SAL_CALL
+ next() override;
+
+ virtual sal_Bool SAL_CALL
+ isBeforeFirst() override;
+
+ virtual sal_Bool SAL_CALL
+ isAfterLast() override;
+
+ virtual sal_Bool SAL_CALL
+ isFirst() override;
+
+ virtual sal_Bool SAL_CALL
+ isLast() override;
+
+ virtual void SAL_CALL
+ beforeFirst() override;
+
+ virtual void SAL_CALL
+ afterLast() override;
+
+ virtual sal_Bool SAL_CALL
+ first() override;
+
+ virtual sal_Bool SAL_CALL
+ last() override;
+
+ virtual sal_Int32 SAL_CALL
+ getRow() override;
+
+ virtual sal_Bool SAL_CALL
+ absolute( sal_Int32 row ) override;
+
+ virtual sal_Bool SAL_CALL
+ relative( sal_Int32 rows ) override;
+
+ virtual sal_Bool SAL_CALL
+ previous() override;
+
+ virtual void SAL_CALL
+ refreshRow() override;
+
+ virtual sal_Bool SAL_CALL
+ rowUpdated() override;
+
+ virtual sal_Bool SAL_CALL
+ rowInserted() override;
+
+ virtual sal_Bool SAL_CALL
+ rowDeleted() override;
+
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ getStatement() override;
+
+ // XCloseable
+
+ virtual void SAL_CALL
+ close() override;
+
+ // XContentAccess
+
+ virtual OUString SAL_CALL
+ queryContentIdentifierString() override;
+
+ virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
+ queryContentIdentifier() override;
+
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent() override;
+
+ // XResultSetMetaDataSupplier
+ virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL
+ getMetaData() override;
+
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& aPropertyName,
+ const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getPropertyValue(
+ const OUString& PropertyName ) override;
+
+ virtual void SAL_CALL
+ addPropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+
+ virtual void SAL_CALL
+ removePropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL
+ addVetoableChangeListener(
+ const OUString& PropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& PropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ protected:
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::ucb::XContentProvider > m_xProvider;
+ sal_Int32 m_nRow;
+ bool m_nWasNull;
+
+ typedef std::vector< css::uno::Reference< css::ucb::XContentIdentifier > > IdentSet;
+ typedef std::vector< css::uno::Reference< css::sdbc::XRow > > ItemSet;
+
+ IdentSet m_aIdents;
+ ItemSet m_aItems;
+ std::vector<OUString> m_aPath;
+
+ css::uno::Sequence< css::beans::Property > m_sProperty;
+
+ std::mutex m_aMutex;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aDisposeEventListeners;
+ comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener> m_aRowCountListeners;
+ comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener> m_aIsFinalListeners;
+ };
+
+
+} // end namespace fileaccess
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx b/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx
new file mode 100644
index 0000000000..b78b7a4e32
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx
@@ -0,0 +1,42 @@
+/* -*- 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 "resultsetbase.hxx"
+#include <rtl/ref.hxx>
+
+namespace chelp {
+
+ class ResultSetBase;
+
+ class ResultSetFactory
+ {
+ public:
+
+ virtual ~ResultSetFactory() { };
+
+ virtual rtl::Reference<ResultSetBase> createResultSet() = 0;
+ };
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx b/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx
new file mode 100644
index 0000000000..b598c241e7
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx
@@ -0,0 +1,331 @@
+/* -*- 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 <iterator>
+
+#include <com/sun/star/ucb/Command.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/i18n/Transliteration.hpp>
+#include <com/sun/star/ucb/XCommandProcessor.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+
+#include <helpcompiler/HelpSearch.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#if defined(__GNUC__)
+# pragma GCC visibility push (default)
+#endif
+#include <CLucene.h>
+#if defined(__GNUC__)
+# pragma GCC visibility pop
+#endif
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include "resultsetforquery.hxx"
+#include "databases.hxx"
+
+using namespace chelp;
+using namespace com::sun::star;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::i18n;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace {
+
+struct HitItem
+{
+ OUString m_aURL;
+ float m_fScore;
+
+ HitItem(OUString aURL, float fScore)
+ : m_aURL(std::move(aURL))
+ , m_fScore(fScore)
+ {}
+ bool operator < ( const HitItem& rHitItem ) const
+ {
+ return rHitItem.m_fScore < m_fScore;
+ }
+};
+
+}
+
+ResultSetForQuery::ResultSetForQuery( const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< XContentProvider >& xProvider,
+ const uno::Sequence< beans::Property >& seq,
+ const URLParameter& aURLParameter,
+ Databases* pDatabases )
+ : ResultSetBase( rxContext,xProvider,seq )
+{
+ Reference< XExtendedTransliteration > xTrans = Transliteration::create( rxContext );
+ Locale aLocale( aURLParameter.get_language(),
+ OUString(),
+ OUString() );
+ xTrans->loadModule(TransliterationModules_UPPERCASE_LOWERCASE,
+ aLocale );
+
+ std::vector< std::vector< OUString > > queryList;
+ {
+ sal_Int32 idx;
+ OUString query = aURLParameter.get_query();
+ while( !query.isEmpty() )
+ {
+ idx = query.indexOf( ' ' );
+ if( idx == -1 )
+ idx = query.getLength();
+
+ std::vector< OUString > currentQuery;
+ OUString tmp(query.copy( 0,idx ));
+ Sequence<sal_Int32> aSeq;
+ OUString toliterate = xTrans->transliterate(
+ tmp,0,tmp.getLength(),aSeq);
+
+ currentQuery.push_back( toliterate );
+ queryList.push_back( currentQuery );
+
+ int nCpy = 1 + idx;
+ if( nCpy >= query.getLength() )
+ query.clear();
+ else
+ query = query.copy( 1 + idx );
+ }
+ }
+
+ std::vector< OUString > aCompleteResultVector;
+ OUString scope = aURLParameter.get_scope();
+ bool bCaptionsOnly = scope == "Heading";
+ sal_Int32 hitCount = aURLParameter.get_hitCount();
+
+ IndexFolderIterator aIndexFolderIt( *pDatabases, aURLParameter.get_module(), aURLParameter.get_language() );
+ OUString idxDir;
+ bool bExtension = false;
+ std::vector< std::vector<HitItem> > aIndexFolderResultVectorVector;
+
+ bool bTemporary;
+ for (;;)
+ {
+ idxDir = aIndexFolderIt.nextIndexFolder( bExtension, bTemporary );
+ if( idxDir.isEmpty() )
+ break;
+ std::vector<HitItem> aIndexFolderResultVector;
+
+ try
+ {
+ std::vector< std::vector<HitItem> > aQueryListResultVectorVector;
+ std::set< OUString > aSet,aCurrent,aResultSet;
+
+ int nQueryListSize = queryList.size();
+ if( nQueryListSize > 1 )
+ hitCount = 2000;
+
+ for( int i = 0; i < nQueryListSize; ++i )
+ {
+ std::vector<HitItem>* pQueryResultVector;
+ if( nQueryListSize > 1 )
+ {
+ aQueryListResultVectorVector.emplace_back();
+ pQueryResultVector = &aQueryListResultVectorVector.back();
+ }
+ else
+ {
+ pQueryResultVector = &aIndexFolderResultVector;
+ }
+ pQueryResultVector->reserve( hitCount );
+
+ const std::vector< OUString >& aListItem = queryList[i];
+ OUString aNewQueryStr = aListItem[0];
+
+ std::vector<float> aScoreVector;
+ std::vector<OUString> aPathVector;
+
+ try
+ {
+ HelpSearch searcher(idxDir);
+ searcher.query(aNewQueryStr, bCaptionsOnly, aPathVector, aScoreVector);
+ }
+ catch (CLuceneError &e)
+ {
+ SAL_WARN("xmlhelp", "CLuceneError: " << e.what());
+ }
+
+ if( nQueryListSize > 1 )
+ aSet.clear();
+
+ for (size_t j = 0; j < aPathVector.size(); ++j) {
+ pQueryResultVector->push_back(HitItem(aPathVector[j], aScoreVector[j]));
+ if (nQueryListSize > 1)
+ aSet.insert(aPathVector[j]);
+ }
+
+ // intersect
+ if( nQueryListSize > 1 )
+ {
+ if( i == 0 )
+ {
+ aResultSet = aSet;
+ }
+ else
+ {
+ aCurrent = aResultSet;
+ aResultSet.clear();
+ set_intersection( aSet.begin(),aSet.end(),
+ aCurrent.begin(),aCurrent.end(),
+ inserter(aResultSet,aResultSet.begin()));
+ }
+ }
+ }
+
+ // Combine results in aIndexFolderResultVector
+ if( nQueryListSize > 1 )
+ {
+ for( int n = 0 ; n < nQueryListSize ; ++n )
+ {
+ std::vector<HitItem>& rQueryResultVector = aQueryListResultVectorVector[n];
+
+ int nItemCount = rQueryResultVector.size();
+ for( int i = 0 ; i < nItemCount ; ++i )
+ {
+ const HitItem& rItem = rQueryResultVector[ i ];
+ if( (aResultSet.find( rItem.m_aURL )) != aResultSet.end() )
+ {
+ HitItem aItemCopy( rItem );
+ aItemCopy.m_fScore /= nQueryListSize; // To get average score
+ if( n == 0 )
+ {
+ // Use first pass to create entry
+ aIndexFolderResultVector.push_back( aItemCopy );
+ }
+ else
+ {
+ // Find entry in vector
+ int nCount = aIndexFolderResultVector.size();
+ for( int j = 0 ; j < nCount ; ++j )
+ {
+ HitItem& rFindItem = aIndexFolderResultVector[ j ];
+ if( rFindItem.m_aURL == aItemCopy.m_aURL )
+ {
+ rFindItem.m_fScore += aItemCopy.m_fScore;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ sort( aIndexFolderResultVector.begin(), aIndexFolderResultVector.end() );
+ }
+
+ aIndexFolderResultVectorVector.push_back( std::move(aIndexFolderResultVector) );
+ }
+ catch (const Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("xmlhelp", "");
+ }
+
+ if( bTemporary )
+ aIndexFolderIt.deleteTempIndexFolder( idxDir );
+
+ } // Iterator
+
+
+ int nVectorCount = aIndexFolderResultVectorVector.size();
+ std::unique_ptr<std::vector<HitItem>::size_type[]> pCurrentVectorIndex(new std::vector<HitItem>::size_type[nVectorCount]);
+ for( int j = 0 ; j < nVectorCount ; ++j )
+ pCurrentVectorIndex[j] = 0;
+
+ sal_Int32 nTotalHitCount = aURLParameter.get_hitCount();
+ sal_Int32 nHitCount = 0;
+ while( nHitCount < nTotalHitCount )
+ {
+ int iVectorWithBestScore = -1;
+ float fBestScore = 0.0;
+ for( int k = 0 ; k < nVectorCount ; ++k )
+ {
+ std::vector<HitItem>& rIndexFolderVector = aIndexFolderResultVectorVector[k];
+ if( pCurrentVectorIndex[k] < rIndexFolderVector.size() )
+ {
+ const HitItem& rItem = rIndexFolderVector[ pCurrentVectorIndex[k] ];
+
+ if( fBestScore < rItem.m_fScore )
+ {
+ fBestScore = rItem.m_fScore;
+ iVectorWithBestScore = k;
+ }
+ }
+ }
+
+ if( iVectorWithBestScore == -1 ) // No item left at all
+ break;
+
+ std::vector<HitItem>& rIndexFolderVector = aIndexFolderResultVectorVector[iVectorWithBestScore];
+ const HitItem& rItem = rIndexFolderVector[ pCurrentVectorIndex[iVectorWithBestScore] ];
+
+ pCurrentVectorIndex[iVectorWithBestScore]++;
+
+ aCompleteResultVector.push_back( rItem.m_aURL );
+ ++nHitCount;
+ }
+
+ pCurrentVectorIndex.reset();
+ aIndexFolderResultVectorVector.clear();
+
+ sal_Int32 replIdx = strlen( "#HLP#" );
+ OUString replWith = "vnd.sun.star.help://";
+
+ int nResultCount = aCompleteResultVector.size();
+ for( int r = 0 ; r < nResultCount ; ++r )
+ {
+ OUString aURL = aCompleteResultVector[r];
+ OUString aResultStr = replWith + aURL.subView(replIdx);
+ m_aPath.push_back( aResultStr );
+ }
+
+ m_aItems.resize( m_aPath.size() );
+ m_aIdents.resize( m_aPath.size() );
+
+ Command aCommand;
+ aCommand.Name = "getPropertyValues";
+ aCommand.Argument <<= m_sProperty;
+
+ for( m_nRow = 0; sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aPath.size(); ++m_nRow )
+ {
+ m_aPath[m_nRow] =
+ m_aPath[m_nRow] +
+ "?Language=" +
+ aURLParameter.get_language() +
+ "&System=" +
+ aURLParameter.get_system();
+
+ uno::Reference< XContent > content = queryContent();
+ if( content.is() )
+ {
+ uno::Reference< XCommandProcessor > cmd( content,uno::UNO_QUERY );
+ cmd->execute( aCommand,0,uno::Reference< XCommandEnvironment >( nullptr ) ) >>= m_aItems[m_nRow]; //TODO: check return value of operator >>=
+ }
+ }
+ m_nRow = 0xffffffff;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx b/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx
new file mode 100644
index 0000000000..c20d64b9a7
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx
@@ -0,0 +1,47 @@
+/* -*- 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/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+#include "resultsetbase.hxx"
+#include "urlparameter.hxx"
+
+namespace chelp {
+
+ class Databases;
+
+ class ResultSetForQuery
+ : public ResultSetBase
+ {
+ public:
+
+ ResultSetForQuery( const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::ucb::XContentProvider>& xProvider,
+ const css::uno::Sequence< css::beans::Property >& seq,
+ const URLParameter& aURLParameter,
+ Databases* pDatabases );
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx b/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx
new file mode 100644
index 0000000000..b2b7e29a2d
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx
@@ -0,0 +1,69 @@
+/* -*- 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 <com/sun/star/ucb/Command.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XCommandProcessor.hpp>
+
+#include "resultsetforroot.hxx"
+#include "databases.hxx"
+
+using namespace chelp;
+using namespace com::sun::star;
+using namespace com::sun::star::ucb;
+
+
+ResultSetForRoot::ResultSetForRoot( const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< XContentProvider >& xProvider,
+ const uno::Sequence< beans::Property >& seq,
+ URLParameter const & aURLParameter,
+ Databases* pDatabases )
+ : ResultSetBase( rxContext, xProvider,seq )
+{
+ m_aPath = pDatabases->getModuleList( aURLParameter.get_language() );
+ m_aItems.resize( m_aPath.size() );
+ m_aIdents.resize( m_aPath.size() );
+
+ Command aCommand;
+ aCommand.Name = "getPropertyValues";
+ aCommand.Argument <<= m_sProperty;
+
+ for( size_t i = 0; i < m_aPath.size(); ++i )
+ {
+ m_aPath[i] =
+ "vnd.sun.star.help://" +
+ m_aPath[i] +
+ "?Language=" +
+ aURLParameter.get_language() +
+ "&System=" +
+ aURLParameter.get_system();
+
+ m_nRow = sal_Int32( i );
+
+ uno::Reference< XContent > content = queryContent();
+ if( content.is() )
+ {
+ uno::Reference< XCommandProcessor > cmd( content,uno::UNO_QUERY );
+ cmd->execute( aCommand,0,uno::Reference< XCommandEnvironment >( nullptr ) ) >>= m_aItems[i]; //TODO: check return value of operator >>=
+ }
+ m_nRow = 0xffffffff;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx b/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx
new file mode 100644
index 0000000000..58e04407e4
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx
@@ -0,0 +1,47 @@
+/* -*- 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/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+#include "resultsetbase.hxx"
+#include "urlparameter.hxx"
+
+namespace chelp {
+
+ class Databases;
+
+ class ResultSetForRoot
+ : public ResultSetBase
+ {
+ public:
+
+ ResultSetForRoot( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::ucb::XContentProvider >& xProvider,
+ const css::uno::Sequence< css::beans::Property >& seq,
+ URLParameter const & aURLParameter,
+ Databases* pDatabases );
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/urlparameter.cxx b/xmlhelp/source/cxxhelp/provider/urlparameter.cxx
new file mode 100644
index 0000000000..c242c74add
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/urlparameter.cxx
@@ -0,0 +1,983 @@
+/* -*- 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 <string.h>
+#include <osl/thread.h>
+#include <osl/file.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/processfactory.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/character.hxx>
+#include <o3tl/string_view.hxx>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/security.h>
+#include "db.hxx"
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+
+#include "urlparameter.hxx"
+#include "databases.hxx"
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+using namespace cppu;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+using namespace chelp;
+
+
+URLParameter::URLParameter( const OUString& aURL,
+ Databases* pDatabases )
+ : m_pDatabases( pDatabases ),
+ m_aURL( aURL )
+{
+ init();
+ parse();
+}
+
+
+bool URLParameter::isErrorDocument()
+{
+ bool bErrorDoc = false;
+
+ if( isFile() )
+ {
+ Reference< XHierarchicalNameAccess > xNA =
+ m_pDatabases->findJarFileForPath( get_jar(), get_language(), get_path() );
+ bErrorDoc = !xNA.is();
+ }
+
+ return bErrorDoc;
+}
+
+
+OString URLParameter::getByName( const char* par )
+{
+ OUString val;
+
+ if( strcmp( par,"Program" ) == 0 )
+ val = get_program();
+ else if( strcmp( par,"Database" ) == 0 )
+ val = get_module();
+ else if( strcmp( par,"DatabasePar" ) == 0 )
+ val = get_dbpar();
+ else if( strcmp( par,"Id" ) == 0 )
+ val = get_id();
+ else if( strcmp( par,"Path" ) == 0 )
+ val = get_path();
+ else if( strcmp( par,"Language" ) == 0 )
+ val = get_language();
+ else if( strcmp( par,"System" ) == 0 )
+ val = get_system();
+ else if( strcmp( par,"HelpPrefix" ) == 0 )
+ val = m_aPrefix;
+
+ return OUStringToOString( val, RTL_TEXTENCODING_UTF8 );
+}
+
+
+OUString const & URLParameter::get_id()
+{
+ if( m_aId == "start" )
+ { // module is set
+ StaticModuleInformation* inf =
+ m_pDatabases->getStaticInformationForModule( get_module(),
+ get_language() );
+ if( inf )
+ m_aId = inf->get_id();
+ }
+
+ return m_aId;
+}
+
+OUString URLParameter::get_tag()
+{
+ if( isFile() )
+ return get_the_tag();
+ else
+ return m_aTag;
+}
+
+
+OUString URLParameter::get_title()
+{
+ if( isFile() )
+ return get_the_title();
+ else if( !m_aModule.isEmpty() )
+ {
+ StaticModuleInformation* inf =
+ m_pDatabases->getStaticInformationForModule( get_module(),
+ get_language() );
+ if( inf )
+ m_aTitle = inf->get_title();
+ }
+ else // This must be the root
+ m_aTitle = "root";
+
+ return m_aTitle;
+}
+
+
+OUString const & URLParameter::get_language() const
+{
+ return m_aLanguage;
+}
+
+
+OUString const & URLParameter::get_program()
+{
+ if( m_aProgram.isEmpty() )
+ {
+ StaticModuleInformation* inf =
+ m_pDatabases->getStaticInformationForModule( get_module(),
+ get_language() );
+ if( inf )
+ m_aProgram = inf->get_program();
+ }
+ return m_aProgram;
+}
+
+
+void URLParameter::init()
+{
+ m_bHelpDataFileRead = false;
+ m_bUseDB = true;
+ m_nHitCount = 100; // The default maximum hitcount
+}
+
+
+OUString URLParameter::get_the_tag()
+{
+ if(m_bUseDB) {
+ if( ! m_bHelpDataFileRead )
+ readHelpDataFile();
+
+ m_bHelpDataFileRead = true;
+
+ return m_aTag;
+ }
+ else
+ return OUString();
+}
+
+
+OUString const & URLParameter::get_path()
+{
+ if(m_bUseDB) {
+ if( ! m_bHelpDataFileRead )
+ readHelpDataFile();
+ m_bHelpDataFileRead = true;
+
+ return m_aPath;
+ }
+ else
+ return get_id();
+}
+
+
+OUString URLParameter::get_the_title()
+{
+ if(m_bUseDB) {
+ if( ! m_bHelpDataFileRead )
+ readHelpDataFile();
+ m_bHelpDataFileRead = true;
+
+ return m_aTitle;
+ }
+ else
+ return OUString();
+}
+
+
+OUString URLParameter::get_jar()
+{
+ if(m_bUseDB) {
+ if( ! m_bHelpDataFileRead )
+ readHelpDataFile();
+ m_bHelpDataFileRead = true;
+
+ return m_aJar;
+ }
+ else
+ return get_module() + ".jar";
+}
+
+
+void URLParameter::readHelpDataFile()
+{
+ if( get_id().isEmpty() )
+ return;
+
+ OUString aModule = get_module();
+ OUString aLanguage = get_language();
+
+ DataBaseIterator aDbIt( *m_pDatabases, aModule, aLanguage, false );
+ bool bSuccess = false;
+
+ const char* pData = nullptr;
+
+ helpdatafileproxy::HDFData aHDFData;
+ OUString aExtensionPath;
+ OUString aExtensionRegistryPath;
+ while( true )
+ {
+ helpdatafileproxy::Hdf* pHdf = aDbIt.nextHdf( &aExtensionPath, &aExtensionRegistryPath );
+ if( !pHdf )
+ break;
+
+ OString keyStr = OUStringToOString( m_aId,RTL_TEXTENCODING_UTF8 );
+ bSuccess = pHdf->getValueForKey( keyStr, aHDFData );
+ if( bSuccess )
+ {
+ pData = aHDFData.getData();
+ break;
+ }
+ }
+
+ if( !bSuccess )
+ return;
+
+ DbtToStringConverter converter( pData );
+ m_aTitle = converter.getTitle();
+ m_pDatabases->replaceName( m_aTitle );
+ m_aPath = converter.getFile();
+ m_aJar = converter.getDatabase();
+ if( !aExtensionPath.isEmpty() )
+ {
+ m_aJar = "?" + aExtensionPath + "?" + m_aJar;
+ m_aExtensionRegistryPath = aExtensionRegistryPath;
+ }
+ m_aTag = converter.getHash();
+}
+
+
+// Class encapsulating the transformation of the XInputStream to XHTML
+
+namespace {
+
+class InputStreamTransformer
+ : public OWeakObject,
+ public XInputStream,
+ public XSeekable
+{
+public:
+
+ InputStreamTransformer( URLParameter* urlParam,
+ Databases* pDatatabases,
+ bool isRoot );
+
+ virtual Any SAL_CALL queryInterface( const Type& rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead ) override;
+
+ virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead ) override;
+
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+
+ virtual sal_Int32 SAL_CALL available() override;
+
+ virtual void SAL_CALL closeInput() override;
+
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+
+ virtual sal_Int64 SAL_CALL getPosition() override;
+
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+ void addToBuffer( const char* buffer,int len );
+
+ OStringBuffer const & getData() const { return buffer; }
+
+private:
+
+ std::mutex m_aMutex;
+
+ int pos;
+ OStringBuffer buffer;
+};
+
+}
+
+void URLParameter::open( const Reference< XOutputStream >& xDataSink )
+{
+ if( ! xDataSink.is() )
+ return;
+
+ // a standard document or else an active help text, plug in the new input stream
+ rtl::Reference<InputStreamTransformer> p(new InputStreamTransformer( this,m_pDatabases,isRoot() ));
+ try
+ {
+ xDataSink->writeBytes( Sequence< sal_Int8 >( reinterpret_cast<const sal_Int8*>(p->getData().getStr()), p->getData().getLength() ) );
+ }
+ catch( const Exception& )
+ {
+ }
+ p.clear();
+ xDataSink->closeOutput();
+}
+
+
+void URLParameter::open( const Reference< XActiveDataSink >& xDataSink )
+{
+ // a standard document or else an active help text, plug in the new input stream
+ xDataSink->setInputStream( new InputStreamTransformer( this,m_pDatabases,isRoot() ) );
+}
+
+
+void URLParameter::parse()
+{
+ m_aExpr = m_aURL;
+
+ sal_Int32 lstIdx = m_aExpr.lastIndexOf( '#' );
+ if( lstIdx != -1 )
+ m_aExpr = m_aExpr.copy( 0,lstIdx );
+
+ if( ! scheme() ||
+ ! name( module() ) ||
+ ! query() ||
+ m_aLanguage.isEmpty() ||
+ m_aSystem.isEmpty() )
+ throw css::ucb::IllegalIdentifierException();
+}
+
+
+bool URLParameter::scheme()
+{
+ // Correct extension help links as sometimes the
+ // module is missing resulting in a malformed URL
+ if( m_aExpr.startsWith("vnd.sun.star.help:///") )
+ {
+ sal_Int32 nLen = m_aExpr.getLength();
+ std::u16string_view aLastStr =
+ m_aExpr.subView(sal::static_int_cast<sal_uInt32>(nLen) - 6);
+ if( aLastStr == u"DbPAR=" )
+ {
+ m_aExpr = OUString::Concat(m_aExpr.subView( 0, 20 )) +
+ "shared" +
+ m_aExpr.subView( 20 ) +
+ "shared";
+ }
+ }
+
+ for( sal_Int32 nPrefixLen = 20 ; nPrefixLen >= 18 ; --nPrefixLen )
+ {
+ if( m_aExpr.matchAsciiL( "vnd.sun.star.help://", nPrefixLen ) )
+ {
+ m_aExpr = m_aExpr.copy( nPrefixLen );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool URLParameter::module()
+{
+ sal_Int32 idx = 0,length = m_aExpr.getLength();
+
+ while( idx < length && rtl::isAsciiAlphanumeric( m_aExpr[idx] ) )
+ ++idx;
+
+ if( idx != 0 )
+ {
+ m_aModule = m_aExpr.copy( 0,idx );
+ m_aExpr = m_aExpr.copy( idx );
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool URLParameter::name( bool modulePresent )
+{
+ // if modulepresent, a name may be present, but must not
+
+ sal_Int32 length = m_aExpr.getLength();
+
+ if( length != 0 && m_aExpr[0] == '/' )
+ {
+ sal_Int32 idx = 1;
+ while( idx < length && m_aExpr[idx] != '?' )
+ ++idx;
+
+ if( idx != 1 && ! modulePresent )
+ return false;
+ else
+ {
+ m_aId = m_aExpr.copy( 1,idx-1 );
+ m_aExpr = m_aExpr.copy( idx );
+ }
+ }
+
+ return true;
+}
+
+
+bool URLParameter::query()
+{
+ OUString query_;
+
+ if( m_aExpr.isEmpty() )
+ return true;
+ else if( m_aExpr[0] == '?' )
+ query_ = o3tl::trim(m_aExpr.subView( 1 ));
+ else
+ return false;
+
+
+ bool ret = true;
+ sal_Int32 delimIdx,equalIdx;
+ OUString parameter,value;
+
+ while( !query_.isEmpty() )
+ {
+ delimIdx = query_.indexOf( '&' );
+ equalIdx = query_.indexOf( '=' );
+ parameter = o3tl::trim(query_.subView( 0,equalIdx ));
+ if( delimIdx == -1 )
+ {
+ value = o3tl::trim(query_.subView( equalIdx + 1 ));
+ query_.clear();
+ }
+ else
+ {
+ value = o3tl::trim(query_.subView( equalIdx+1,delimIdx - equalIdx - 1 ));
+ query_ = o3tl::trim(query_.subView( delimIdx+1 ));
+ }
+
+ if( parameter == "Language" )
+ m_aLanguage = value;
+ else if( parameter == "Device" )
+ ;
+ else if( parameter == "Program" )
+ m_aProgram = value;
+ else if( parameter == "Eid" )
+ m_aEid = value;
+ else if( parameter == "UseDB" )
+ m_bUseDB = value != "no";
+ else if( parameter == "DbPAR" )
+ m_aDbPar = value;
+ else if( parameter == "Query" )
+ {
+ if( m_aQuery.isEmpty() )
+ m_aQuery = value;
+ else
+ m_aQuery += " " + value;
+ }
+ else if( parameter == "Scope" )
+ m_aScope = value;
+ else if( parameter == "System" )
+ m_aSystem = value;
+ else if( parameter == "HelpPrefix" )
+ m_aPrefix = rtl::Uri::decode(
+ value,
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 );
+ else if( parameter == "HitCount" )
+ m_nHitCount = value.toInt32();
+ else if( parameter == "Active" )
+ m_aActive = value;
+ else if( parameter == "Version" )
+ ; // ignored (but accepted) in the built-in help, useful only for the online help
+ else
+ ret = false;
+ }
+
+ return ret;
+}
+
+namespace {
+
+struct UserData {
+
+ UserData( URLParameter* pInitial,
+ Databases* pDatabases )
+ : m_pDatabases( pDatabases ),
+ m_pInitial( pInitial )
+ {
+ }
+
+ Databases* m_pDatabases;
+ URLParameter* m_pInitial;
+};
+
+}
+
+static UserData *ugblData = nullptr;
+
+extern "C" {
+
+static int
+fileMatch(const char * URI) {
+ if ((URI != nullptr) && !strncmp(URI, "file:/", 6))
+ return 1;
+ return 0;
+}
+
+static int
+zipMatch(const char * URI) {
+ if ((URI != nullptr) && !strncmp(URI, "vnd.sun.star.zip:/", 18))
+ return 1;
+ return 0;
+}
+
+static int
+helpMatch(const char * URI) {
+ if ((URI != nullptr) && !strncmp(URI, "vnd.sun.star.help:/", 19))
+ return 1;
+ return 0;
+}
+
+static void *
+fileOpen(const char *URI) {
+ osl::File *pRet = new osl::File(OUString(URI, strlen(URI), RTL_TEXTENCODING_UTF8));
+ (void)pRet->open(osl_File_OpenFlag_Read);
+ return pRet;
+}
+
+static void *
+zipOpen(SAL_UNUSED_PARAMETER const char *) {
+ OUString language,jar,path;
+
+ if( !ugblData->m_pInitial->get_eid().isEmpty() )
+ return new Reference<XHierarchicalNameAccess>;
+ else
+ {
+ jar = ugblData->m_pInitial->get_jar();
+ language = ugblData->m_pInitial->get_language();
+ path = ugblData->m_pInitial->get_path();
+ }
+
+ Reference< XHierarchicalNameAccess > xNA =
+ ugblData->m_pDatabases->findJarFileForPath( jar, language, path );
+
+ Reference< XInputStream > xInputStream;
+
+ if( xNA.is() )
+ {
+ try
+ {
+ Any aEntry = xNA->getByHierarchicalName( path );
+ Reference< XActiveDataSink > xSink;
+ if( ( aEntry >>= xSink ) && xSink.is() )
+ xInputStream = xSink->getInputStream();
+ }
+ catch ( NoSuchElementException & )
+ {
+ }
+ }
+
+ if( xInputStream.is() )
+ {
+ return new Reference<XInputStream>(xInputStream);
+ }
+ return nullptr;
+}
+
+static void *
+helpOpen(const char * URI) {
+ OUString language,jar,path;
+
+ URLParameter urlpar( OUString::createFromAscii( URI ),
+ ugblData->m_pDatabases );
+
+ jar = urlpar.get_jar();
+ language = urlpar.get_language();
+ path = urlpar.get_path();
+
+ Reference< XHierarchicalNameAccess > xNA =
+ ugblData->m_pDatabases->findJarFileForPath( jar, language, path );
+
+ Reference< XInputStream > xInputStream;
+
+ if( xNA.is() )
+ {
+ try
+ {
+ Any aEntry = xNA->getByHierarchicalName( path );
+ Reference< XActiveDataSink > xSink;
+ if( ( aEntry >>= xSink ) && xSink.is() )
+ xInputStream = xSink->getInputStream();
+ }
+ catch ( NoSuchElementException & )
+ {
+ }
+ }
+
+ if( xInputStream.is() )
+ return new Reference<XInputStream>(xInputStream);
+ return nullptr;
+}
+
+static int
+helpRead(void * context, char * buffer, int len) {
+ Reference< XInputStream > *pRef = static_cast<Reference< XInputStream >*>(context);
+
+ Sequence< sal_Int8 > aSeq;
+ len = (*pRef)->readBytes( aSeq,len);
+ memcpy(buffer, aSeq.getConstArray(), len);
+
+ return len;
+}
+
+static int
+zipRead(void * context, char * buffer, int len) {
+ return helpRead(context, buffer, len);
+}
+
+static int
+fileRead(void * context, char * buffer, int len) {
+ int nRead = 0;
+ osl::File *pFile = static_cast<osl::File*>(context);
+ if (pFile)
+ {
+ sal_uInt64 uRead = 0;
+ if (osl::FileBase::E_None == pFile->read(buffer, len, uRead))
+ nRead = static_cast<int>(uRead);
+ }
+ return nRead;
+}
+
+static int
+uriClose(void * context) {
+ Reference< XInputStream > *pRef = static_cast<Reference< XInputStream >*>(context);
+ delete pRef;
+ return 0;
+}
+
+static int
+fileClose(void * context) {
+ osl::File *pFile = static_cast<osl::File*>(context);
+ if (pFile)
+ {
+ pFile->close();
+ delete pFile;
+ }
+ return 0;
+}
+
+} // extern "C"
+
+InputStreamTransformer::InputStreamTransformer( URLParameter* urlParam,
+ Databases* pDatabases,
+ bool isRoot )
+ : pos( 0 )
+{
+ if( isRoot )
+ {
+ buffer.setLength(0);
+ pDatabases->cascadingStylesheet( urlParam->get_language(),
+ buffer );
+ }
+ else if( urlParam->isActive() )
+ {
+ buffer.setLength(0);
+ pDatabases->setActiveText( urlParam->get_module(),
+ urlParam->get_language(),
+ urlParam->get_id(),
+ buffer );
+ }
+ else
+ {
+ UserData userData( urlParam,pDatabases );
+
+ // Uses the implementation detail, that OString::getStr returns a zero terminated character-array
+
+ const char* parameter[47];
+ OString parString[46];
+ int last = 0;
+
+ parString[last++] = "Program"_ostr;
+ OString aPureProgramm( urlParam->getByName( "Program" ) );
+ parString[last++] = "'" + aPureProgramm + "'";
+ parString[last++] = "Database"_ostr;
+ parString[last++] = "'" + urlParam->getByName( "DatabasePar" ) + "'";
+ parString[last++] = "Id"_ostr;
+ parString[last++] = "'" + urlParam->getByName( "Id" ) + "'";
+ parString[last++] = "Path"_ostr;
+ OString aPath( urlParam->getByName( "Path" ) );
+ parString[last++] = "'" + aPath + "'";
+
+ OString aPureLanguage = urlParam->getByName( "Language" );
+ parString[last++] = "Language"_ostr;
+ parString[last++] = "'" + aPureLanguage + "'";
+ parString[last++] = "System"_ostr;
+ parString[last++] = "'" + urlParam->getByName( "System" ) + "'";
+ parString[last++] = "productname"_ostr;
+ parString[last++] = "'" + OUStringToOString(
+ pDatabases->getProductName(),
+ RTL_TEXTENCODING_UTF8 ) + "'";
+ parString[last++] = "productversion"_ostr;
+ parString[last++] = "'" +
+ OUStringToOString( pDatabases->getProductVersion(),
+ RTL_TEXTENCODING_UTF8 ) + "'";
+
+ parString[last++] = "imgtheme"_ostr;
+ parString[last++] = "'" + pDatabases->getImageTheme() + "'";
+ parString[last++] = "hp"_ostr;
+ parString[last++] = "'" + urlParam->getByName( "HelpPrefix" ) + "'";
+
+ if( !parString[last-1].isEmpty() )
+ {
+ parString[last++] = "sm"_ostr;
+ parString[last++] = "'vnd.sun.star.help%3A%2F%2F'"_ostr;
+ parString[last++] = "qm"_ostr;
+ parString[last++] = "'%3F'"_ostr;
+ parString[last++] = "es"_ostr;
+ parString[last++] = "'%3D'"_ostr;
+ parString[last++] = "am"_ostr;
+ parString[last++] = "'%26'"_ostr;
+ parString[last++] = "cl"_ostr;
+ parString[last++] = "'%3A'"_ostr;
+ parString[last++] = "sl"_ostr;
+ parString[last++] = "'%2F'"_ostr;
+ parString[last++] = "hm"_ostr;
+ parString[last++] = "'%23'"_ostr;
+ parString[last++] = "cs"_ostr;
+ parString[last++] = "'css'"_ostr;
+
+ parString[last++] = "vendorname"_ostr;
+ parString[last++] = "''"_ostr;
+ parString[last++] = "vendorversion"_ostr;
+ parString[last++] = "''"_ostr;
+ parString[last++] = "vendorshort"_ostr;
+ parString[last++] = "''"_ostr;
+ }
+
+ // Do we need to add extension path?
+ OUString aExtensionPath;
+ OUString aJar = urlParam->get_jar();
+
+ bool bAddExtensionPath = false;
+ OUString aExtensionRegistryPath;
+ sal_Int32 nQuestionMark1 = aJar.indexOf( '?' );
+ sal_Int32 nQuestionMark2 = aJar.lastIndexOf( '?' );
+ if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 )
+ {
+ aExtensionPath = aJar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
+ aExtensionRegistryPath = urlParam->get_ExtensionRegistryPath();
+ bAddExtensionPath = true;
+ }
+ else
+ {
+ // Path not yet specified, search directly
+ Reference< XHierarchicalNameAccess > xNA = pDatabases->findJarFileForPath
+ ( aJar, urlParam->get_language(), urlParam->get_path(), &aExtensionPath, &aExtensionRegistryPath );
+ if( xNA.is() && !aExtensionPath.isEmpty() )
+ bAddExtensionPath = true;
+ }
+
+ if( bAddExtensionPath )
+ {
+ Reference< XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+
+ OUString aOUExpandedExtensionPath = Databases::expandURL( aExtensionRegistryPath, xContext );
+ OString aExpandedExtensionPath = OUStringToOString( aOUExpandedExtensionPath, osl_getThreadTextEncoding() );
+
+ parString[last++] = "ExtensionPath"_ostr;
+ parString[last++] = "'" + aExpandedExtensionPath + "'";
+
+ // ExtensionId
+ OString aPureExtensionId;
+ sal_Int32 iSlash = aPath.indexOf( '/' );
+ if( iSlash != -1 )
+ aPureExtensionId = aPath.copy( 0, iSlash );
+
+ parString[last++] = "ExtensionId"_ostr;
+ parString[last++] = "'" + aPureExtensionId + "'";
+ }
+
+ for( int i = 0; i < last; ++i )
+ parameter[i] = parString[i].getStr();
+ parameter[last] = nullptr;
+
+ OUString xslURL = pDatabases->getInstallPathAsURL();
+
+ OString xslURLascii = OUStringToOString(
+ xslURL,
+ RTL_TEXTENCODING_UTF8) +
+ "main_transform.xsl";
+
+ ugblData = &userData;
+
+ xmlInitParser();
+ xmlRegisterInputCallbacks(zipMatch, zipOpen, zipRead, uriClose);
+ xmlRegisterInputCallbacks(helpMatch, helpOpen, helpRead, uriClose);
+ xmlRegisterInputCallbacks(fileMatch, fileOpen, fileRead, fileClose);
+
+ xsltStylesheetPtr cur =
+ xsltParseStylesheetFile(reinterpret_cast<const xmlChar *>(xslURLascii.getStr()));
+
+ xmlDocPtr doc = xmlParseFile("vnd.sun.star.zip:/");
+
+ xmlDocPtr res = nullptr;
+ xsltTransformContextPtr transformContext = xsltNewTransformContext(cur, doc);
+ if (transformContext)
+ {
+ xsltSecurityPrefsPtr securityPrefs = xsltNewSecurityPrefs();
+ if (securityPrefs)
+ {
+ xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityAllow);
+ if (xsltSetCtxtSecurityPrefs(securityPrefs, transformContext) == 0)
+ {
+ res = xsltApplyStylesheetUser(cur, doc, parameter, nullptr, nullptr, transformContext);
+ if (res)
+ {
+ xmlChar *doc_txt_ptr=nullptr;
+ int doc_txt_len;
+ xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, res, cur);
+ addToBuffer(reinterpret_cast<char*>(doc_txt_ptr), doc_txt_len);
+ xmlFree(doc_txt_ptr);
+ }
+ }
+ xsltFreeSecurityPrefs(securityPrefs);
+ }
+ xsltFreeTransformContext(transformContext);
+ }
+ xmlPopInputCallbacks(); //filePatch
+ xmlPopInputCallbacks(); //helpPatch
+ xmlPopInputCallbacks(); //zipMatch
+ xmlFreeDoc(res);
+ xmlFreeDoc(doc);
+ xsltFreeStylesheet(cur);
+ }
+}
+
+
+Any SAL_CALL InputStreamTransformer::queryInterface( const Type& rType )
+{
+ Any aRet = ::cppu::queryInterface( rType,
+ static_cast< XInputStream* >(this),
+ static_cast< XSeekable* >(this) );
+
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+
+void SAL_CALL InputStreamTransformer::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL InputStreamTransformer::release() noexcept
+{
+ OWeakObject::release();
+}
+
+
+sal_Int32 SAL_CALL InputStreamTransformer::readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ int curr,available_ = buffer.getLength() - pos;
+ if( nBytesToRead <= available_ )
+ curr = nBytesToRead;
+ else
+ curr = available_;
+
+ if( 0 <= curr && aData.getLength() < curr )
+ aData.realloc( curr );
+
+ std::copy_n(buffer.getStr() + pos, curr, aData.getArray());
+ pos += curr;
+
+ return std::max(curr, 0);
+}
+
+
+sal_Int32 SAL_CALL InputStreamTransformer::readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead )
+{
+ return readBytes( aData,nMaxBytesToRead );
+}
+
+
+void SAL_CALL InputStreamTransformer::skipBytes( sal_Int32 nBytesToSkip )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ while( nBytesToSkip-- ) ++pos;
+}
+
+
+sal_Int32 SAL_CALL InputStreamTransformer::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return std::min<sal_Int64>(SAL_MAX_INT32, buffer.getLength() - pos);
+}
+
+
+void SAL_CALL InputStreamTransformer::closeInput()
+{
+}
+
+
+void SAL_CALL InputStreamTransformer::seek( sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if( location < 0 )
+ throw IllegalArgumentException();
+
+ pos = sal::static_int_cast<sal_Int32>( location );
+
+ if( pos > buffer.getLength() )
+ pos = buffer.getLength();
+}
+
+
+sal_Int64 SAL_CALL InputStreamTransformer::getPosition()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return sal_Int64( pos );
+}
+
+
+sal_Int64 SAL_CALL InputStreamTransformer::getLength()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ return buffer.getLength();
+}
+
+
+void InputStreamTransformer::addToBuffer( const char* buffer_,int len_ )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ buffer.append( buffer_, len_ );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/provider/urlparameter.hxx b/xmlhelp/source/cxxhelp/provider/urlparameter.hxx
new file mode 100644
index 0000000000..3c6d02430b
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/provider/urlparameter.hxx
@@ -0,0 +1,223 @@
+/* -*- 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 <rtl/string.hxx>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+namespace chelp {
+
+
+ class Databases;
+
+
+ class DbtToStringConverter
+ {
+ public:
+
+ explicit DbtToStringConverter( const char* ptr )
+ : m_ptr( ptr )
+ {
+ }
+
+ OUString getHash() const
+ {
+ if( m_ptr )
+ {
+ sal_Int32 sizeOfFile = static_cast<sal_Int32>(m_ptr[0]);
+ OUString Hash( m_ptr+1,sizeOfFile,RTL_TEXTENCODING_UTF8 );
+ sal_Int32 idx;
+ if( ( idx = Hash.indexOf( u'#' ) ) != -1 )
+ return Hash.copy( 1+idx );
+ }
+ return OUString();
+ }
+
+
+ OUString getFile() const
+ {
+ if( ! m_ptr )
+ return OUString();
+
+ sal_Int32 sizeOfFile = static_cast<sal_Int32>(m_ptr[0]);
+ OUString File( m_ptr+1,sizeOfFile,RTL_TEXTENCODING_UTF8 );
+ sal_Int32 idx;
+ if( ( idx = File.indexOf( u'#' ) ) != -1 )
+ return File.copy( 0,idx );
+ else
+ return File;
+ }
+
+
+ OUString getDatabase() const
+ {
+ if( ! m_ptr )
+ return OUString();
+
+ sal_Int32 sizeOfDatabase = static_cast<int>(m_ptr[ 1+ static_cast<sal_Int32>(m_ptr[0]) ]);
+ return OUString( m_ptr + 2 + static_cast<sal_Int32>(m_ptr[0]),sizeOfDatabase,RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ OUString getTitle() const
+ {
+ if( ! m_ptr )
+ return OUString();
+
+ //fdo#82025 - use strlen instead of stored length byte to determine string len
+ //There is a one byte length field at m_ptr[2 + m_ptr[0] + m_ptr[1
+ //+ m_ptr[0]]] but by default char is signed so anything larger
+ //than 127 defaults to a negative value, casting it would allow up
+ //to 255 but instead make use of the null termination to avoid
+ //running into a later problem with strings >= 255
+ const char* pTitle = m_ptr + 3 + m_ptr[0] + static_cast<sal_Int32>(m_ptr[ 1+ static_cast<sal_Int32>(m_ptr[0]) ]);
+
+ return OStringToOUString(pTitle, RTL_TEXTENCODING_UTF8);
+ }
+
+
+ private:
+
+ const char* m_ptr;
+
+ };
+
+
+ class URLParameter
+ {
+ public:
+ /// @throws css::ucb::IllegalIdentifierException
+ URLParameter( const OUString& aURL,
+ Databases* pDatabases );
+
+ bool isActive() const { return !m_aActive.isEmpty() && m_aActive == "true"; }
+ bool isQuery() const { return m_aId.isEmpty() && !m_aQuery.isEmpty(); }
+ bool isFile() const { return !m_aId.isEmpty(); }
+ bool isModule() const { return m_aId.isEmpty() && !m_aModule.isEmpty(); }
+ bool isRoot() const { return m_aModule.isEmpty(); }
+ bool isErrorDocument();
+
+ OUString const & get_id();
+
+ OUString get_tag();
+
+ // Not called for a directory
+
+ OUString const & get_path();
+
+ const OUString& get_eid() const { return m_aEid; }
+
+ OUString get_title();
+
+ OUString get_jar();
+
+ const OUString& get_ExtensionRegistryPath() const { return m_aExtensionRegistryPath; }
+
+ const OUString& get_module() const { return m_aModule; }
+
+ OUString const & get_dbpar() const
+ {
+ if( !m_aDbPar.isEmpty() )
+ return m_aDbPar;
+ else
+ return m_aModule;
+ }
+
+ OUString const & get_language() const;
+
+ OUString const & get_program();
+
+ const OUString& get_query() const { return m_aQuery; }
+
+ const OUString& get_scope() const { return m_aScope; }
+
+ const OUString& get_system() const { return m_aSystem; }
+
+ sal_Int32 get_hitCount() const { return m_nHitCount; }
+
+ OString getByName( const char* par );
+
+ void open( const css::uno::Reference< css::io::XActiveDataSink >& xDataSink );
+
+ void open( const css::uno::Reference< css::io::XOutputStream >& xDataSink );
+
+ private:
+
+ Databases* m_pDatabases;
+
+ bool m_bHelpDataFileRead;
+ bool m_bUseDB;
+
+ OUString m_aURL;
+
+ OUString m_aTag;
+ OUString m_aId;
+ OUString m_aPath;
+ OUString m_aModule;
+ OUString m_aTitle;
+ OUString m_aJar;
+ OUString m_aExtensionRegistryPath;
+ OUString m_aEid;
+ OUString m_aDbPar;
+
+ OUString m_aLanguage;
+
+ OUString m_aPrefix;
+ OUString m_aProgram;
+ OUString m_aSystem;
+ OUString m_aActive;
+
+ OUString m_aQuery;
+ OUString m_aScope;
+
+ OUString m_aExpr;
+
+ sal_Int32 m_nHitCount; // The default maximum hitcount
+
+
+ // private methods
+
+ void init();
+
+ OUString get_the_tag();
+
+ OUString get_the_title();
+
+ void readHelpDataFile();
+
+ /// @throws css::ucb::IllegalIdentifierException
+ void parse();
+
+ bool scheme();
+
+ bool module();
+
+ bool name( bool modulePresent );
+
+ bool query();
+
+ }; // end class URLParameter
+
+
+} // end namespace chelp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */