summaryrefslogtreecommitdiffstats
path: root/xmlhelp
diff options
context:
space:
mode:
Diffstat (limited to 'xmlhelp')
-rw-r--r--xmlhelp/IwyuFilter_xmlhelp.yaml9
-rw-r--r--xmlhelp/Library_ucpchelp1.mk62
-rw-r--r--xmlhelp/Makefile14
-rw-r--r--xmlhelp/Module_xmlhelp.mk17
-rw-r--r--xmlhelp/Package_helpxsl.mk18
-rw-r--r--xmlhelp/README.md1
-rw-r--r--xmlhelp/source/cxxhelp/inc/tvfactory.hxx77
-rw-r--r--xmlhelp/source/cxxhelp/inc/tvread.hxx293
-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
-rw-r--r--xmlhelp/source/cxxhelp/test/abidebug.hxx27
-rw-r--r--xmlhelp/source/cxxhelp/test/makefile.mk60
-rw-r--r--xmlhelp/source/cxxhelp/test/searchdemo.cxx107
-rw-r--r--xmlhelp/source/treeview/tvfactory.cxx132
-rw-r--r--xmlhelp/source/treeview/tvread.cxx1134
-rw-r--r--xmlhelp/util/compact.xsl50
-rw-r--r--xmlhelp/util/embed.xsl100
-rw-r--r--xmlhelp/util/idxcaption.xsl51
-rw-r--r--xmlhelp/util/idxcontent.xsl131
-rw-r--r--xmlhelp/util/main_transform.xsl933
-rw-r--r--xmlhelp/util/ucpchelp1.component32
41 files changed, 9993 insertions, 0 deletions
diff --git a/xmlhelp/IwyuFilter_xmlhelp.yaml b/xmlhelp/IwyuFilter_xmlhelp.yaml
new file mode 100644
index 0000000000..a4c4ad7ac9
--- /dev/null
+++ b/xmlhelp/IwyuFilter_xmlhelp.yaml
@@ -0,0 +1,9 @@
+---
+assumeFilename: xmlhelp/source/cxxhelp/provider/content.cxx
+excludelist:
+ xmlhelp/source/cxxhelp/provider/content.cxx:
+ # Needed for CPPU_TYPE_REF macro
+ - com/sun/star/ucb/XCommandInfo.hpp
+ xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx:
+ # Needed for CLuceneError as a pass-through header
+ - CLucene.h
diff --git a/xmlhelp/Library_ucpchelp1.mk b/xmlhelp/Library_ucpchelp1.mk
new file mode 100644
index 0000000000..087a1da2de
--- /dev/null
+++ b/xmlhelp/Library_ucpchelp1.mk
@@ -0,0 +1,62 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,ucpchelp1))
+
+$(eval $(call gb_Library_set_componentfile,ucpchelp1,xmlhelp/util/ucpchelp1,services))
+
+$(eval $(call gb_Library_set_include,ucpchelp1,\
+ -I$(SRCDIR)/xmlhelp/source/cxxhelp/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_use_custom_headers,ucpchelp1, \
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_sdk_api,ucpchelp1))
+
+$(eval $(call gb_Library_use_externals,ucpchelp1,\
+ boost_headers \
+ clucene \
+ expat \
+ libxml2 \
+ libxslt \
+))
+
+$(eval $(call gb_Library_use_libraries,ucpchelp1,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ helplinker \
+ sal \
+ tl \
+ ucbhelper \
+ utl \
+ i18nlangtag \
+ vcl \
+))
+
+$(eval $(call gb_Library_add_exception_objects,ucpchelp1,\
+ xmlhelp/source/cxxhelp/provider/content \
+ xmlhelp/source/cxxhelp/provider/contentcaps \
+ xmlhelp/source/cxxhelp/provider/databases \
+ xmlhelp/source/cxxhelp/provider/db \
+ xmlhelp/source/cxxhelp/provider/inputstream \
+ xmlhelp/source/cxxhelp/provider/provider \
+ xmlhelp/source/cxxhelp/provider/resultset \
+ xmlhelp/source/cxxhelp/provider/resultsetbase \
+ xmlhelp/source/cxxhelp/provider/resultsetforquery \
+ xmlhelp/source/cxxhelp/provider/resultsetforroot \
+ xmlhelp/source/cxxhelp/provider/urlparameter \
+ xmlhelp/source/treeview/tvfactory \
+ xmlhelp/source/treeview/tvread \
+))
+
+# vim: set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/xmlhelp/Makefile b/xmlhelp/Makefile
new file mode 100644
index 0000000000..0997e62848
--- /dev/null
+++ b/xmlhelp/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/xmlhelp/Module_xmlhelp.mk b/xmlhelp/Module_xmlhelp.mk
new file mode 100644
index 0000000000..a1f79df747
--- /dev/null
+++ b/xmlhelp/Module_xmlhelp.mk
@@ -0,0 +1,17 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Module_Module,xmlhelp))
+
+$(eval $(call gb_Module_add_targets,xmlhelp,\
+ Library_ucpchelp1 \
+ Package_helpxsl \
+))
+
+# vim: set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/xmlhelp/Package_helpxsl.mk b/xmlhelp/Package_helpxsl.mk
new file mode 100644
index 0000000000..edb4dfbded
--- /dev/null
+++ b/xmlhelp/Package_helpxsl.mk
@@ -0,0 +1,18 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Package_Package,xmlhelp_helpxsl,$(SRCDIR)/xmlhelp/util))
+
+$(eval $(call gb_Package_add_files,xmlhelp_helpxsl,$(LIBO_SHARE_HELP_FOLDER),\
+ idxcaption.xsl \
+ idxcontent.xsl \
+ main_transform.xsl \
+))
+
+# vim: set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/xmlhelp/README.md b/xmlhelp/README.md
new file mode 100644
index 0000000000..4950405429
--- /dev/null
+++ b/xmlhelp/README.md
@@ -0,0 +1 @@
+Help viewer component for the XML help. For more info see /README.help.md.
diff --git a/xmlhelp/source/cxxhelp/inc/tvfactory.hxx b/xmlhelp/source/cxxhelp/inc/tvfactory.hxx
new file mode 100644
index 0000000000..8b51c29603
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/inc/tvfactory.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <cppuhelper/implbase.hxx>
+
+
+namespace treeview {
+
+class TVFactory final : public cppu::WeakImplHelper <
+ css::lang::XServiceInfo,
+ css::lang::XMultiServiceFactory >
+ {
+ public:
+
+ TVFactory( css::uno::Reference< css::uno::XComponentContext > xContext );
+
+ virtual ~TVFactory() 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;
+
+ // XMultiServiceFactory
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ createInstance(
+ const OUString& aServiceSpecifier ) override;
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ createInstanceWithArguments(
+ const OUString& ServiceSpecifier,
+ const css::uno::Sequence< css::uno::Any >& Arguments ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getAvailableServiceNames( ) override;
+
+ private:
+
+ // Members
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::uno::XInterface > m_xHDS;
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/inc/tvread.hxx b/xmlhelp/source/cxxhelp/inc/tvread.hxx
new file mode 100644
index 0000000000..08bfdb6482
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/inc/tvread.hxx
@@ -0,0 +1,293 @@
+/* -*- 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 <mutex>
+#include <vector>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <memory>
+
+namespace treeview {
+
+ class ConfigData
+ {
+ public:
+ enum {
+ PRODUCTNAME, PRODUCTVERSION, VENDORNAME, VENDORVERSION,
+ VENDORSHORT };
+ ConfigData();
+ int m_vAdd[5] = {};
+ OUString m_vReplacement[5];
+
+ std::vector< sal_uInt64 > vFileLen;
+ std::vector< OUString > vFileURL;
+ OUString locale,system;
+ OUString appendix;
+
+ void replaceName( OUString& oustring ) const;
+ };
+
+ class TVDom;
+ class TVChildTarget;
+
+ class TVBase : public cppu::WeakImplHelper <
+ css::container::XNameAccess,
+ css::container::XHierarchicalNameAccess,
+ css::util::XChangesNotifier,
+ css::lang::XComponent >
+ {
+ friend class TVChildTarget;
+
+ public:
+
+ // XNameAccess
+
+ virtual css::uno::Type SAL_CALL
+ getElementType( ) override
+ {
+ return cppu::UnoType<void>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return true;
+ }
+
+ // XChangesNotifier
+
+ virtual void SAL_CALL
+ addChangesListener(
+ const css::uno::Reference< css::util::XChangesListener >& ) override
+ {
+ // read only
+ }
+
+ virtual void SAL_CALL
+ removeChangesListener(
+ const css::uno::Reference< css::util::XChangesListener >& ) override
+ {
+ // read only
+ }
+
+ // 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
+ {}
+
+ // Abstract functions
+ // XNameAccess
+
+ virtual css::uno::Any SAL_CALL
+ getByName( const OUString& aName ) override = 0;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getElementNames( ) override = 0;
+
+ virtual sal_Bool SAL_CALL
+ hasByName( const OUString& aName ) override = 0;
+
+ // XHierarchicalNameAccess
+
+ virtual css::uno::Any SAL_CALL
+ getByHierarchicalName( const OUString& aName ) override = 0;
+
+ virtual sal_Bool SAL_CALL
+ hasByHierarchicalName( const OUString& aName ) override = 0;
+
+ }; // end class TVBase
+
+ class TVRead final
+ : public TVBase
+ {
+ friend class TVChildTarget;
+
+ public:
+ TVRead( const ConfigData& configData, TVDom* tvDom );
+
+ virtual ~TVRead() override;
+
+ // XNameAccess
+
+ virtual css::uno::Any SAL_CALL
+ getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getElementNames( ) override;
+
+ virtual sal_Bool SAL_CALL
+ hasByName( const OUString& aName ) override;
+
+ // XHierarchicalNameAccess
+
+ virtual css::uno::Any SAL_CALL
+ getByHierarchicalName( const OUString& aName ) override;
+
+ virtual sal_Bool SAL_CALL
+ hasByHierarchicalName( const OUString& aName ) override;
+
+ private:
+
+ OUString Title;
+ OUString TargetURL;
+ rtl::Reference< TVChildTarget > Children;
+
+ }; // end class TVRead
+
+ class TVChildTarget
+ : public TVBase
+ {
+ public:
+
+ TVChildTarget( const ConfigData& configData,TVDom* tvDom );
+
+ TVChildTarget( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual ~TVChildTarget() override;
+
+ virtual css::uno::Any SAL_CALL
+ getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getElementNames( ) override;
+
+ virtual sal_Bool SAL_CALL
+ hasByName( const OUString& aName ) override;
+
+ // XHierarchicalNameAccess
+
+ virtual css::uno::Any SAL_CALL
+ getByHierarchicalName( const OUString& aName ) override;
+
+ virtual sal_Bool SAL_CALL
+ hasByHierarchicalName( const OUString& aName ) override;
+
+ private:
+ std::vector< rtl::Reference< TVRead > > Elements;
+
+ static ConfigData init(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ static css::uno::Reference< css::lang::XMultiServiceFactory >
+ getConfiguration(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ static css::uno::Reference< css::container::XHierarchicalNameAccess >
+ getHierAccess( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxProvider,
+ const char* file );
+
+ static OUString
+ getKey( const css::uno::Reference< css::container::XHierarchicalNameAccess >& xHierAccess,
+ const char* key );
+
+ static bool
+ getBooleanKey(
+ const css::uno::Reference< css::container::XHierarchicalNameAccess >& xHierAccess,
+ const char* key);
+
+ static void subst( OUString& instpath );
+
+ std::unique_ptr<TVDom> SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom);
+
+ void Check(TVDom* tvDom);
+
+ }; // end class TVChildTarget
+
+ enum class IteratorState
+ {
+ UserExtensions,
+ SharedExtensions,
+ BundledExtensions,
+ EndReached
+ };
+
+ class TreeFileIterator
+ {
+ public:
+ TreeFileIterator( OUString aLanguage );
+ OUString nextTreeFile( sal_Int32& rnFileSize );
+
+ 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 );
+
+ 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 );
+
+ void implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFA;
+
+ IteratorState m_eState;
+ 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;
+
+ OUString expandURL( const OUString& aURL );
+ OUString implGetTreeFileFromPackage( sal_Int32& rnFileSize,
+ const css::uno::Reference< css::deployment::XPackage >& xPackage );
+
+ }; // end class TreeFileIterator
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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: */
diff --git a/xmlhelp/source/cxxhelp/test/abidebug.hxx b/xmlhelp/source/cxxhelp/test/abidebug.hxx
new file mode 100644
index 0000000000..833233bcb2
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/test/abidebug.hxx
@@ -0,0 +1,27 @@
+/* -*- 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 <iostream>
+#include <rtl/ustring>
+
+ostream& operator<<(ostream& out, const OUString& bla);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/cxxhelp/test/makefile.mk b/xmlhelp/source/cxxhelp/test/makefile.mk
new file mode 100644
index 0000000000..b7c3859fff
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/test/makefile.mk
@@ -0,0 +1,60 @@
+#
+# 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 .
+#
+
+PRJ=..$/..$/..
+
+PRJNAME=xmlhelp
+TARGET=searchdemo
+TARGETTYPE = CUI
+LIBTARGET=NO
+
+ENABLE_EXCEPTIONS=TRUE
+
+# --- Settings ---
+
+.INCLUDE : settings.mk
+
+
+.IF "$(OS)"=="WNT"
+CFLAGS+=-GR
+.ENDIF
+
+# --- Files ---
+
+OBJFILES=\
+ $(OBJ)$/searchdemo.obj
+
+APP1TARGET= searchdemo
+APP1OBJS=\
+ $(OBJ)$/searchdemo.obj
+
+APP1STDLIBS=\
+ $(SALLIB)
+
+
+APP1LIBS=\
+ $(SLB)/jaqe.lib \
+ $(SLB)/jadb.lib \
+ $(SLB)/jautil.lib
+
+APP1DEF= $(MISC)\$(APP1TARGET).def
+
+# --- Targets ---
+
+.INCLUDE : target.mk
+
diff --git a/xmlhelp/source/cxxhelp/test/searchdemo.cxx b/xmlhelp/source/cxxhelp/test/searchdemo.cxx
new file mode 100644
index 0000000000..1300e68e61
--- /dev/null
+++ b/xmlhelp/source/cxxhelp/test/searchdemo.cxx
@@ -0,0 +1,107 @@
+/* -*- 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 <rtl/string.hxx>
+#include <osl/file.hxx>
+#include <db/Block.hxx>
+#include <db/BtreeDictParameters.hxx>
+#include <db/BtreeDict.hxx>
+#include <util/RandomAccessStream.hxx>
+#include <db/DBEnv.hxx>
+#include <qe/QueryProcessor.hxx>
+
+#ifdef ABIDEBUG
+#include <abidebug.hxx>
+#endif
+
+
+using namespace xmlsearch::util;
+using namespace xmlsearch::db;
+using namespace xmlsearch::qe;
+
+
+extern RandomAccessStream* theFile();
+
+
+void print_rtl_OUString( const OUString bla )
+{
+ OString bluber = OUStringToOString( bla,RTL_TEXTENCODING_UTF8 );
+ char* bluberChr = new char[ 1+bluber.getLength() ];
+ const char* jux = bluber.getStr();
+
+ for( int i = 0; i < bluber.getLength(); ++i )
+ bluberChr[i] = jux[i];
+
+ bluberChr[ bluber.getLength() ] = 0;
+ printf( "%s\n",bluberChr );
+ delete[] bluberChr;
+}
+
+
+extern void bla();
+extern void blu();
+
+
+int main( int argc,char* argv[] )
+{
+
+ QueryResults* queryResults = 0;
+
+ try
+ {
+ OUString installDir("//./e|/index/");
+ QueryProcessor queryProcessor( installDir );
+
+ std::vector<OUString> Query(2);
+ Query[0] = "text*";
+ Query[1] = "abbildung";
+ OUString Scope = "headingheading";
+ int HitCount = 40;
+
+ QueryStatement queryStatement( HitCount,Query,Scope );
+ queryResults = queryProcessor.processQuery( queryStatement );
+
+ OUString translations[2];
+ translations[0] = "#HLP#";
+ translations[1] = "vnd.sun.star.help://";
+
+ PrefixTranslator* translator = PrefixTranslator::makePrefixTranslator( translations,2 );
+
+ QueryHitIterator* it = queryResults->makeQueryHitIterator();
+ sal_Int32 j = 0;
+ while( j < 10 && it->next() )
+ {
+ printf( "Ergebnis %2d ",j );
+ QueryHitData* qhd = it->getHit( translator );
+ printf( "Penalty = %10.4f ",qhd->getPenalty() );
+ print_rtl_OUString( qhd->getDocument() );
+ ++j;
+ }
+
+ delete it;
+ }
+ catch( ... )
+ {
+ printf( "caught exception" );
+ throw;
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/treeview/tvfactory.cxx b/xmlhelp/source/treeview/tvfactory.cxx
new file mode 100644
index 0000000000..99a55580f9
--- /dev/null
+++ b/xmlhelp/source/treeview/tvfactory.cxx
@@ -0,0 +1,132 @@
+/* -*- 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/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/factory.hxx>
+#include <tvfactory.hxx>
+#include <tvread.hxx>
+#include <utility>
+
+using namespace treeview;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+TVFactory::TVFactory( uno::Reference< XComponentContext > xContext )
+ : m_xContext(std::move( xContext ))
+{
+}
+
+TVFactory::~TVFactory()
+{
+}
+
+// XServiceInfo methods.
+
+OUString SAL_CALL
+TVFactory::getImplementationName()
+{
+ return "com.sun.star.help.TreeViewImpl";
+}
+
+sal_Bool SAL_CALL TVFactory::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL
+TVFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.help.TreeView", "com.sun.star.ucb.HiearchyDataSource" };
+}
+
+// XMultiServiceFactory
+
+Reference< XInterface > SAL_CALL
+TVFactory::createInstance(
+ const OUString& aServiceSpecifier )
+{
+ uno::Sequence<uno::Any> seq(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString())}
+ }));
+
+ return createInstanceWithArguments( aServiceSpecifier, seq );
+}
+
+Reference< XInterface > SAL_CALL
+TVFactory::createInstanceWithArguments(
+ const OUString& /*ServiceSpecifier*/,
+ const Sequence< Any >& Arguments )
+{
+ if( ! m_xHDS.is() )
+ {
+ m_xHDS = cppu::getXWeak(new TVChildTarget( m_xContext ));
+ }
+
+ OUString hierview;
+ for( const auto& rArgument : Arguments )
+ {
+ PropertyValue pV;
+ if( ! ( rArgument >>= pV ) )
+ continue;
+
+ if( pV.Name != "nodepath" )
+ continue;
+
+ if( ! ( pV.Value >>= hierview ) )
+ continue;
+
+ break;
+ }
+
+ if( !hierview.isEmpty() )
+ {
+ Reference< XHierarchicalNameAccess > xhieraccess( m_xHDS,UNO_QUERY );
+ Any aAny = xhieraccess->getByHierarchicalName( hierview );
+ Reference< XInterface > xInterface;
+ aAny >>= xInterface;
+ return xInterface;
+ }
+ else
+ return m_xHDS;
+}
+
+Sequence< OUString > SAL_CALL
+TVFactory::getAvailableServiceNames( )
+{
+ return { "com.sun.star.ucb.HierarchyDataReadAccess" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_help_TreeViewImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new TVFactory(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/source/treeview/tvread.cxx b/xmlhelp/source/treeview/tvread.cxx
new file mode 100644
index 0000000000..cc06309369
--- /dev/null
+++ b/xmlhelp/source/treeview/tvread.cxx
@@ -0,0 +1,1134 @@
+/* -*- 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 <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tvread.hxx>
+#include <expat.h>
+#include <osl/file.hxx>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <com/sun/star/deployment/thePackageManagerFactory.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 <unotools/pathoptions.hxx>
+#include <memory>
+#include <utility>
+
+namespace treeview {
+
+ class TVDom
+ {
+ friend class TVChildTarget;
+ friend class TVRead;
+
+ public:
+
+ explicit TVDom( TVDom* arent = nullptr )
+ : kind( Kind::other ),
+ parent( arent ),
+ children( 0 )
+ {
+ }
+
+ TVDom* newChild()
+ {
+ children.emplace_back( new TVDom( this ) );
+ return children.back().get();
+ }
+
+ void newChild(std::unique_ptr<TVDom> p)
+ {
+ children.emplace_back(std::move(p));
+ children.back()->parent = this;
+ }
+
+ TVDom* getParent() const
+ {
+ if( parent )
+ return parent;
+ else
+ return const_cast<TVDom*>(this); // I am my own parent, if I am the root
+ }
+
+ enum class Kind {
+ tree_node,
+ tree_leaf,
+ other
+ };
+
+ bool isLeaf() const { return kind == TVDom::Kind::tree_leaf; }
+ void setKind( Kind ind ) { kind = ind; }
+
+ void setApplication( const char* appl )
+ {
+ application = OUString( appl,
+ strlen( appl ),
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+ void setTitle( const char* itle )
+ {
+ title += OUString( itle,
+ strlen( itle ),
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+ void setTitle( const XML_Char* itle,int len )
+ {
+ title += OUString( itle,
+ len,
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+ void setId( const char* d )
+ {
+ id = OUString( d,
+ strlen( d ),
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+ void setAnchor( const char* nchor )
+ {
+ anchor = OUString( nchor,
+ strlen( nchor ),
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+ OUString const & getTargetURL()
+ {
+ if( targetURL.isEmpty() )
+ {
+ targetURL = "vnd.sun.star.help://" + id;
+ }
+
+ return targetURL;
+ }
+
+ private:
+
+ Kind kind;
+ OUString application;
+ OUString title;
+ OUString id;
+ OUString anchor;
+ OUString targetURL;
+
+ TVDom *parent;
+ std::vector< std::unique_ptr<TVDom> > children;
+ };
+
+}
+
+using namespace treeview;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::configuration;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::util;
+using namespace com::sun::star::container;
+using namespace com::sun::star::deployment;
+
+const char prodName[] = "%PRODUCTNAME";
+const char vendName[] = "%VENDORNAME";
+const char vendVersion[] = "%VENDORVERSION";
+const char vendShort[] = "%VENDORSHORT";
+const char prodVersion[] = "%PRODUCTVERSION";
+
+ConfigData::ConfigData()
+{
+}
+
+void ConfigData::replaceName( OUString& oustring ) const
+{
+ sal_Int32 idx = -1,k = 0,off;
+ bool cap = false;
+ OUStringBuffer aStrBuf( 0 );
+
+ while( ( idx = oustring.indexOf( '%', ++idx ) ) != -1 )
+ {
+ 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
+ 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();
+ }
+}
+
+// TVRead
+
+TVRead::TVRead( const ConfigData& configData,TVDom* tvDom )
+{
+ if( ! tvDom )
+ return;
+
+ Title = tvDom->title;
+ configData.replaceName( Title );
+ if( tvDom->isLeaf() )
+ {
+ TargetURL = tvDom->getTargetURL() + configData.appendix;
+ if( !tvDom->anchor.isEmpty() )
+ TargetURL += "#" + tvDom->anchor;
+ }
+ else
+ Children = new TVChildTarget( configData,tvDom );
+}
+
+TVRead::~TVRead()
+{
+}
+
+// XNameAccess
+
+Any SAL_CALL
+TVRead::getByName( const OUString& aName )
+{
+ bool found( true );
+ Any aAny;
+ if( aName == "Title" )
+ aAny <<= Title;
+ else if( aName == "TargetURL" )
+ aAny <<= TargetURL;
+ else if( aName == "Children" )
+ {
+ cppu::OWeakObject* p = Children.get();
+ aAny <<= Reference< XInterface >( p );
+ }
+ else
+ found = false;
+
+ if( found )
+ return aAny;
+
+ throw NoSuchElementException();
+}
+
+Sequence< OUString > SAL_CALL
+TVRead::getElementNames( )
+{
+ return { "Title", "TargetURL", "Children" };
+}
+
+sal_Bool SAL_CALL
+TVRead::hasByName( const OUString& aName )
+{
+ if( aName == "Title" ||
+ aName == "TargetURL" ||
+ aName == "Children" )
+ return true;
+
+ return false;
+}
+
+// XHierarchicalNameAccess
+
+Any SAL_CALL
+TVRead::getByHierarchicalName( const OUString& aName )
+{
+ OUString aRest;
+ if( aName.startsWith("Children/", &aRest) )
+ return Children->getByHierarchicalName( aRest );
+
+ return getByName( aName );
+}
+
+sal_Bool SAL_CALL
+TVRead::hasByHierarchicalName( const OUString& aName )
+{
+ OUString aRest;
+ if( aName.startsWith("Children/", &aRest) )
+ return Children->hasByHierarchicalName( aRest );
+
+ return hasByName( aName );
+}
+
+/**************************************************************************/
+/* */
+/* TVChildTarget */
+/* */
+/**************************************************************************/
+
+extern "C" {
+
+static void start_handler(void *userData,
+ const XML_Char *name,
+ const XML_Char **atts)
+{
+ TVDom::Kind kind;
+
+ if( strcmp( name,"help_section" ) == 0 ||
+ strcmp( name,"node" ) == 0 )
+ kind = TVDom::Kind::tree_node;
+ else if( strcmp( name,"topic" ) == 0 )
+ kind = TVDom::Kind::tree_leaf;
+ else
+ return;
+
+ TVDom **tvDom = static_cast< TVDom** >( userData );
+ TVDom *p;
+ p = *tvDom;
+
+ *tvDom = p->newChild();
+ p = *tvDom;
+
+ p->setKind( kind );
+ while( *atts )
+ {
+ if( strcmp( *atts,"application" ) == 0 )
+ p->setApplication( *(atts+1) );
+ else if( strcmp( *atts,"title" ) == 0 )
+ p->setTitle( *(atts+1) );
+ else if( strcmp( *atts,"id" ) == 0 )
+ p->setId( *(atts+1) );
+ else if( strcmp( *atts,"anchor" ) == 0 )
+ p->setAnchor( *(atts+1) );
+
+ atts+=2;
+ }
+}
+
+static void end_handler(void *userData,
+ SAL_UNUSED_PARAMETER const XML_Char * )
+{
+ TVDom **tvDom = static_cast< TVDom** >( userData );
+ *tvDom = (*tvDom)->getParent();
+}
+
+static void data_handler( void *userData,
+ const XML_Char *s,
+ int len)
+{
+ TVDom **tvDom = static_cast< TVDom** >( userData );
+ if( (*tvDom)->isLeaf() )
+ (*tvDom)->setTitle( s,len );
+}
+
+}
+
+TVChildTarget::TVChildTarget( const ConfigData& configData,TVDom* tvDom )
+{
+ Elements.resize( tvDom->children.size() );
+ for( size_t i = 0; i < Elements.size(); ++i )
+ Elements[i] = new TVRead( configData,tvDom->children[i].get() );
+}
+
+TVChildTarget::TVChildTarget( const Reference< XComponentContext >& xContext )
+{
+ ConfigData configData = init( xContext );
+
+ if( configData.locale.isEmpty() || configData.system.isEmpty() )
+ return;
+
+ sal_uInt64 ret,len = 0;
+ int j = configData.vFileURL.size();
+
+ TVDom tvDom;
+ TVDom* pTVDom = &tvDom;
+
+ while( j )
+ {
+ len = configData.vFileLen[--j];
+ std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files
+ osl::File aFile( configData.vFileURL[j] );
+ (void)aFile.open( osl_File_OpenFlag_Read );
+ aFile.read( s.get(),len,ret );
+ aFile.close();
+
+ XML_Parser parser = XML_ParserCreate( nullptr );
+ XML_SetElementHandler( parser,
+ start_handler,
+ end_handler );
+ XML_SetCharacterDataHandler( parser,
+ data_handler);
+ XML_SetUserData( parser,&pTVDom ); // does not return this
+
+ XML_Status const parsed = XML_Parse(parser, s.get(), int(len), j==0);
+ SAL_WARN_IF(XML_STATUS_ERROR == parsed, "xmlhelp",
+ "TVChildTarget::TVChildTarget(): Tree file parsing failed");
+
+ XML_ParserFree( parser );
+
+ Check(pTVDom);
+ }
+ // now TVDom holds the relevant information
+
+ Elements.resize( tvDom.children.size() );
+ for( size_t i = 0; i < Elements.size(); ++i )
+ Elements[i] = new TVRead( configData,tvDom.children[i].get() );
+}
+
+TVChildTarget::~TVChildTarget()
+{
+}
+
+void TVChildTarget::Check(TVDom* tvDom)
+{
+ if (tvDom->children.empty())
+ {
+ return;
+ }
+
+ unsigned i = 0;
+ bool h = false;
+
+ while((i<tvDom->children.size()-1) && (!h))
+ {
+ if (((tvDom->children[i])->application == (tvDom->children[tvDom->children.size()-1])->application) &&
+ ((tvDom->children[i])->id == (tvDom->children[tvDom->children.size()-1])->id))
+ {
+ TVDom* p = tvDom->children.back().get();
+
+ for(auto & k : p->children)
+ {
+ std::unique_ptr<TVDom> tmp(SearchAndInsert(std::move(k), tvDom->children[i].get()));
+ if (tmp)
+ {
+ tvDom->children[i]->newChild(std::move(tmp));
+ }
+ }
+
+ tvDom->children.pop_back();
+ h = true;
+ }
+ ++i;
+ }
+}
+
+std::unique_ptr<TVDom>
+TVChildTarget::SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom)
+{
+ if (p->isLeaf()) return p;
+
+ bool h = false;
+ sal_Int32 max = 0;
+
+ std::vector< std::unique_ptr<TVDom> >::iterator max_It, i;
+ max_It = tvDom->children.begin();
+
+ sal_Int32 c_int;
+ sal_Int32 p_int = p->id.toInt32();
+
+ for(i = tvDom->children.begin(); i!=tvDom->children.end(); ++i)
+ if (!((*i)->isLeaf()) &&
+ ((*i)->id.getLength() == p->id.getLength()) &&
+ (p->id.replaceAt((*i)->parent->id.getLength(), p->id.getLength()-(*i)->parent->id.getLength(), u"") == (*i)->parent->id)) //prefix check
+ {
+ h = true;
+ c_int = (*i)->id.toInt32();
+
+ if (p_int==c_int)
+ {
+ (*(tvDom->children.insert(i+1, std::move(p))))->parent = tvDom;
+ return nullptr;
+ }
+ else if(c_int>max && c_int < p_int)
+ {
+ max = c_int;
+ max_It = i+1;
+ }
+ }
+ if (h)
+ {
+ (*(tvDom->children.insert(max_It, std::move(p))))->parent = tvDom;
+ return nullptr;
+ }
+ else
+ {
+ for (auto& child : tvDom->children)
+ {
+ p = SearchAndInsert(std::move(p), child.get());
+ if (p == nullptr)
+ break;
+ }
+ return p;
+ }
+}
+
+Any SAL_CALL
+TVChildTarget::getByName( const OUString& aName )
+{
+ std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) );
+ sal_Int32 idx = o3tl::toInt32(num) - 1;
+ if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
+ throw NoSuchElementException();
+
+ cppu::OWeakObject* p = Elements[idx].get();
+ return Any( Reference< XInterface >( p ) );
+}
+
+Sequence< OUString > SAL_CALL
+TVChildTarget::getElementNames( )
+{
+ Sequence< OUString > seq( Elements.size() );
+ auto seqRange = asNonConstRange(seq);
+ for( size_t i = 0; i < Elements.size(); ++i )
+ seqRange[i] = OUString::number( 1+i );
+
+ return seq;
+}
+
+sal_Bool SAL_CALL
+TVChildTarget::hasByName( const OUString& aName )
+{
+ std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) );
+ sal_Int32 idx = o3tl::toInt32(num) - 1;
+ if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
+ return false;
+
+ return true;
+}
+
+// XHierarchicalNameAccess
+
+Any SAL_CALL
+TVChildTarget::getByHierarchicalName( const OUString& aName )
+{
+ sal_Int32 idx;
+
+ if( ( idx = aName.indexOf( '/' ) ) != -1 )
+ {
+ std::u16string_view num( aName.subView( 2, idx-4 ) );
+ sal_Int32 pref = o3tl::toInt32(num) - 1;
+
+ if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
+ throw NoSuchElementException();
+
+ return Elements[pref]->getByHierarchicalName( aName.copy( 1 + idx ) );
+ }
+ else
+ return getByName( aName );
+}
+
+sal_Bool SAL_CALL
+TVChildTarget::hasByHierarchicalName( const OUString& aName )
+{
+ sal_Int32 idx;
+
+ if( ( idx = aName.indexOf( '/' ) ) != -1 )
+ {
+ std::u16string_view num( aName.subView( 2, idx-4 ) );
+ sal_Int32 pref = o3tl::toInt32(num) - 1;
+ if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
+ return false;
+
+ return Elements[pref]->hasByHierarchicalName( aName.copy( 1 + idx ) );
+ }
+ else
+ return hasByName( aName );
+}
+
+ConfigData TVChildTarget::init( const Reference< XComponentContext >& xContext )
+{
+ ConfigData configData;
+ Reference< XMultiServiceFactory > sProvider( getConfiguration(xContext) );
+
+ /**********************************************************************/
+ /* reading Office.Common */
+ /**********************************************************************/
+
+ Reference< XHierarchicalNameAccess > xHierAccess( getHierAccess( sProvider,
+ "org.openoffice.Office.Common" ) );
+ OUString system( getKey( xHierAccess,"Help/System" ) );
+ bool showBasic( getBooleanKey(xHierAccess,"Help/ShowBasic") );
+ OUString instPath( getKey( xHierAccess,"Path/Current/Help" ) );
+ if( instPath.isEmpty() )
+ // try to determine path from default
+ instPath = "$(instpath)/help";
+
+ // replace anything like $(instpath);
+ subst( instPath );
+
+ /**********************************************************************/
+ /* reading setup */
+ /**********************************************************************/
+
+ xHierAccess = getHierAccess( sProvider,
+ "org.openoffice.Setup" );
+
+ OUString setupversion( getKey( xHierAccess,"Product/ooSetupVersion" ) );
+ OUString setupextension;
+
+ try
+ {
+ Reference< lang::XMultiServiceFactory > xConfigProvider = theDefaultProvider::get( xContext );
+
+ uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Setup/Product"))}
+ }));
+
+ // open it
+ uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ lParams) );
+
+ uno::Reference< container::XNameAccess > xDirectAccess(xCFG, uno::UNO_QUERY);
+ uno::Any aRet = xDirectAccess->getByName("ooSetupExtension");
+
+ aRet >>= setupextension;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ OUString productVersion( setupversion + " " + setupextension );
+ OUString locale( getKey( xHierAccess,"L10N/ooLocale" ) );
+
+ // Determine fileurl from url and locale
+ OUString url;
+ osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url );
+ if( errFile != osl::FileBase::E_None ) return configData;
+ if( !url.endsWith("/") )
+ url += "/";
+ OUString ret;
+ sal_Int32 idx;
+ osl::DirectoryItem aDirItem;
+ if( osl::FileBase::E_None == osl::DirectoryItem::get( url + locale,aDirItem ) )
+ ret = locale;
+ else if( ( ( idx = locale.indexOf( '-' ) ) != -1 ||
+ ( idx = locale.indexOf( '_' ) ) != -1 ) &&
+ osl::FileBase::E_None == osl::DirectoryItem::get( url + locale.subView( 0,idx ),
+ aDirItem ) )
+ ret = locale.copy( 0,idx );
+ else
+ {
+ locale = "en-US";
+ ret = "en";
+ }
+ url += ret;
+
+ // first of all, try do determine whether there are any *.tree files present
+
+ // Start with extensions to set them at the end of the list
+ TreeFileIterator aTreeIt( locale );
+ OUString aTreeFile;
+ sal_Int32 nFileSize;
+ for (;;)
+ {
+ aTreeFile = aTreeIt.nextTreeFile( nFileSize );
+ if( aTreeFile.isEmpty() )
+ break;
+ configData.vFileLen.push_back( nFileSize );
+ configData.vFileURL.push_back( aTreeFile );
+ }
+
+ osl::Directory aDirectory( url );
+ osl::FileStatus aFileStatus(
+ osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_FileURL );
+ if( osl::Directory::E_None == aDirectory.open() )
+ {
+ OUString aFileUrl, aFileName;
+ while( aDirectory.getNextItem( aDirItem ) == osl::FileBase::E_None &&
+ aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
+ aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) &&
+ aFileStatus.isValid( osl_FileStatus_Mask_FileName ) )
+ {
+ aFileUrl = aFileStatus.getFileURL();
+ aFileName = aFileStatus.getFileName();
+ int idx_ = aFileName.lastIndexOf( '.' );
+ if( idx_ == -1 )
+ continue;
+
+ const sal_Unicode* str = aFileName.getStr();
+
+ if( aFileName.getLength() == idx_ + 5 &&
+ ( str[idx_ + 1] == 't' || str[idx_ + 1] == 'T' ) &&
+ ( str[idx_ + 2] == 'r' || str[idx_ + 2] == 'R' ) &&
+ ( str[idx_ + 3] == 'e' || str[idx_ + 3] == 'E' ) &&
+ ( str[idx_ + 4] == 'e' || str[idx_ + 4] == 'E' ) )
+ {
+ OUString baseName = aFileName.copy(0,idx_).toAsciiLowerCase();
+ if(! showBasic && baseName == "sbasic" )
+ continue;
+ osl::File aFile( aFileUrl );
+ if( osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) )
+ {
+ // use the file size, not aFileStatus size, in case the
+ // tree file is a symlink
+ sal_uInt64 nSize;
+ aFile.getSize( nSize );
+ configData.vFileLen.push_back( nSize );
+ configData.vFileURL.push_back( aFileUrl );
+ aFile.close();
+ }
+ }
+ }
+ aDirectory.close();
+ }
+
+ configData.m_vAdd[0] = 12;
+ configData.m_vAdd[1] = 15;
+ configData.m_vAdd[2] = 11;
+ configData.m_vAdd[3] = 14;
+ configData.m_vAdd[4] = 12;
+ configData.m_vReplacement[0] = utl::ConfigManager::getProductName();
+ configData.m_vReplacement[1] = productVersion;
+ // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
+
+ configData.system = system;
+ configData.locale = locale;
+ configData.appendix =
+ "?Language=" +
+ configData.locale +
+ "&System=" +
+ configData.system +
+ "&UseDB=no";
+
+ return configData;
+}
+
+Reference< XMultiServiceFactory >
+TVChildTarget::getConfiguration(const Reference< XComponentContext >& rxContext)
+{
+ Reference< XMultiServiceFactory > xProvider;
+ if( rxContext.is() )
+ {
+ try
+ {
+ xProvider = theDefaultProvider::get( rxContext );
+ }
+ catch( const css::uno::Exception& )
+ {
+ OSL_ENSURE( xProvider.is(),"can not instantiate configuration" );
+ }
+ }
+
+ return xProvider;
+}
+
+Reference< XHierarchicalNameAccess >
+TVChildTarget::getHierAccess( const Reference< XMultiServiceFactory >& sProvider,
+ const char* file )
+{
+ Reference< XHierarchicalNameAccess > xHierAccess;
+
+ if( sProvider.is() )
+ {
+ try
+ {
+ xHierAccess =
+ Reference< XHierarchicalNameAccess >
+ ( sProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", { Any(OUString::createFromAscii(file)) }),
+ UNO_QUERY );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ }
+
+ return xHierAccess;
+}
+
+OUString
+TVChildTarget::getKey( const Reference< XHierarchicalNameAccess >& xHierAccess,
+ const char* key )
+{
+ OUString instPath;
+ if( xHierAccess.is() )
+ {
+ Any aAny;
+ try
+ {
+ aAny =
+ xHierAccess->getByHierarchicalName( OUString::createFromAscii( key ) );
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ aAny >>= instPath;
+ }
+ return instPath;
+}
+
+bool
+TVChildTarget::getBooleanKey(const Reference<
+ XHierarchicalNameAccess >& xHierAccess,
+ const char* key)
+{
+ bool ret = false;
+ if( xHierAccess.is() )
+ {
+ Any aAny;
+ try
+ {
+ aAny =
+ xHierAccess->getByHierarchicalName(
+ OUString::createFromAscii(key));
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ aAny >>= ret;
+ }
+ return ret;
+}
+
+void TVChildTarget::subst( OUString& instpath )
+{
+ SvtPathOptions aOptions;
+ instpath = aOptions.SubstituteVariable( instpath );
+}
+
+
+const char aHelpMediaType[] = "application/vnd.sun.star.help";
+
+TreeFileIterator::TreeFileIterator( OUString aLanguage )
+ : m_eState( IteratorState::UserExtensions )
+ , m_aLanguage(std::move( aLanguage ))
+{
+ m_xContext = ::comphelper::getProcessComponentContext();
+ if( !m_xContext.is() )
+ {
+ throw RuntimeException( "TreeFileIterator::TreeFileIterator(), no XComponentContext" );
+ }
+
+ 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 > TreeFileIterator::implGetHelpPackageFromPackage
+ ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ o_xParentPackageBundle.clear();
+
+ Reference< deployment::XPackage > xHelpPackage;
+ if( !xPackage.is() )
+ 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 )
+ return xHelpPackage;
+
+ 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(),
+ [](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;
+ }
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > TreeFileIterator::implGetNextUserHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bUserPackagesLoaded )
+ {
+ Reference< XPackageManager > xUserManager =
+ thePackageManagerFactory::get( m_xContext )->getPackageManager("user");
+ m_aUserPackagesSeq = xUserManager->getDeployedPackages
+ ( 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(), "TreeFileIterator::implGetNextUserHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > TreeFileIterator::implGetNextSharedHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bSharedPackagesLoaded )
+ {
+ Reference< XPackageManager > xSharedManager =
+ thePackageManagerFactory::get( m_xContext )->getPackageManager("shared");
+ m_aSharedPackagesSeq = xSharedManager->getDeployedPackages
+ ( 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(), "TreeFileIterator::implGetNextSharedHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+Reference< deployment::XPackage > TreeFileIterator::implGetNextBundledHelpPackage
+ ( Reference< deployment::XPackage >& o_xParentPackageBundle )
+{
+ Reference< deployment::XPackage > xHelpPackage;
+
+ if( !m_bBundledPackagesLoaded )
+ {
+ Reference< XPackageManager > xBundledManager =
+ thePackageManagerFactory::get( m_xContext )->getPackageManager("bundled");
+ m_aBundledPackagesSeq = xBundledManager->getDeployedPackages
+ ( 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(), "TreeFileIterator::implGetNextBundledHelpPackage(): Invalid package" );
+ xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
+ }
+
+ return xHelpPackage;
+}
+
+static bool isLetter( sal_Unicode c )
+{
+ return rtl::isAsciiAlpha(c);
+}
+
+void TreeFileIterator::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 );
+ }
+ }
+ }
+}
+
+
+OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize )
+{
+ OUString aRetFile;
+
+ while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached )
+ {
+ switch( m_eState )
+ {
+ case IteratorState::UserExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
+ break;
+ }
+
+ case IteratorState::SharedExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
+ break;
+ }
+ case IteratorState::BundledExtensions:
+ {
+ Reference< deployment::XPackage > xParentPackageBundle;
+ Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
+ if( !xHelpPackage.is() )
+ break;
+
+ aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
+ break;
+ }
+
+ case IteratorState::EndReached:
+ OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" );
+ break;
+ }
+ }
+
+ return aRetFile;
+}
+
+OUString TreeFileIterator::expandURL( const OUString& aURL )
+{
+ static Reference< util::XMacroExpander > xMacroExpander;
+ static Reference< uri::XUriReferenceFactory > xFac;
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ if( !xMacroExpander.is() || !xFac.is() )
+ {
+ xFac = uri::UriReferenceFactory::create( m_xContext );
+
+ xMacroExpander = util::theMacroExpander::get(m_xContext);
+ }
+
+ OUString aRetURL = aURL;
+ 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;
+}
+
+OUString TreeFileIterator::implGetTreeFileFromPackage
+ ( sal_Int32& rnFileSize, const Reference< deployment::XPackage >& xPackage )
+{
+ OUString aRetFile;
+ OUString aLanguage = m_aLanguage;
+ for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
+ {
+ aRetFile = expandURL( xPackage->getURL() + "/" + aLanguage + "/help.tree" );
+ if( iPass == 0 )
+ {
+ if( m_xSFA->exists( aRetFile ) )
+ 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;
+ }
+ }
+
+ rnFileSize = 0;
+ if( m_xSFA->exists( aRetFile ) )
+ rnFileSize = m_xSFA->getSize( aRetFile );
+ else
+ aRetFile.clear();
+
+ return aRetFile;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlhelp/util/compact.xsl b/xmlhelp/util/compact.xsl
new file mode 100644
index 0000000000..781ad31792
--- /dev/null
+++ b/xmlhelp/util/compact.xsl
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+
+<!-- Remove unwanted attributes or/and nodes -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="xml" encoding="UTF-8"/>
+
+ <xsl:strip-space elements="*"/>
+ <xsl:preserve-space elements="paragraph"/>
+
+ <!-- Copy everything -->
+ <xsl:template match="@*|node()|text()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*[normalize-space()]|node()|text()"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- To remove attributes or nodes,
+ simply write a matching template that doesn't do anything.
+ Therefore, it is removed -->
+ <xsl:template match="image/@localize">
+ <xsl:copy>
+ <xsl:apply-templates select="@*[normalize-space()]|node()|text()"/>
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="@localize"/>
+ <xsl:template match="@xml-lang"/>
+ <xsl:template match="alt"/>
+ <xsl:template match="bookmark_value"/>
+ <xsl:template match="comment()"/> <!-- Remove all XML comments -->
+ <xsl:template match="comment"/>
+ <xsl:template match="history"/>
+ <xsl:template match="image/@id"/>
+ <xsl:template match="image/@width"/>
+ <xsl:template match="image/@height"/>
+ <xsl:template match="link/@name"/>
+ <xsl:template match="paragraph/@id"/>
+ <xsl:template match="table/@id"/>
+ <xsl:template match="title/@id"/>
+ <xsl:template match="topic/@id"/>
+ <xsl:template match="topic/@indexer"/>
+ <xsl:template match="topic/@status"/>
+
+</xsl:stylesheet>
diff --git a/xmlhelp/util/embed.xsl b/xmlhelp/util/embed.xsl
new file mode 100644
index 0000000000..0cc067714c
--- /dev/null
+++ b/xmlhelp/util/embed.xsl
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output indent="yes" method="xml"/>
+
+<!-- SPECIFY YOUR FILE SYSTEM ROOT PATH TO THE HELP FILES -->
+<xsl:param name="fsroot" select="'file:///handbuch/WORKBENCH/helpcontent2/source/'"/>
+
+<!--
+######################################################
+All others
+######################################################
+-->
+<xsl:template match="/">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="*|@*|comment()|processing-instruction()|text()">
+ <xsl:copy>
+ <xsl:apply-templates select="*|@*|comment()|processing-instruction()|text()"/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="*|@*|comment()|processing-instruction()|text()" mode="embedded">
+ <xsl:copy>
+ <xsl:apply-templates select="*|@*|comment()|processing-instruction()|text()" mode="embedded"/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="bookmark" mode="embedded" />
+<xsl:template match="ahelp" mode="embedded">
+ <xsl:apply-templates mode="embedded"/>
+</xsl:template>
+
+<xsl:template match="paragraph[@role='heading']">
+ <title>
+ <xsl:apply-templates/>
+ </title>
+</xsl:template>
+
+<xsl:template match="paragraph[@role=*]">
+ <paragraph>
+ <xsl:apply-templates/>
+ </paragraph>
+</xsl:template>
+
+<xsl:template match="sort">
+ <xsl:apply-templates/>
+</xsl:template>
+
+
+<!--
+######################################################
+EMBED
+######################################################
+-->
+<xsl:template match="embed">
+
+ <xsl:variable name="href"><xsl:value-of select="substring-before(concat($fsroot,@href),'#')"/></xsl:variable>
+ <xsl:variable name="anchor"><xsl:value-of select="substring-after(@href,'#')"/></xsl:variable>
+ <xsl:variable name="doc" select="document($href)"/>
+ <xsl:apply-templates select="$doc//section[@id=$anchor]" mode="embedded"/>
+ <xsl:if test="not($doc//section[@id=$anchor])"> <!-- fallback for embeds that actually should be embedvars -->
+ <paragraph role="paragraph"><xsl:apply-templates select="$doc//variable[@id=$anchor]" mode="embedded"/></paragraph>
+ </xsl:if>
+</xsl:template>
+
+<!--
+######################################################
+EMBEDVAR
+######################################################
+-->
+<xsl:template match="embedvar">
+ <xsl:if test="not(@href='text/shared/00/00000004.xhp#wie')"> <!-- special treatment if howtoget links -->
+ <xsl:variable name="href"><xsl:value-of select="substring-before(concat($fsroot,@href),'#')"/></xsl:variable>
+ <xsl:variable name="anchor"><xsl:value-of select="substring-after(@href,'#')"/></xsl:variable>
+ <xsl:variable name="doc" select="document($href)"/>
+ <xsl:apply-templates select="$doc//variable[@id=$anchor]" mode="embedded"/>
+ </xsl:if>
+
+ <!-- FPE: embedvars, that point to "text/shared/00/00000004.xml#wie" will only be resolved in the main_transform -->
+
+</xsl:template>
+</xsl:stylesheet>
diff --git a/xmlhelp/util/idxcaption.xsl b/xmlhelp/util/idxcaption.xsl
new file mode 100644
index 0000000000..398b59af3c
--- /dev/null
+++ b/xmlhelp/util/idxcaption.xsl
@@ -0,0 +1,51 @@
+<!--
+ * 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 .
+-->
+<xsl:stylesheet version="1.0" encoding="UTF-8"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:office="http://openoffice.org/2000/office"
+ xmlns:style="http://openoffice.org/2000/style"
+ xmlns:table="http://openoffice.org/2000/table"
+ xmlns:draw="http://openoffice.org/2000/drawing"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:meta="http://openoffice.org/2000/meta"
+ xmlns:number="http://openoffice.org/2000/datastyle"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:chart="http://openoffice.org/2000/chart"
+ xmlns:help="http://openoffice.org/2000/help"
+ xmlns:index="http://sun.com/2000/XMLSearch"
+ xmlns:text="http://openoffice.org/2000/text">
+
+<xsl:param name="Language" select="'en-US'"/>
+<xsl:output method="text" encoding="UTF-8"/>
+
+<xsl:template match="/">
+ <xsl:apply-templates select="//title" mode="include"/>
+ <xsl:apply-templates select="//paragraph[@role='heading']" mode="include"/>
+</xsl:template>
+
+<xsl:template match="*" mode="include">
+ <xsl:value-of select="."/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="*"/>
+
+</xsl:stylesheet>
+
diff --git a/xmlhelp/util/idxcontent.xsl b/xmlhelp/util/idxcontent.xsl
new file mode 100644
index 0000000000..d06b4f67ba
--- /dev/null
+++ b/xmlhelp/util/idxcontent.xsl
@@ -0,0 +1,131 @@
+<!--
+ * 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 .
+-->
+<xsl:stylesheet version="1.0" encoding="UTF-8"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:office="http://openoffice.org/2000/office"
+ xmlns:style="http://openoffice.org/2000/style"
+ xmlns:table="http://openoffice.org/2000/table"
+ xmlns:draw="http://openoffice.org/2000/drawing"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:meta="http://openoffice.org/2000/meta"
+ xmlns:number="http://openoffice.org/2000/datastyle"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:chart="http://openoffice.org/2000/chart"
+ xmlns:help="http://openoffice.org/2000/help"
+ xmlns:index="http://sun.com/2000/XMLSearch"
+ xmlns:text="http://openoffice.org/2000/text">
+
+<xsl:param name="Language" select="'en-US'"/>
+<xsl:output method="text" encoding="UTF-8"/>
+
+<xsl:template match="helpdocument|body">
+ <xsl:choose>
+ <xsl:when test="meta/topic[@indexer='exclude']"/>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="title">
+ <xsl:value-of select="."/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="table">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="tablecell">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="tablerow">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="list">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="listitem">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="item">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="emph">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="sub">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="sup">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="paragraph">
+ <xsl:value-of select="."/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="section">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="bookmark">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="bookmark_value">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="link">
+ <xsl:apply-templates/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="ahelp[@visibility='visible']">
+ <xsl:value-of select="."/>
+ <xsl:text>&#xA;</xsl:text>
+</xsl:template>
+
+<xsl:template match="*"/>
+
+</xsl:stylesheet>
+
+
diff --git a/xmlhelp/util/main_transform.xsl b/xmlhelp/util/main_transform.xsl
new file mode 100644
index 0000000000..ac1b3abb7d
--- /dev/null
+++ b/xmlhelp/util/main_transform.xsl
@@ -0,0 +1,933 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--***********************************************************************
+ This is the main transformation style sheet for transforming.
+ For use with LibreOffice 4.0+
+ =========================================================================
+ Changes Log
+ May 24 2004 Created
+ Aug 24 2004 Fixed for help2 CWS
+ Aug 27 2004 Added css link, fixed missing embed-mode for variable
+ Removed width/height for images
+ Sep 03 2004 Modularized xsl, added some embedded modes
+ Oct 08 2004 Fixed bug wrong mode "embedded" for links
+ Added embedded modes for embed and embedvar (for cascaded embeds)
+ Added <p> tags around falsely embedded pars and vars
+ Dec 08 2004 #i38483#, fixed wrong handling of web links
+ #i37377#, fixed missing usage of Database parameter for switching
+ Jan 04 2005 #i38905#, fixed buggy branding replacement template
+ Mar 17 2005 #i43972#, added language info to image URL, evaluate Language parameter
+ evaluate new localize attribute in images
+ May 10 2005 #i48785#, fixed wrong setting of distrib variable
+ Aug 16 2005 workaround for #i53365#
+ Aug 19 2005 fixed missing list processing in embedded sections
+ Aug 19 2005 #i53535#, fixed wrong handling of Database parameter
+ Oct 17 2006 #i70462#, disabled sorting to avoid output of error messages to console
+ Jun 15 2009 #i101799#, fixed wrong handling of http URLs with anchors
+***********************************************************************//-->
+
+<!--
+ * 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 .
+-->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output indent="yes" method="html"/>
+
+<!--
+############################
+# Variables and Parameters #
+############################
+//-->
+
+<!-- General Usage -->
+<xsl:variable name="am" select="'&amp;'"/>
+<xsl:variable name="sl" select="'/'"/>
+<xsl:variable name="qt" select="'&quot;'"/>
+
+<!-- generic Icon alt text -->
+<xsl:variable name="alttext" select="'text/shared/00/icon_alt.xhp'"/>
+
+<!-- For calculating pixel sizes -->
+<xsl:variable name="dpi" select="'96'"/>
+<xsl:variable name="dpcm" select="'38'"/>
+
+<!-- Product brand variables used in the help files -->
+<xsl:variable name="brand1" select="'$[officename]'"/>
+<xsl:variable name="brand2" select="'$[officeversion]'"/>
+<xsl:variable name="brand3" select="'%PRODUCTNAME'"/>
+<xsl:variable name="brand4" select="'%PRODUCTVERSION'"/>
+
+<!-- meta data variables from the help file -->
+<xsl:variable name="filename" select="/helpdocument/meta/topic/filename"/>
+<xsl:variable name="title" select="/helpdocument/meta/topic/title"/>
+
+<!-- Module and the corresponding switching values-->
+<xsl:param name="Database" select="'swriter'"/>
+<xsl:variable name="module" select="$Database"/>
+<xsl:variable name="appl">
+ <xsl:choose>
+ <xsl:when test="$module = 'swriter'"><xsl:value-of select="'WRITER'"/></xsl:when>
+ <xsl:when test="$module = 'scalc'"><xsl:value-of select="'CALC'"/></xsl:when>
+ <xsl:when test="$module = 'sdraw'"><xsl:value-of select="'DRAW'"/></xsl:when>
+ <xsl:when test="$module = 'simpress'"><xsl:value-of select="'IMPRESS'"/></xsl:when>
+ <xsl:when test="$module = 'schart'"><xsl:value-of select="'CHART'"/></xsl:when>
+ <xsl:when test="$module = 'sbasic'"><xsl:value-of select="'BASIC'"/></xsl:when>
+ <xsl:when test="$module = 'smath'"><xsl:value-of select="'MATH'"/></xsl:when>
+ </xsl:choose>
+</xsl:variable>
+
+ <!-- the other parameters given by the help caller -->
+<xsl:param name="System" select="'WIN'"/>
+<xsl:param name="productname" select="'Office'"/>
+<xsl:param name="productversion" select="''"/>
+<xsl:variable name="pversion">
+ <xsl:value-of select="translate($productversion,' ','')"/>
+</xsl:variable>
+<!-- this is were the images are -->
+<xsl:param name="imgtheme" select="''"/>
+<xsl:param name="Id" />
+<xsl:param name="Language" select="'en-US'"/>
+<xsl:variable name="lang" select="$Language"/>
+
+<xsl:param name="ExtensionId" select="''"/>
+<xsl:param name="ExtensionPath" select="''"/>
+
+
+ <!-- parts of help and image urls -->
+<xsl:variable name="help_url_prefix" select="'vnd.sun.star.help://'"/>
+<xsl:variable name="img_url_prefix" select="concat('vnd.libreoffice.image://',$imgtheme,'/')"/>
+<xsl:variable name="img_url_internal" select="'vnd.libreoffice.image://helpimg/'"/>
+<xsl:variable name="urlpost" select="concat('?Language=',$lang,$am,'System=',$System,$am,'UseDB=no')"/>
+<xsl:variable name="urlpre" select="$help_url_prefix" />
+<xsl:variable name="linkprefix" select="$urlpre"/>
+<xsl:variable name="linkpostfix" select="$urlpost"/>
+
+<xsl:variable name="css" select="'default.css'"/>
+
+<!-- images for notes, tips and warnings -->
+<xsl:variable name="note_img" select="concat($img_url_internal,'media/helpimg/note.png')"/>
+<xsl:variable name="tip_img" select="concat($img_url_internal,'media/helpimg/tip.png')"/>
+<xsl:variable name="warning_img" select="concat($img_url_internal,'media/helpimg/warning.png')"/>
+
+<!--
+#############
+# Templates #
+#############
+//-->
+
+<!-- Create the document skeleton -->
+<xsl:template match="/">
+ <xsl:variable name="csslink" select="concat($urlpre,'/',$urlpost)"/>
+ <html>
+ <head>
+ <title><xsl:value-of select="$title"/></title>
+ <link href="{$csslink}" rel="Stylesheet" type="text/css" /> <!-- stylesheet link -->
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
+ </head>
+ <body lang="{$lang}">
+ <xsl:apply-templates select="/helpdocument/body"/>
+ </body>
+ </html>
+</xsl:template>
+
+<!-- AHELP -->
+<xsl:template match="ahelp">
+ <xsl:if test="not(@visibility='hidden')"><span class="avis"><xsl:apply-templates /></span></xsl:if>
+</xsl:template>
+
+<!-- ALT -->
+<xsl:template match="alt"/>
+
+<!-- BOOKMARK -->
+<xsl:template match="bookmark">
+ <a name="{@id}"></a>
+ <xsl:choose>
+ <xsl:when test="starts-with(@branch,'hid')" />
+ <xsl:otherwise><xsl:apply-templates /></xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+<xsl:template match="bookmark" mode="embedded" />
+
+<!-- BOOKMARK_VALUE -->
+<xsl:template match="bookmark_value" />
+
+<!-- BR -->
+<xsl:template match="br"><br /></xsl:template>
+
+<!-- CAPTION -->
+<xsl:template match="caption" />
+
+<!-- CASE -->
+<xsl:template match="case"><xsl:call-template name="insertcase" /></xsl:template>
+<xsl:template match="case" mode="embedded">
+ <xsl:call-template name="insertcase">
+ <xsl:with-param name="embedded" select="'yes'"/>
+ </xsl:call-template>
+</xsl:template>
+
+<!-- CASEINLINE -->
+<xsl:template match="caseinline"><xsl:call-template name="insertcase" /></xsl:template>
+<xsl:template match="caseinline" mode="embedded">
+ <xsl:call-template name="insertcase">
+ <xsl:with-param name="embedded" select="'yes'"/>
+ </xsl:call-template>
+</xsl:template>
+
+<!-- COMMENT -->
+<xsl:template match="comment" />
+<xsl:template match="comment" mode="embedded"/>
+
+<!-- CREATED -->
+<xsl:template match="created" />
+
+<!-- DEFAULT -->
+<xsl:template match="default"><xsl:call-template name="insertdefault" /></xsl:template>
+<xsl:template match="default" mode="embedded">
+ <xsl:call-template name="insertdefault">
+ <xsl:with-param name="embedded" select="'yes'"/>
+ </xsl:call-template>
+</xsl:template>
+
+<!-- DEFAULTINLINE -->
+<xsl:template match="defaultinline"><xsl:call-template name="insertdefault" /></xsl:template>
+<xsl:template match="defaultinline" mode="embedded">
+ <xsl:call-template name="insertdefault">
+ <xsl:with-param name="embedded" select="'yes'"/>
+ </xsl:call-template>
+</xsl:template>
+
+<!-- EMBED -->
+<xsl:template match="embed"><xsl:call-template name="resolveembed"/></xsl:template>
+<xsl:template match="embed" mode="embedded"><xsl:call-template name="resolveembed"/></xsl:template>
+
+<!-- EMBEDVAR -->
+<xsl:template match="embedvar"><xsl:call-template name="resolveembedvar"/></xsl:template>
+<xsl:template match="embedvar" mode="embedded"><xsl:call-template name="resolveembedvar"/></xsl:template>
+
+<!-- EMPH -->
+<xsl:template match="emph">
+ <span class="emph"><xsl:apply-templates /></span>
+</xsl:template>
+<xsl:template match="emph" mode="embedded">
+ <span class="emph"><xsl:apply-templates /></span>
+</xsl:template>
+
+<!-- SUB -->
+<xsl:template match="sub">
+ <sub><xsl:apply-templates /></sub>
+</xsl:template>
+<xsl:template match="sub" mode="embedded">
+ <sub><xsl:apply-templates /></sub>
+</xsl:template>
+
+<!-- SUP -->
+<xsl:template match="sup">
+ <sup><xsl:apply-templates /></sup>
+</xsl:template>
+<xsl:template match="sup" mode="embedded">
+ <sup><xsl:apply-templates /></sup>
+</xsl:template>
+
+<!-- FILENAME -->
+<xsl:template match="filename" />
+
+<!-- HISTORY -->
+<xsl:template match="history" />
+
+<!-- IMAGE -->
+<xsl:template match="image"><xsl:call-template name="insertimage"/></xsl:template>
+<xsl:template match="image" mode="embedded"><xsl:call-template name="insertimage"/></xsl:template>
+
+<!-- ITEM -->
+<xsl:template match="item"><span class="{@type}"><xsl:apply-templates /></span></xsl:template>
+<xsl:template match="item" mode="embedded"><span class="{@type}"><xsl:apply-templates /></span></xsl:template>
+
+<!-- LINK -->
+<xsl:template match="link">
+ <xsl:choose> <!-- don't insert the heading link to itself -->
+ <xsl:when test="(concat('/',@href) = /helpdocument/meta/topic/filename) or (@href = /helpdocument/meta/topic/filename)">
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="contains(child::embedvar/@href,'/00/00000004.xhp#wie')"> <!-- special treatment of howtoget links -->
+ <xsl:call-template name="insert_howtoget">
+ <xsl:with-param name="linkhref" select="@href"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="createlink" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+<xsl:template match="link" mode="embedded">
+ <xsl:call-template name="createlink"/>
+</xsl:template>
+
+<!-- LIST -->
+<xsl:template match="list">
+ <xsl:choose>
+ <xsl:when test="@type='ordered'">
+ <ol>
+ <xsl:if test="@startwith">
+ <xsl:attribute name="start"><xsl:value-of select="@startwith"/></xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates />
+ </ol>
+ </xsl:when>
+ <xsl:otherwise>
+ <ul><xsl:apply-templates /></ul>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="list" mode="embedded">
+ <xsl:choose>
+ <xsl:when test="@type='ordered'">
+ <ol>
+ <xsl:if test="@startwith">
+ <xsl:attribute name="start"><xsl:value-of select="@startwith"/></xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates mode="embedded"/>
+ </ol>
+ </xsl:when>
+ <xsl:otherwise>
+ <ul><xsl:apply-templates mode="embedded"/></ul>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- LISTITEM -->
+<xsl:template match="listitem">
+ <li><xsl:apply-templates /></li>
+</xsl:template>
+
+<xsl:template match="listitem" mode="embedded">
+ <li><xsl:apply-templates mode="embedded"/></li>
+</xsl:template>
+
+<!-- META, SEE HEADER -->
+<xsl:template match="meta" />
+
+<!-- OBJECT (UNUSED) -->
+<xsl:template match="object" />
+
+<!-- PARAGRAPH -->
+<xsl:template match="paragraph">
+ <xsl:choose>
+
+ <xsl:when test="@role='heading'">
+ <xsl:call-template name="insertheading">
+ <xsl:with-param name="level" select="@level"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains(' note warning tip ',@role)">
+ <xsl:call-template name="insertnote">
+ <xsl:with-param name="type" select="@role" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains(descendant::embedvar/@href,'/00/00000004.xhp#wie')"> <!-- special treatment of howtoget links -->
+ <xsl:apply-templates />
+ </xsl:when>
+
+ <xsl:when test="@role='bascode'">
+ <xsl:call-template name="insertbascode" />
+ </xsl:when>
+
+ <xsl:when test="@role='logocode'">
+ <xsl:call-template name="insertlogocode" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="insertpara" />
+ </xsl:otherwise>
+
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="paragraph" mode="embedded">
+ <xsl:choose>
+
+ <xsl:when test="@role='heading'"> <!-- increase the level of headings that are embedded -->
+ <!--
+ The internal sablotron processor does not seem to support the number function.
+ Therefore, we need a workaround for
+ <xsl:variable name="level"><xsl:value-of select="number(@level)+1"/></xsl:variable>
+ -->
+ <xsl:variable name="newlevel">
+ <xsl:choose>
+ <xsl:when test="@level='1'"><xsl:value-of select="'2'"/></xsl:when>
+ <xsl:when test="@level='2'"><xsl:value-of select="'2'"/></xsl:when>
+ <xsl:when test="@level='3'"><xsl:value-of select="'3'"/></xsl:when>
+ <xsl:when test="@level='4'"><xsl:value-of select="'4'"/></xsl:when>
+ <xsl:when test="@level='5'"><xsl:value-of select="'5'"/></xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:call-template name="insertheading">
+ <xsl:with-param name="level" select="$newlevel"/>
+ <xsl:with-param name="embedded" select="'yes'"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains(' note warning tip ',@role)">
+ <xsl:call-template name="insertnote">
+ <xsl:with-param name="type" select="@role" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains(descendant::embedvar/@href,'/00/00000004.xhp#wie')"> <!-- special treatment of howtoget links -->
+ <xsl:apply-templates />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="insertpara" />
+ </xsl:otherwise>
+
+ </xsl:choose>
+</xsl:template>
+
+
+<!-- SECTION -->
+<xsl:template match="section">
+ <a name="{@id}"></a>
+
+ <xsl:choose>
+
+ <xsl:when test="@id='relatedtopics'">
+ <div class="relatedtopics">
+ <xsl:variable name="href"><xsl:value-of select="concat($urlpre,'shared/text/shared/00/00000004.xhp',$urlpost)"/></xsl:variable>
+ <xsl:variable name="anchor"><xsl:value-of select="'related'"/></xsl:variable>
+ <xsl:variable name="doc" select="document($href)"/>
+ <p class="related">
+ <xsl:apply-templates select="$doc//variable[@id=$anchor]"/>
+ </p>
+ <div class="relatedbody">
+ <xsl:apply-templates />
+ </div>
+ </div>
+ </xsl:when>
+
+ <xsl:when test="@id='howtoget'">
+ <xsl:call-template name="insert_howtoget" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+
+ </xsl:choose>
+
+</xsl:template>
+
+
+<!-- SECTION -->
+<xsl:template match="section" mode="embedded">
+ <a name="{@id}"></a>
+ <xsl:apply-templates mode="embedded"/>
+</xsl:template>
+
+<!-- SORT -->
+<xsl:template match="sort" >
+ <xsl:apply-templates><xsl:sort select="descendant::paragraph"/></xsl:apply-templates>
+</xsl:template>
+<xsl:template match="sort" mode="embedded">
+ <xsl:apply-templates><xsl:sort select="descendant::paragraph"/></xsl:apply-templates>
+</xsl:template>
+
+<!-- SWITCH -->
+<xsl:template match="switch"><xsl:apply-templates /></xsl:template>
+<xsl:template match="switch" mode="embedded"><xsl:apply-templates /></xsl:template>
+
+<!-- SWITCHINLINE -->
+<xsl:template match="switchinline"><xsl:apply-templates /></xsl:template>
+<xsl:template match="switchinline" mode="embedded"><xsl:apply-templates mode="embedded"/></xsl:template>
+
+<!-- TABLE -->
+<xsl:template match="table"><xsl:call-template name="inserttable"/></xsl:template>
+<xsl:template match="table" mode="embedded"><xsl:call-template name="inserttable"/></xsl:template>
+
+<!-- TABLECELL -->
+<xsl:template match="tablecell"><td valign="top"><xsl:apply-templates /></td></xsl:template>
+<xsl:template match="tablecell" mode="icontable"><td valign="top"><xsl:apply-templates/></td></xsl:template>
+<xsl:template match="tablecell" mode="embedded"><td valign="top"><xsl:apply-templates mode="embedded"/></td></xsl:template>
+
+<!-- TABLEROW -->
+<xsl:template match="tablerow"><tr><xsl:apply-templates /></tr></xsl:template>
+<xsl:template match="tablerow" mode="icontable"><tr><xsl:apply-templates mode="icontable"/></tr></xsl:template>
+<xsl:template match="tablerow" mode="embedded"><tr><xsl:apply-templates mode="embedded"/></tr></xsl:template>
+
+<!-- TITLE -->
+<xsl:template match="title"/>
+
+<!-- TOPIC -->
+<xsl:template match="topic"/>
+
+<!-- VARIABLE -->
+<xsl:template match="variable"><a name="{@id}"></a><xsl:apply-templates /></xsl:template>
+<xsl:template match="variable" mode="embedded"><a name="{@id}"></a><xsl:apply-templates mode="embedded"/></xsl:template>
+
+<xsl:template match="text()">
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string"><xsl:value-of select="."/></xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="text()" mode="embedded">
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string"><xsl:value-of select="."/></xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+<!-- In case of missing help files -->
+<xsl:template match="help-id-missing"><xsl:value-of select="$Id"/></xsl:template>
+
+<!--
+###################
+# NAMED TEMPLATES #
+###################
+//-->
+
+<!-- Branding -->
+<xsl:template name="brand" >
+ <xsl:param name="string"/>
+
+ <xsl:choose>
+
+ <xsl:when test="contains($string,$brand1)">
+ <xsl:variable name="newstr">
+ <xsl:value-of select="substring-before($string,$brand1)"/>
+ <xsl:value-of select="$productname"/>
+ <xsl:value-of select="substring-after($string,$brand1)"/>
+ </xsl:variable>
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string" select="$newstr"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains($string,$brand2)">
+ <xsl:variable name="newstr">
+ <xsl:value-of select="substring-before($string,$brand2)"/>
+ <xsl:value-of select="$pversion"/>
+ <xsl:value-of select="substring-after($string,$brand2)"/>
+ </xsl:variable>
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string" select="$newstr"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains($string,$brand3)">
+ <xsl:variable name="newstr">
+ <xsl:value-of select="substring-before($string,$brand3)"/>
+ <xsl:value-of select="$productname"/>
+ <xsl:value-of select="substring-after($string,$brand3)"/>
+ </xsl:variable>
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string" select="$newstr"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="contains($string,$brand4)">
+ <xsl:variable name="newstr">
+ <xsl:value-of select="substring-before($string,$brand4)"/>
+ <xsl:value-of select="$pversion"/>
+ <xsl:value-of select="substring-after($string,$brand4)"/>
+ </xsl:variable>
+ <xsl:call-template name="brand">
+ <xsl:with-param name="string" select="$newstr"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$string"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+
+<!-- Insert Paragraph -->
+<xsl:template name="insertpara">
+ <xsl:variable name="role">
+ <xsl:choose>
+ <xsl:when test="ancestor::table">
+ <xsl:value-of select="concat(@role,'intable')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@role"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <p class="{$role}"><xsl:apply-templates /></p>
+</xsl:template>
+
+<!-- Insert Basic code snippet -->
+<xsl:template name="insertbascode">
+ <pre><xsl:apply-templates /></pre>
+</xsl:template>
+
+<!-- Insert Logo code snippet -->
+<xsl:template name="insertlogocode">
+ <pre><xsl:apply-templates /></pre>
+</xsl:template>
+
+<!-- Insert "How to get Link" -->
+<xsl:template name="insert_howtoget">
+ <xsl:param name="linkhref" />
+ <xsl:variable name="archive" select="'shared'"/>
+ <xsl:variable name="tmp_href"><xsl:value-of select="concat($urlpre,'shared/text/shared/00/00000004.xhp',$urlpost)"/></xsl:variable>
+ <xsl:variable name="tmp_doc" select="document($tmp_href)"/>
+ <table class="howtoget" width="100%" border="1" cellpadding="3" cellspacing="0">
+ <tr>
+ <td>
+ <p class="howtogetheader"><xsl:apply-templates select="$tmp_doc//variable[@id='wie']"/></p>
+ <div class="howtogetbody">
+ <xsl:choose>
+ <xsl:when test="$linkhref = ''"> <!-- new style -->
+ <xsl:apply-templates/>
+ </xsl:when>
+ <xsl:otherwise> <!-- old style -->
+ <xsl:variable name="archive1"><xsl:value-of select="concat(substring-before(substring-after($linkhref,'text/'),'/'),'/')"/></xsl:variable>
+ <xsl:variable name="href"><xsl:value-of select="concat($urlpre,$archive1,substring-before($linkhref,'#'),$urlpost)"/></xsl:variable>
+ <xsl:variable name="anc"><xsl:value-of select="substring-after($linkhref,'#')"/></xsl:variable>
+ <xsl:variable name="docum" select="document($href)"/>
+
+ <xsl:call-template name="insertembed">
+ <xsl:with-param name="doc" select="$docum" />
+ <xsl:with-param name="anchor" select="$anc" />
+ </xsl:call-template>
+
+ </xsl:otherwise>
+ </xsl:choose>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+</xsl:template>
+
+<!-- Create a link -->
+<xsl:template name="createlink">
+<xsl:variable name="archive"><xsl:value-of select="concat(substring-before(substring-after(@href,'text/'),'/'),'/')"/></xsl:variable>
+<xsl:variable name="dbpostfix"><xsl:call-template name="createDBpostfix"><xsl:with-param name="archive" select="$archive"/></xsl:call-template></xsl:variable>
+ <xsl:choose>
+ <xsl:when test="starts-with(@href,'http://') or starts-with(@href,'https://')"> <!-- web links -->
+ <a href="{@href}"><xsl:apply-templates /></a>
+ </xsl:when>
+ <xsl:when test="contains(@href,'#')">
+ <xsl:variable name="anchor"><xsl:value-of select="concat('#',substring-after(@href,'#'))"/></xsl:variable>
+ <xsl:variable name="href"><xsl:value-of select="concat($linkprefix,$archive,substring-before(@href,'#'),$linkpostfix,$dbpostfix,$anchor)"/></xsl:variable>
+ <a href="{$href}"><xsl:apply-templates /></a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="href"><xsl:value-of select="concat($linkprefix,$archive,@href,$linkpostfix,$dbpostfix)"/></xsl:variable>
+ <a href="{$href}"><xsl:apply-templates /></a>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Insert Note, Warning, or Tip -->
+<xsl:template name="insertnote">
+ <xsl:param name="type" /> <!-- note, tip, or warning -->
+ <xsl:variable name="imgsrc">
+ <xsl:choose>
+ <xsl:when test="$type='note'"><xsl:value-of select="$note_img"/></xsl:when>
+ <xsl:when test="$type='tip'"><xsl:value-of select="$tip_img"/></xsl:when>
+ <xsl:when test="$type='warning'"><xsl:value-of select="$warning_img"/></xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="dbpostfix"><xsl:call-template name="createDBpostfix"><xsl:with-param name="archive" select="'shared'"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="alt">
+ <xsl:variable name="href"><xsl:value-of select="concat($urlpre,'shared/',$alttext,$urlpost,$dbpostfix)"/></xsl:variable>
+ <xsl:variable name="anchor"><xsl:value-of select="concat('alt_',$type)"/></xsl:variable>
+ <xsl:variable name="doc" select="document($href)"/>
+ <xsl:apply-templates select="$doc//variable[@id=$anchor]" mode="embedded"/>
+ </xsl:variable>
+ <div class="{$type}">
+ <table border="0" class="{$type}" cellspacing="0" cellpadding="5">
+ <tr>
+ <td><img src="{$imgsrc}" alt="{$alt}" title="{$alt}"/></td>
+ <td><xsl:apply-templates /></td>
+ </tr>
+ </table>
+ </div>
+ <br/>
+</xsl:template>
+
+<!-- Insert a heading -->
+<xsl:template name="insertheading">
+ <xsl:param name="level" />
+ <xsl:param name="embedded" />
+ <xsl:text disable-output-escaping="yes">&lt;h</xsl:text><xsl:value-of select="$level"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text disable-output-escaping="yes">&lt;/h</xsl:text><xsl:value-of select="$level"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
+</xsl:template>
+
+<!-- Evaluate a case or caseinline switch -->
+<xsl:template name="insertcase">
+ <xsl:param name="embedded" />
+ <xsl:choose>
+ <xsl:when test="parent::switch[@select='sys'] or parent::switchinline[@select='sys']">
+ <xsl:if test="@select = $System">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::switch[@select='appl'] or parent::switchinline[@select='appl']">
+ <xsl:if test="@select = $appl">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::switch[@select='distrib'] or parent::switchinline[@select='distrib']">
+ <xsl:if test="@select = $distrib">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Evaluate a default or defaultinline switch -->
+<xsl:template name="insertdefault">
+ <xsl:param name="embedded" />
+
+ <xsl:choose>
+ <xsl:when test="parent::switch[@select='sys'] or parent::switchinline[@select='sys']">
+ <xsl:if test="not(../child::case[@select=$System]) and not(../child::caseinline[@select=$System])">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::switch[@select='appl'] or parent::switchinline[@select='appl']">
+ <xsl:if test="not(../child::case[@select=$appl]) and not(../child::caseinline[@select=$appl])">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::switch[@select='distrib'] or parent::switchinline[@select='distrib']">
+ <xsl:if test="not(../child::case[@select=$distrib]) and not(../child::caseinline[@select=$distrib])">
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!-- evaluate embeds -->
+<xsl:template name="insertembed">
+ <xsl:param name="doc" />
+ <xsl:param name="anchor" />
+ <!-- different embed targets (also falsely used embed instead embedvar) -->
+ <xsl:choose>
+ <xsl:when test="$doc//section[@id=$anchor]"> <!-- first test for a section of that name -->
+ <xsl:apply-templates select="$doc//section[@id=$anchor]" mode="embedded"/>
+ </xsl:when>
+ <xsl:when test="$doc//paragraph[@id=$anchor]"> <!-- then test for a para of that name -->
+ <p class="embedded">
+ <xsl:apply-templates select="$doc//paragraph[@id=$anchor]" mode="embedded"/>
+ </p>
+ </xsl:when>
+ <xsl:when test="$doc//variable[@id=$anchor]"> <!-- then test for a variable of that name -->
+ <p class="embedded">
+ <xsl:apply-templates select="$doc//variable[@id=$anchor]" mode="embedded"/>
+ </p>
+ </xsl:when>
+ <xsl:otherwise> <!-- then give up -->
+ <p class="bug">D'oh! You found a bug (<xsl:value-of select="@href"/> not found).</p>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Insert an image -->
+<xsl:template name="insertimage">
+ <xsl:variable name="src">
+ <xsl:choose>
+ <xsl:when test="starts-with(@src,'media/')">
+ <xsl:value-of select="concat($img_url_internal,@src)"/>
+ </xsl:when>
+ <xsl:when test="not($ExtensionId='') and starts-with(@src,$ExtensionId)">
+ <xsl:value-of select="concat($ExtensionPath,'/',@src)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="@localize='true'">
+ <xsl:value-of select="concat($img_url_prefix,@src,'?lang=',$lang)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($img_url_prefix,@src)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!--<xsl:variable name="src"><xsl:value-of select="concat($img_url_prefix,@src)"/></xsl:variable>-->
+ <xsl:variable name="alt"><xsl:value-of select="./alt"/></xsl:variable>
+ <xsl:variable name="width" select="''"/> <!-- Images don't all have the correct size -->
+ <xsl:variable name="height" select="''"/><!-- Image don't all have the correct size -->
+ <img src="{$src}" alt="{$alt}" title="{$alt}">
+ <xsl:if test="not($width='')"><xsl:attribute name="width"><xsl:value-of select="$width"/></xsl:attribute></xsl:if>
+ <xsl:if test="not($height='')"><xsl:attribute name="height"><xsl:value-of select="$height"/></xsl:attribute></xsl:if>
+ </img>
+</xsl:template>
+
+<!-- Insert a Table -->
+<xsl:template name="inserttable">
+ <xsl:variable name="imgsrc"> <!-- see if we are in an image table -->
+ <xsl:value-of select="tablerow/tablecell[1]/paragraph[1]/image/@src"/>
+ </xsl:variable>
+
+ <xsl:choose>
+
+ <xsl:when test="count(descendant::tablecell)=1">
+ <table border="0" class="onecell" cellpadding="0" cellspacing="0">
+ <xsl:apply-templates />
+ </table>
+ </xsl:when>
+
+ <xsl:when test="descendant::tablecell[1]/descendant::image">
+ <table border="0" class="icontable" cellpadding="5" cellspacing="0">
+ <xsl:apply-templates mode="icontable"/>
+ </table>
+ </xsl:when>
+
+ <xsl:when test="@class='wide'">
+ <table border="1" class="{@class}" cellpadding="0" cellspacing="0" width="100%" >
+ <xsl:apply-templates />
+ </table>
+ </xsl:when>
+
+ <xsl:when test="not(@class='')">
+ <table border="1" class="{@class}" cellpadding="0" cellspacing="0" >
+ <xsl:apply-templates />
+ </table>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <table border="1" class="border" cellpadding="0" cellspacing="0" >
+ <xsl:apply-templates />
+ </table>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <br/>
+</xsl:template>
+
+<xsl:template name="resolveembed">
+ <div class="embedded">
+ <xsl:variable name="archive"><xsl:value-of select="concat(substring-before(substring-after(@href,'text/'),'/'),'/')"/></xsl:variable>
+ <xsl:variable name="dbpostfix"><xsl:call-template name="createDBpostfix"><xsl:with-param name="archive" select="$archive"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="href"><xsl:value-of select="concat($urlpre,$archive,substring-before(@href,'#'),$urlpost,$dbpostfix)"/></xsl:variable>
+ <xsl:variable name="anc"><xsl:value-of select="substring-after(@href,'#')"/></xsl:variable>
+ <xsl:variable name="docum" select="document($href)"/>
+
+ <xsl:call-template name="insertembed">
+ <xsl:with-param name="doc" select="$docum" />
+ <xsl:with-param name="anchor" select="$anc" />
+ </xsl:call-template>
+
+ </div>
+</xsl:template>
+
+<xsl:template name="resolveembedvar">
+ <xsl:if test="not(@href='text/shared/00/00000004.xhp#wie')"> <!-- special treatment if howtoget links -->
+ <xsl:variable name="archive"><xsl:value-of select="concat(substring-before(substring-after(@href,'text/'),'/'),'/')"/></xsl:variable>
+ <xsl:variable name="dbpostfix"><xsl:call-template name="createDBpostfix"><xsl:with-param name="archive" select="$archive"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="href"><xsl:value-of select="concat($urlpre,$archive,substring-before(@href,'#'),$urlpost,$dbpostfix)"/></xsl:variable>
+ <xsl:variable name="anchor"><xsl:value-of select="substring-after(@href,'#')"/></xsl:variable>
+ <xsl:variable name="doc" select="document($href)"/>
+ <xsl:choose>
+ <xsl:when test="$doc//variable[@id=$anchor]"> <!-- test for a variable of that name -->
+ <xsl:apply-templates select="$doc//variable[@id=$anchor]" mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise> <!-- or give up -->
+ <span class="bug">[<xsl:value-of select="@href"/> not found].</span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+</xsl:template>
+
+<!-- Apply -->
+<xsl:template name="apply">
+ <xsl:param name="embedded" />
+ <xsl:choose>
+ <xsl:when test="$embedded = 'yes'">
+ <xsl:apply-templates mode="embedded"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="createDBpostfix">
+ <xsl:param name="archive"/>
+ <xsl:variable name="newDB">
+ <xsl:choose>
+ <xsl:when test="(substring($archive,1,6) = 'shared')"><xsl:value-of select="$Database"/></xsl:when>
+ <xsl:otherwise><xsl:value-of select="substring-before($archive,'/')"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:value-of select="concat($am,'DbPAR=',$newDB)"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/xmlhelp/util/ucpchelp1.component b/xmlhelp/util/ucpchelp1.component
new file mode 100644
index 0000000000..dc1934ddfd
--- /dev/null
+++ b/xmlhelp/util/ucpchelp1.component
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="CHelpContentProvider"
+ constructor="CHelpContentProvider_get_implementation">
+ <service name="com.sun.star.help.XMLHelp"/>
+ <service name="com.sun.star.ucb.HelpContentProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.help.TreeViewImpl"
+ constructor="com_sun_star_help_TreeViewImpl_get_implementation">
+ <service name="com.sun.star.help.TreeView"/>
+ <service name="com.sun.star.ucb.HiearchyDataSource"/>
+ </implementation>
+</component>