summaryrefslogtreecommitdiffstats
path: root/xmlhelp/source/cxxhelp/provider/databases.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'xmlhelp/source/cxxhelp/provider/databases.cxx')
-rw-r--r--xmlhelp/source/cxxhelp/provider/databases.cxx1804
1 files changed, 1804 insertions, 0 deletions
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: */