diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /xmlhelp/source/cxxhelp/provider | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xmlhelp/source/cxxhelp/provider')
23 files changed, 6863 insertions, 0 deletions
diff --git a/xmlhelp/source/cxxhelp/provider/content.cxx b/xmlhelp/source/cxxhelp/provider/content.cxx new file mode 100644 index 000000000..4ad2c3fa7 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/content.cxx @@ -0,0 +1,444 @@ +/* -*- 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 "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() +{ + uno::Sequence<OUString> aSNS { "com.sun.star.ucb.CHelpContent" }; + + return aSNS; +} + +// 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( + const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< ucb::XContentProvider >& xProvider, + const uno::Sequence< beans::Property >& seq, + const URLParameter& rURLParameter, + Databases* pDatabases ) + : m_xContext( xContext ), + m_xProvider( xProvider ), + m_seq( seq ), + m_aURLParameter( rURLParameter ), + m_pDatabases( pDatabases ) + { + } + + 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( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< ucb::XContentProvider >& xProvider, + const uno::Sequence< beans::Property >& seq, + const URLParameter& rURLParameter, + Databases* pDatabases ) + : m_xContext( rxContext ), + m_xProvider( xProvider ), + m_seq( seq ), + m_aURLParameter( rURLParameter ), + m_pDatabases( pDatabases ) + { + } + + 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()); + uno::Sequence< beans::Property > props(getProperties(Environment)); + // No properties can be set + std::transform(propertyValues.begin(), propertyValues.end(), ret.begin(), + [&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( 2 ); + seq[0] = "Heading"; + seq[1] = "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 uno::Reference< sdbc::XRow >( xRow.get() ); +} + +/* 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 000000000..fe372e1fd --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/content.hxx @@ -0,0 +1,102 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_CONTENT_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_CONTENT_HXX + +#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 ); + }; + +} + +#endif + +/* 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 000000000..15ad6ba6a --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/contentcaps.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 <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); + + sal_Int32 idx = 0; + props[idx++] = + beans::Property( + "ContentType", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "IsReadOnly", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "IsErrorDocument", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "IsDocument", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "IsFolder", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "Title", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + if( withMediaType ) + props[idx++] = + beans::Property( + "MediaType", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + if( isModule ) + { + props[idx++] = + beans::Property( + "Order", + -1, + cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "KeywordList", + -1, + cppu::UnoType<uno::Sequence< OUString >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "KeywordRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "KeywordTitleForRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "KeywordAnchorForRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + props[idx++] = + beans::Property( + "SearchScopes", + -1, + cppu::UnoType<uno::Sequence< OUString >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + } + + if( isFile ) + { + props[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 000000000..7a25dba31 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/databases.cxx @@ -0,0 +1,1804 @@ +/* -*- 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 <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 <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/storagehelper.hxx> + +#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 ) +{ + osl::MutexGuard aGuard( m_aMutex ); + 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; +} + +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()), + newProdName( "$[officename]" ), + newProdVersion( "$[officeversion]" ), + prodName( "%PRODUCTNAME" ), + prodVersion( "%PRODUCTVERSION" ), + vendName( "%VENDORNAME" ), + vendVersion( "%VENDORVERSION" ), + vendShort( "%VENDORSHORT" ) +{ + 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() +{ + osl::MutexGuard aGuard( m_aMutex ); + + 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( const OUString& Module, + const OUString& Language ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OUString key = processLang(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( getInstallPathAsURL() + 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 ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + 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( getInstallPathAsURL() + rFB, aDirItem)) + { + ret = rFB; + m_aLangSet[ Language ] = ret; + break; // for + } + } + } + else + ret = it->second; + + return ret; +} + +helpdatafileproxy::Hdf* Databases::getHelpDataFile( const OUString& Database, + const OUString& Language, bool helpText, + const OUString* pExtensionPath ) +{ + if( Database.isEmpty() || Language.isEmpty() ) + return nullptr; + + osl::MutexGuard aGuard( m_aMutex ); + + OUString aFileExt( helpText ? OUString(".ht") : OUString(".db") ); + OUString dbFileName = "/" + Database + aFileExt; + OUString key; + if( pExtensionPath == nullptr ) + key = processLang( 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(*pExtensionPath) + Language + dbFileName; + else + fileURL = getInstallPathAsURL() + 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( const OUString& Language ) +{ + OUString key = Language; + + osl::MutexGuard aGuard( m_aMutex ); + + 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 const & ky, + OUString const & data ) + : key( ky ) +{ + pDatabases->replaceName( key ); + init( pDatabases,pHdf,data ); +} + +void KeywordInfo::KeywordElement::init( Databases const *pDatabases,helpdatafileproxy::Hdf* pHdf,const OUString& ids ) +{ + std::vector< OUString > id,anchor; + int idx = -1,k; + for (;;) + { + k = ++idx; + idx = ids.indexOf( ';', k ); + if( idx == -1 ) + break; + int h = ids.indexOf( '#', k ); + if( h < idx ) + { + // found an anchor + id.push_back( ids.copy( k, h-k ) ); + anchor.push_back( ids.copy( h+1, idx-h-1 ) ); + } + else + { + id.push_back( ids.copy( k, idx-k ) ); + anchor.emplace_back( ); + } + } + + listId.realloc( id.size() ); + listAnchor.realloc( id.size() ); + listTitle.realloc( id.size() ); + + for( size_t i = 0; i < id.size(); ++i ) + { + listId[i] = id[i]; + listAnchor[i] = anchor[i]; + + helpdatafileproxy::HDFData aHDFData; + const char* pData = nullptr; + + if( pHdf ) + { + OString idi( id[i].getStr(),id[i].getLength(),RTL_TEXTENCODING_UTF8 ); + bool bSuccess = pHdf->getValueForKey( idi, aHDFData ); + if( bSuccess ) + pData = aHDFData.getData(); + } + + DbtToStringConverter converter( pData ); + + OUString title = converter.getTitle(); + pDatabases->replaceName( title ); + listTitle[i] = title; + } +} + +KeywordInfo::KeywordInfo( const std::vector< KeywordElement >& aVec ) + : listKey( aVec.size() ), + listId( aVec.size() ), + listAnchor( aVec.size() ), + listTitle( aVec.size() ) +{ + for( size_t i = 0; i < aVec.size(); ++i ) + { + listKey[i] = aVec[i].key; + listId[i] = aVec[i].listId; + listAnchor[i] = aVec[i].listAnchor; + listTitle[i] = aVec[i].listTitle; + } +} + +bool Databases::checkModuleMatchForExtension + ( const OUString& 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 ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OUString key = processLang(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( 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( 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( 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( const OUString& jar, + const OUString& Language ) +{ + if( jar.isEmpty() || Language.isEmpty() ) + { + return Reference< XHierarchicalNameAccess >( nullptr ); + } + OUString key = processLang(Language) + "/" + jar; + + osl::MutexGuard aGuard( m_aMutex ); + + ZipFileTable::iterator it = + m_aZipFileTable.emplace( key,Reference< XHierarchicalNameAccess >(nullptr) ).first; + + if( ! it->second.is() ) + { + OUString zipFile; + try + { + // Extension jar file? Search for ? + sal_Int32 nQuestionMark1 = jar.indexOf( '?' ); + sal_Int32 nQuestionMark2 = jar.lastIndexOf( '?' ); + if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 ) + { + OUString aExtensionPath = jar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 ); + OUString aPureJar = jar.copy( nQuestionMark2 + 1 ); + + zipFile = expandURL( aExtensionPath + "/" + aPureJar ); + } + else + { + zipFile = getInstallPathAsURL() + key; + } + + Sequence< Any > aArguments( 2 ); + + std::unique_ptr<XInputStream_impl> p(new XInputStream_impl( zipFile )); + if( p->CtorSuccess() ) + { + Reference< XInputStream > xInputStream( p.release() ); + aArguments[ 0 ] <<= xInputStream; + } + else + { + p.reset(); + aArguments[ 0 ] <<= zipFile; + } + + // let ZipPackage be used ( no manifest.xml is required ) + beans::NamedValue aArg; + aArg.Name = "StorageFormat"; + aArg.Value <<= OUString(ZIP_STORAGE_FORMAT_STRING); + aArguments[ 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; + } + + JarFileIterator aJarFileIt( m_xContext, *this, jar, Language ); + Reference< XHierarchicalNameAccess > xTestNA; + Reference< deployment::XPackage > xParentPackageBundle; + for (;;) + { + xTestNA = aJarFileIt.nextJarFile( 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, + const OUString& Id, + OStringBuffer& buffer ) +{ + DataBaseIterator aDbIt( m_xContext, *this, Module, Language, true ); + + // #i84550 Cache information about failed ids + OString id( Id.getStr(),Id.getLength(),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 ) +{ + osl::MutexGuard 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, const OUString& aInitialModule, const OUString& aLanguage ) + : m_xContext( xContext ) + , m_rDatabases( rDatabases ) + , m_eState( IteratorState::InitialModule ) + , m_aInitialModule( aInitialModule ) + , m_aLanguage( aLanguage ) +{ + assert( m_xContext.is() ); + init(); +} + +ExtensionIteratorBase::ExtensionIteratorBase( Databases& rDatabases, + const OUString& aInitialModule, const OUString& aLanguage ) + : m_xContext( comphelper::getProcessComponentContext() ) + , m_rDatabases( rDatabases ) + , m_eState( IteratorState::InitialModule ) + , m_aInitialModule( aInitialModule ) + , m_aLanguage( 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() ) + { + 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( + const OUString& rFileExtension, const Reference< deployment::XPackage >& xPackage ) +{ + // No extension -> search for pure language folder + bool bLangFolderOnly = rFileExtension.isEmpty(); + + OUString aFile; + OUString aLanguage = m_aLanguage; + for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass ) + { + OUString aStr = xPackage->getRegistrationDataURL().Value + "/" + aLanguage; + if( !bLangFolderOnly ) + { + aStr += "/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( bool& o_rbExtension ) +{ + OUString aRetFile; + + while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached ) + { + switch( m_eState ) + { + case IteratorState::InitialModule: + aRetFile = OUStringBuffer(m_rDatabases.getInstallPathAsURL()). + append(m_rDatabases.processLang(m_aLanguage)).append('/'). + append(m_aInitialModule).append(".key").makeStringAndClear(); + + 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( xHelpPackage ); + o_rbExtension = true; + break; + } + + case IteratorState::SharedExtensions: + { + Reference< deployment::XPackage > xParentPackageBundle; + Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + aRetFile = implGetDbFileFromPackage( xHelpPackage ); + o_rbExtension = true; + break; + } + + case IteratorState::BundledExtensions: + { + Reference< deployment::XPackage > xParentPackageBundle; + Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + aRetFile = implGetDbFileFromPackage( 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 + ( const Reference< deployment::XPackage >& xPackage ) +{ + OUString aExpandedURL = + implGetFileFromPackage( ".key", xPackage ); + + return aExpandedURL; +} + + +Reference< XHierarchicalNameAccess > JarFileIterator::nextJarFile + ( 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( 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( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); + break; + } + + case IteratorState::SharedExtensions: + { + Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( o_xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); + break; + } + + case IteratorState::BundledExtensions: + { + Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( o_xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); + break; + } + + case IteratorState::EndReached: + OSL_FAIL( "JarFileIterator::nextJarFile(): Invalid case IteratorState::EndReached" ); + break; + } + } + + return xNA; +} + +Reference< XHierarchicalNameAccess > JarFileIterator::implGetJarFromPackage +( const Reference< deployment::XPackage >& xPackage, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ) +{ + Reference< XHierarchicalNameAccess > xNA; + + OUString zipFile = + implGetFileFromPackage( ".jar", xPackage ); + + try + { + Sequence< Any > aArguments( 2 ); + aArguments[ 0 ] <<= zipFile; + + // let ZipPackage be used ( no manifest.xml is required ) + beans::NamedValue aArg; + aArg.Name = "StorageFormat"; + aArg.Value <<= OUString(ZIP_STORAGE_FORMAT_STRING); + aArguments[ 1 ] <<= aArg; + + 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.copy( 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( ".idxl", xPackage ); + + o_rbTemporary = false; + if( !m_xSFA->isFolder( aIndexFolder ) ) + { + // i98680: Missing index? Try to generate now + OUString aLangURL = implGetFileFromPackage( OUString(), 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( ".idxl", xPackage ); + else + aIndexFolder = aZipDir + "/help.idxl"; + } + catch (const Exception &) + { + } + } + } + + return aIndexFolder; +} + +void IndexFolderIterator::deleteTempIndexFolder( const OUString& aIndexFolder ) +{ + sal_Int32 nLastSlash = aIndexFolder.lastIndexOf( '/' ); + if( nLastSlash != -1 ) + { + OUString aTmpFolder = aIndexFolder.copy( 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 000000000..6edf6a0ce --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/databases.hxx @@ -0,0 +1,439 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_DATABASES_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_DATABASES_HXX + +#include <sal/config.h> + +#include <memory> +#include <unordered_map> +#include <unordered_set> +#include <vector> +#include <osl/mutex.hxx> +#include <rtl/ustring.hxx> +#include <rtl/string.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( const OUString& aTitle, + const OUString& aStartId, + const OUString& aProgramSwitch, + const OUString& aOrder ) + : m_aStartId( aStartId ), + m_aProgramSwitch( aProgramSwitch ), + m_aTitle( aTitle ), + m_nOrder( aOrder.toInt32() ) + { + } + + 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 const & key, + OUString const & 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,const OUString& 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(); + + const std::vector< OUString >& getModuleList( const OUString& Language ); + + StaticModuleInformation* getStaticInformationForModule( const OUString& Module, + const OUString& Language ); + + bool checkModuleMatchForExtension( const OUString& Database, const OUString& doclist ); + KeywordInfo* getKeyword( const OUString& Module, + const OUString& Language ); + + helpdatafileproxy::Hdf* getHelpDataFile( const OUString& 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( 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, + const OUString& Id, + OStringBuffer& buffer ); + + /** + * Has the purpose of forcing the jarfile to stay open + */ + + css::uno::Reference< css::container::XHierarchicalNameAccess > + jarFile( const OUString& 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 ); + + 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 ); + + static OUString expandURL( const OUString& aURL, + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + private: + + osl::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 newProdName,newProdVersion, + prodName,prodVersion,vendName,vendVersion,vendShort; + + 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, const OUString& aInitialModule, const OUString& aLanguage ); + ExtensionIteratorBase( Databases& rDatabases, const OUString& aInitialModule, + const 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( const OUString& 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( bool& o_rbExtension ); + + private: + OUString implGetDbFileFromPackage( + 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( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle, + OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ); + + private: + css::uno::Reference< css::container::XHierarchicalNameAccess > + implGetJarFromPackage(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( const OUString& aIndexFolder ); + + private: + OUString implGetIndexFolderFromPackage( bool& o_rbTemporary, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + + }; // end class KeyDataBaseFileIterator + +} // end namespace chelp + +#endif + +/* 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 000000000..f8a38ecd8 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/db.cxx @@ -0,0 +1,252 @@ +/* -*- 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 <cstring> + +#include <com/sun/star/io/XSeekable.hpp> + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; + +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, int& riPos, HDFData& rValue ) +{ + bool bSuccess = false; + + // Read key len + const char* pStartPtr = pData + riPos; + char* pEndPtr; + sal_Int32 nKeyLen = strtol( pStartPtr, &pEndPtr, 16 ); + 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() ) + { + 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()); + int iPos = 0; + while( iPos < nRead ) + { + HDFData aDBKey; + if( !implReadLenAndData( pData, iPos, aDBKey ) ) + break; + + OString aOKeyStr = aDBKey.getData(); + + // Read val len + const char* pStartPtr = pData + iPos; + char* pEndPtr; + sal_Int32 nValLen = strtol( pStartPtr, &pEndPtr, 16 ); + 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_pItData = reinterpret_cast<const char*>(m_aItData.getConstArray()); + m_iItPos = 0; + } + else + { + stopIteration(); + } + } + + return bSuccess; +} + +bool Hdf::getNextKeyAndValue( HDFData& rKey, HDFData& rValue ) +{ + bool bSuccess = false; + + if( m_iItPos < m_nItRead ) + { + if( implReadLenAndData( m_pItData, m_iItPos, rKey ) ) + { + if( implReadLenAndData( m_pItData, m_iItPos, rValue ) ) + bSuccess = true; + } + } + + return bSuccess; +} + +void Hdf::stopIteration() +{ + m_aItData = Sequence<sal_Int8>(); + m_pItData = nullptr; + 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 000000000..607b987ff --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/db.hxx @@ -0,0 +1,100 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_DB_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_DB_HXX + +#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; + const char* m_pItData; + int m_nItRead; + int m_iItPos; + + static bool implReadLenAndData( const char* pData, 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( const OUString& rFileURL, + css::uno::Reference< css::ucb::XSimpleFileAccess3 > const & xSFA ) + : m_aFileURL( rFileURL ) + , m_xSFA( xSFA ) + , m_pItData( nullptr ) + , m_nItRead( -1 ) + , m_iItPos( -1 ) + { + OSL_ASSERT(comphelper::isFileUrl(rFileURL)); + } + ~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; + }; + +} + +#endif + +/* 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 000000000..8eaebf364 --- /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() + throw() +{ + OWeakObject::acquire(); +} + + +void SAL_CALL +XInputStream_impl::release() + throw() +{ + 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 000000000..d3292ec19 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/inputstream.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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_INPUTSTREAM_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_INPUTSTREAM_HXX + +#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() + throw() override; + + virtual void SAL_CALL + release() + throw() 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 + +#endif + +/* 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 000000000..b82ba4917 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/provider.cxx @@ -0,0 +1,271 @@ +/* -*- 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 ) + : ::ucbhelper::ContentProviderImplHelper( rxContext ) + , isInitialized( false ) +{ +} + +// virtual +ContentProvider::~ContentProvider() +{ +} + +// XInterface methods. +void SAL_CALL ContentProvider::acquire() + throw() +{ + OWeakObject::acquire(); +} + +void SAL_CALL ContentProvider::release() + throw() +{ + OWeakObject::release(); +} + +css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = cppu::queryInterface( rType, + static_cast< lang::XTypeProvider* >(this), + static_cast< lang::XServiceInfo* >(this), + static_cast< ucb::XContentProvider* >(this), + static_cast< lang::XComponent* >(this), + static_cast< lang::XEventListener* >(this), + static_cast< container::XContainerListener* >(this) + ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + +// XTypeProvider methods. + +css::uno::Sequence< sal_Int8 > SAL_CALL ContentProvider::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL ContentProvider::getTypes() +{ + static cppu::OTypeCollection ourTypeCollection( + cppu::UnoType<lang::XTypeProvider>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<ucb::XContentProvider>::get(), + cppu::UnoType<lang::XComponent>::get(), + cppu::UnoType<container::XContainerListener>::get() + ); + + return ourTypeCollection.getTypes(); +} + + +// XServiceInfo methods. + +OUString SAL_CALL ContentProvider::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString ContentProvider::getImplementationName_Static() +{ + return "CHelpContentProvider"; +} + +sal_Bool SAL_CALL +ContentProvider::supportsService(const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL +ContentProvider::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +/// @throws uno::Exception +static uno::Reference< uno::XInterface > +ContentProvider_CreateInstance( + const uno::Reference< lang::XMultiServiceFactory> & rSMgr ) +{ + lang::XServiceInfo * pX = new ContentProvider( comphelper::getComponentContext(rSMgr) ); + return uno::Reference< uno::XInterface >::query( pX ); +} + +uno::Sequence< OUString > +ContentProvider::getSupportedServiceNames_Static() +{ + return { MYUCP_CONTENT_PROVIDER_SERVICE_NAME1, MYUCP_CONTENT_PROVIDER_SERVICE_NAME2 }; +} + +// Service factory implementation. + +css::uno::Reference< css::lang::XSingleServiceFactory > +ContentProvider::createServiceFactory( const css::uno::Reference< + css::lang::XMultiServiceFactory >& rxServiceMgr ) +{ + return cppu::createOneInstanceFactory( + rxServiceMgr, + ContentProvider::getImplementationName_Static(), + ContentProvider_CreateInstance, + ContentProvider::getSupportedServiceNames_Static() ); +} + +// 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 ).get(); + 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(m_xContext)); + 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(m_xContext)); + + // now adding as configuration change listener for the stylesheet + m_xContainer.set( + officecfg::Office::Common::Help::get(m_xContext), + css::uno::UNO_QUERY_THROW); + m_xContainer->addContainerListener( this ); + + OUString setupversion( + officecfg::Setup::Product::ooSetupVersion::get(m_xContext)); + OUString setupextension( + officecfg::Setup::Product::ooSetupExtension::get(m_xContext)); + OUString productversion( setupversion + " " + setupextension ); + + bool showBasic = officecfg::Office::Common::Help::ShowBasic::get( + m_xContext); + 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 ); +} + +/* 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 000000000..c12d0cb7b --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/provider.hxx @@ -0,0 +1,136 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_PROVIDER_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_PROVIDER_HXX + +#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 { + +// UNO service name for the provider. This name will be used by the UCB to +// create instances of the provider. + +#define MYUCP_CONTENT_PROVIDER_SERVICE_NAME1 "com.sun.star.help.XMLHelp" + +#define MYUCP_CONTENT_PROVIDER_SERVICE_NAME2 "com.sun.star.ucb.HelpContentProvider" + +// 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" +#define MYUCP_CONTENT_TYPE "application/vnd.sun.star.xmlhelp" // UCB Content Type. + + class Databases; + + class ContentProvider : + public ::ucbhelper::ContentProviderImplHelper, + public css::container::XContainerListener, + public css::lang::XComponent + { + public: + explicit ContentProvider( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual ~ContentProvider() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() + throw() override; + virtual void SAL_CALL release() + throw() override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + static OUString getImplementationName_Static(); + + static css::uno::Sequence< OUString > getSupportedServiceNames_Static(); + + static css::uno::Reference< css::lang::XSingleServiceFactory > createServiceFactory( + const css::uno::Reference< css::lang::XMultiServiceFactory >& rxServiceMgr ); + + // 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 ); + }; + +} + +#endif + +/* 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 000000000..23d7dcbf7 --- /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 000000000..2d276fa36 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultset.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSET_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSET_HXX + +#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; + }; + +} + +#endif + +/* 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 000000000..4a88add7e --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx @@ -0,0 +1,507 @@ +/* -*- 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 "resultsetbase.hxx" + +using namespace chelp; +using namespace com::sun::star; + +ResultSetBase::ResultSetBase( const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< ucb::XContentProvider >& xProvider, + const uno::Sequence< beans::Property >& seq ) + : m_xContext( rxContext ), + m_xProvider( xProvider ), + m_nRow( -1 ), + m_nWasNull( true ), + m_sProperty( seq ) +{ +} + +ResultSetBase::~ResultSetBase() +{ +} + + +// XInterface + +void SAL_CALL +ResultSetBase::acquire() + throw() +{ + OWeakObject::acquire(); +} + + +void SAL_CALL +ResultSetBase::release() + throw() +{ + 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 ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + if ( ! m_pDisposeEventListeners ) + m_pDisposeEventListeners.reset( + new comphelper::OInterfaceContainerHelper2( m_aMutex )); + + m_pDisposeEventListeners->addInterface( Listener ); +} + + +void SAL_CALL +ResultSetBase::removeEventListener( + const uno::Reference< lang::XEventListener >& Listener ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pDisposeEventListeners ) + m_pDisposeEventListeners->removeInterface( Listener ); +} + + +void SAL_CALL +ResultSetBase::dispose() +{ + osl::MutexGuard aGuard( m_aMutex ); + + lang::EventObject aEvt; + aEvt.Source = static_cast< lang::XComponent * >( this ); + + if ( m_pDisposeEventListeners && m_pDisposeEventListeners->getLength() ) + { + m_pDisposeEventListeners->disposeAndClear( aEvt ); + } + if( m_pRowCountListeners && m_pRowCountListeners->getLength() ) + { + m_pRowCountListeners->disposeAndClear( aEvt ); + } + if( m_pIsFinalListeners && m_pIsFinalListeners->getLength() ) + { + m_pIsFinalListeners->disposeAndClear( 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() + throw() override + { + OWeakObject::acquire(); + } + + + void SAL_CALL release() + throw() 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(m_aSeq.begin(), m_aSeq.end(), + [&aName](const beans::Property& rProp) { return aName == rProp.Name; }); + if (pProp != m_aSeq.end()) + return *pProp; + throw beans::UnknownPropertyException(aName); + } + + sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override + { + return std::any_of(m_aSeq.begin(), m_aSeq.end(), + [&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(2); + seq[0].Name = "RowCount"; + seq[0].Handle = -1; + seq[0].Type = cppu::UnoType<sal_Int32>::get(); + seq[0].Attributes = beans::PropertyAttribute::READONLY; + + seq[1].Name = "IsRowCountFinal"; + seq[1].Handle = -1; + seq[1].Type = cppu::UnoType<sal_Bool>::get(); + seq[1].Attributes = 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" ) + { + osl::MutexGuard aGuard( m_aMutex ); + if ( ! m_pIsFinalListeners ) + m_pIsFinalListeners.reset( + new comphelper::OInterfaceContainerHelper2( m_aMutex )); + + m_pIsFinalListeners->addInterface( xListener ); + } + else if ( aPropertyName == "RowCount" ) + { + osl::MutexGuard aGuard( m_aMutex ); + if ( ! m_pRowCountListeners ) + m_pRowCountListeners.reset( + new comphelper::OInterfaceContainerHelper2( m_aMutex )); + m_pRowCountListeners->addInterface( xListener ); + } + else + throw beans::UnknownPropertyException(aPropertyName); +} + + +void SAL_CALL ResultSetBase::removePropertyChangeListener( + const OUString& aPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& aListener ) +{ + if( aPropertyName == "IsRowCountFinal" && + m_pIsFinalListeners ) + { + osl::MutexGuard aGuard( m_aMutex ); + m_pIsFinalListeners->removeInterface( aListener ); + } + else if ( aPropertyName == "RowCount" && + m_pRowCountListeners ) + { + osl::MutexGuard aGuard( m_aMutex ); + m_pRowCountListeners->removeInterface( 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() +{ + ::ucbhelper::ResultSetMetaData* p = + new ::ucbhelper::ResultSetMetaData( m_xContext, m_sProperty ); + return uno::Reference< sdbc::XResultSetMetaData >( p ); +} + +/* 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 000000000..53a79d035 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx @@ -0,0 +1,404 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETBASE_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETBASE_HXX + +#include <vector> +#include <memory> +#include <cppuhelper/weak.hxx> +#include <comphelper/interfacecontainer2.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( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const 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() + throw() override; + + virtual void SAL_CALL + release() + throw() 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; + + osl::Mutex m_aMutex; + std::unique_ptr<comphelper::OInterfaceContainerHelper2> m_pDisposeEventListeners; + + std::unique_ptr<comphelper::OInterfaceContainerHelper2> m_pRowCountListeners; + std::unique_ptr<comphelper::OInterfaceContainerHelper2> m_pIsFinalListeners; + }; + + +} // end namespace fileaccess + + +#endif + +/* 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 000000000..2dba0ec05 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFACTORY_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFACTORY_HXX + +#include "resultsetbase.hxx" + +namespace chelp { + + class ResultSetBase; + + class ResultSetFactory + { + public: + + virtual ~ResultSetFactory() { }; + + virtual ResultSetBase* createResultSet() = 0; + }; + + +} + + +#endif + +/* 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 000000000..cb0d765c7 --- /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 <tools/diagnose_ex.h> + +#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 "resultsetforquery.hxx" +#include "databases.hxx" + +using namespace std; +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(const OUString& aURL, float fScore) + : m_aURL(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 ); + + vector< vector< OUString > > queryList; + { + sal_Int32 idx; + OUString query = aURLParameter.get_query(); + while( !query.isEmpty() ) + { + idx = query.indexOf( ' ' ); + if( idx == -1 ) + idx = query.getLength(); + + 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 ); + } + } + + 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; + vector< vector<HitItem> > aIndexFolderResultVectorVector; + + bool bTemporary; + for (;;) + { + idxDir = aIndexFolderIt.nextIndexFolder( bExtension, bTemporary ); + if( idxDir.isEmpty() ) + break; + vector<HitItem> aIndexFolderResultVector; + + try + { + vector< vector<HitItem> > aQueryListResultVectorVector; + set< OUString > aSet,aCurrent,aResultSet; + + int nQueryListSize = queryList.size(); + if( nQueryListSize > 1 ) + hitCount = 2000; + + for( int i = 0; i < nQueryListSize; ++i ) + { + 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]; + + vector<float> aScoreVector; + 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 ) + { + 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 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 ) + { + 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; + + 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 = OUString( "#HLP#" ).getLength(); + 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.copy(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 000000000..bc1318719 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFORQUERY_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFORQUERY_HXX + +#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 ); + }; + +} + + +#endif + +/* 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 000000000..b2b7e29a2 --- /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 000000000..17add2d3c --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFORROOT_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_RESULTSETFORROOT_HXX + +#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 ); + }; + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/services.cxx b/xmlhelp/source/cxxhelp/provider/services.cxx new file mode 100644 index 000000000..f1541d5c4 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/services.cxx @@ -0,0 +1,58 @@ +/* -*- 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/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include "provider.hxx" +#include <tvfactory.hxx> + +using namespace com::sun::star; + +extern "C" SAL_DLLPUBLIC_EXPORT void * ucpchelp_component_getFactory( + const char * pImplName, + void * pServiceManager, + SAL_UNUSED_PARAMETER void * /*pRegistryKey*/ ) +{ + void * pRet = nullptr; + + uno::Reference< lang::XMultiServiceFactory > xSMgr( + static_cast< lang::XMultiServiceFactory * >( pServiceManager ) ); + uno::Reference< lang::XSingleServiceFactory > xFactory; + + if ( ::chelp::ContentProvider::getImplementationName_Static(). + equalsAscii( pImplName ) ) + { + xFactory = ::chelp::ContentProvider::createServiceFactory( xSMgr ); + } + else if ( treeview::TVFactory::getImplementationName_static().equalsAscii( pImplName ) ) + { + xFactory = treeview::TVFactory::createServiceFactory( xSMgr ); + } + + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* 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 000000000..2ec811373 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/urlparameter.cxx @@ -0,0 +1,982 @@ +/* -*- 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/character.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 <memory> + +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 OString( val.getStr(),val.getLength(),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( m_aId.getStr(),m_aId.getLength(),RTL_TEXTENCODING_UTF8 ); + bSuccess = pHdf->getValueForKey( keyStr, aHDFData ); + if( bSuccess ) + { + pData = aHDFData.getData(); + break; + } + } + + if( bSuccess ) + { + 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() throw() override; + virtual void SAL_CALL release() throw() 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: + + osl::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 + std::unique_ptr<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.reset(); + 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(); + OUString aLastStr = + m_aExpr.copy(sal::static_int_cast<sal_uInt32>(nLen) - 6); + if( aLastStr == "DbPAR=" ) + { + m_aExpr = m_aExpr.copy( 0, 20 ) + + "shared" + + m_aExpr.copy( 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_ = m_aExpr.copy( 1 ).trim(); + else + return false; + + + bool ret = true; + sal_Int32 delimIdx,equalIdx; + OUString parameter,value; + + while( !query_.isEmpty() ) + { + delimIdx = query_.indexOf( '&' ); + equalIdx = query_.indexOf( '=' ); + parameter = query_.copy( 0,equalIdx ).trim(); + if( delimIdx == -1 ) + { + value = query_.copy( equalIdx + 1 ).trim(); + query_.clear(); + } + else + { + value = query_.copy( equalIdx+1,delimIdx - equalIdx - 1 ).trim(); + query_ = query_.copy( delimIdx+1 ).trim(); + } + + 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"; + OString aPureProgramm( urlParam->getByName( "Program" ) ); + parString[last++] = "'" + aPureProgramm + "'"; + parString[last++] = "Database"; + parString[last++] = "'" + urlParam->getByName( "DatabasePar" ) + "'"; + parString[last++] = "Id"; + parString[last++] = "'" + urlParam->getByName( "Id" ) + "'"; + parString[last++] = "Path"; + OString aPath( urlParam->getByName( "Path" ) ); + parString[last++] = "'" + aPath + "'"; + + OString aPureLanguage = urlParam->getByName( "Language" ); + parString[last++] = "Language"; + parString[last++] = "'" + aPureLanguage + "'"; + parString[last++] = "System"; + parString[last++] = "'" + urlParam->getByName( "System" ) + "'"; + parString[last++] = "productname"; + parString[last++] = "'" + OString( + pDatabases->getProductName().getStr(), + pDatabases->getProductName().getLength(), + RTL_TEXTENCODING_UTF8 ) + "'"; + parString[last++] = "productversion"; + parString[last++] = "'" + + OString( pDatabases->getProductVersion().getStr(), + pDatabases->getProductVersion().getLength(), + RTL_TEXTENCODING_UTF8 ) + "'"; + + parString[last++] = "imgtheme"; + parString[last++] = "'" + pDatabases->getImageTheme() + "'"; + parString[last++] = "hp"; + parString[last++] = "'" + urlParam->getByName( "HelpPrefix" ) + "'"; + + if( !parString[last-1].isEmpty() ) + { + parString[last++] = "sm"; + parString[last++] = "'vnd.sun.star.help%3A%2F%2F'"; + parString[last++] = "qm"; + parString[last++] = "'%3F'"; + parString[last++] = "es"; + parString[last++] = "'%3D'"; + parString[last++] = "am"; + parString[last++] = "'%26'"; + parString[last++] = "cl"; + parString[last++] = "'%3A'"; + parString[last++] = "sl"; + parString[last++] = "'%2F'"; + parString[last++] = "hm"; + parString[last++] = "'%23'"; + parString[last++] = "cs"; + parString[last++] = "'css'"; + + parString[last++] = "vendorname"; + parString[last++] = OString("''"); + parString[last++] = "vendorversion"; + parString[last++] = OString("''"); + parString[last++] = "vendorshort"; + parString[last++] = OString("''"); + } + + // 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"; + parString[last++] = "'" + aExpandedExtensionPath + "'"; + + // ExtensionId + OString aPureExtensionId; + sal_Int32 iSlash = aPath.indexOf( '/' ); + if( iSlash != -1 ) + aPureExtensionId = aPath.copy( 0, iSlash ); + + parString[last++] = "ExtensionId"; + parString[last++] = "'" + aPureExtensionId + "'"; + } + + for( int i = 0; i < last; ++i ) + parameter[i] = parString[i].getStr(); + parameter[last] = nullptr; + + OUString xslURL = pDatabases->getInstallPathAsURL(); + + OString xslURLascii = OString( + xslURL.getStr(), + xslURL.getLength(), + 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() throw() +{ + OWeakObject::acquire(); +} + + +void SAL_CALL InputStreamTransformer::release() throw() +{ + OWeakObject::release(); +} + + +sal_Int32 SAL_CALL InputStreamTransformer::readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead ) +{ + osl::MutexGuard 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 ); + + for( int k = 0; k < curr; ++k ) + aData[k] = buffer[pos++]; + + 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 ) +{ + osl::MutexGuard aGuard( m_aMutex ); + while( nBytesToSkip-- ) ++pos; +} + + +sal_Int32 SAL_CALL InputStreamTransformer::available() +{ + osl::MutexGuard 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 ) +{ + osl::MutexGuard 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() +{ + osl::MutexGuard aGuard( m_aMutex ); + return sal_Int64( pos ); +} + + +sal_Int64 SAL_CALL InputStreamTransformer::getLength() +{ + osl::MutexGuard aGuard( m_aMutex ); + + return buffer.getLength(); +} + + +void InputStreamTransformer::addToBuffer( const char* buffer_,int len_ ) +{ + osl::MutexGuard 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 000000000..d81aafcbc --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/urlparameter.hxx @@ -0,0 +1,226 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_URLPARAMETER_HXX +#define INCLUDED_XMLHELP_SOURCE_CXXHELP_PROVIDER_URLPARAMETER_HXX + +#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 OUString(pTitle, rtl_str_getLength(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 + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |