text: /**************************************************************
   * 
   * 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
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   * 
   *************************************************************/
  
  
  
  // MARKER(update_precomp.py): autogen include statement, do not remove
  #include "precompiled_xmlhelp.hxx"
  #include "db.hxx"
  #ifndef _VOS_DIAGNOSE_HXX_
  #include <vos/diagnose.hxx>
  #endif
  #include <osl/thread.h>
  #include <rtl/uri.hxx>
  #include <osl/file.hxx>
  #include <rtl/memory.h>
  #include <com/sun/star/lang/Locale.hpp>
  #include <rtl/ustrbuf.hxx>
  #include "inputstream.hxx"
  #include <algorithm>
  #include <string.h>
  
  // Extensible help
  #include "com/sun/star/deployment/ExtensionManager.hpp"
  #include "com/sun/star/deployment/thePackageManagerFactory.hpp"
  #include <comphelper/processfactory.hxx>
  #include <com/sun/star/beans/XPropertySet.hpp>
  #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/PropertyValue.hpp> 
  #include <com/sun/star/beans/NamedValue.hpp> 
  #include <com/sun/star/frame/XConfigManager.hpp> 
  #include <com/sun/star/util/XMacroExpander.hpp>
  #include <com/sun/star/uri/XUriReferenceFactory.hpp>
  #include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
  #include <com/sun/star/script/XInvocation.hpp>
  #include <comphelper/locale.hxx>
  
  #include <com/sun/star/awt/XToolkit.hpp>
  #include <com/sun/star/awt/XExtendedToolkit.hpp>
  #include <com/sun/star/awt/XWindowPeer.hpp>
  #include <com/sun/star/awt/XVclWindowPeer.hpp>
  #include <com/sun/star/awt/XTopWindow.hpp>
  
  #include <l10ntools/compilehelp.hxx>
  #include <comphelper/storagehelper.hxx>
  
  #include "databases.hxx"
  #include "urlparameter.hxx"
  
  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;
  
  
  static rtl::OUString aSlash( rtl::OUString::createFromAscii( "/" ) );
  static rtl::OUString aHelpFilesBaseName( rtl::OUString::createFromAscii( "help" ) );
  static rtl::OUString aHelpMediaType( rtl::OUString::createFromAscii( "application/vnd.sun.star.help" ) );
  
  rtl::OUString Databases::expandURL( const rtl::OUString& aURL )
  {
  	osl::MutexGuard aGuard( m_aMutex );
  	rtl::OUString aRetURL = expandURL( aURL, m_xContext );
  	return aRetURL;
  }
  
  rtl::OUString Databases::expandURL( const rtl::OUString& aURL, Reference< uno::XComponentContext > xContext )
  {
  	static Reference< util::XMacroExpander > xMacroExpander;
  	static Reference< uri::XUriReferenceFactory > xFac;
  
  	if( !xContext.is() )
  		return rtl::OUString();
  
  	if( !xMacroExpander.is() || !xFac.is() )
  	{
  		Reference< XMultiComponentFactory > xSMgr( xContext->getServiceManager(), UNO_QUERY );
  
  		xFac = Reference< uri::XUriReferenceFactory >(
  			xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii(
  			"com.sun.star.uri.UriReferenceFactory"), xContext ) , UNO_QUERY );
  		if( !xFac.is() )
  		{
  			throw RuntimeException(
  				::rtl::OUString::createFromAscii( "Databases::expand(), could not instatiate UriReferenceFactory." ),
  				Reference< XInterface >() );
  		}
  
  		xMacroExpander = Reference< util::XMacroExpander >(
  			xContext->getValueByName(
  			::rtl::OUString::createFromAscii( "/singletons/com.sun.star.util.theMacroExpander" ) ),
  			UNO_QUERY_THROW );
   	}
  
  	rtl::OUString aRetURL = aURL;
  	if( xMacroExpander.is() )
  	{
  		Reference< uri::XUriReference > uriRef;
  		for (;;)
  		{
  			uriRef = Reference< uri::XUriReference >( xFac->parse( aRetURL ), UNO_QUERY );
  			if ( uriRef.is() )
  			{
  				Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
  				if( !sxUri.is() )
  					break;
  
  				aRetURL = sxUri->expand( xMacroExpander );
  			}
  		}
   	}
  	return aRetURL;
  }
  
  Databases::Databases( sal_Bool showBasic,
  					  const rtl::OUString& instPath,
  					  const com::sun::star::uno::Sequence< rtl::OUString >& imagesZipPaths,
  					  const rtl::OUString& productName,
  					  const rtl::OUString& productVersion,
  					  const rtl::OUString& styleSheet,
  					  Reference< uno::XComponentContext > xContext )
  	: m_xContext( xContext ),
  	  m_bShowBasic(showBasic),
  	  m_nErrorDocLength( 0 ),
  	  m_pErrorDoc( 0 ),
  	  m_nCustomCSSDocLength( 0 ),
  	  m_pCustomCSSDoc( 0 ),
  	  m_aCSS(styleSheet.toAsciiLowerCase()),
  	  newProdName(rtl::OUString::createFromAscii( "$[officename]" ) ),
        newProdVersion(rtl::OUString::createFromAscii( "$[officeversion]" ) ),
        prodName( rtl::OUString::createFromAscii( "%PRODUCTNAME" ) ),
  	  prodVersion( rtl::OUString::createFromAscii( "%PRODUCTVERSION" ) ),
  	  vendName( rtl::OUString::createFromAscii( "%VENDORNAME" ) ),
  	  vendVersion( rtl::OUString::createFromAscii( "%VENDORVERSION" ) ),
  	  vendShort( rtl::OUString::createFromAscii( "%VENDORSHORT" ) ),
  	  m_aImagesZipPaths( imagesZipPaths ),
  	  m_nSymbolsStyle( 0 )
  {
  	m_xSMgr = Reference< XMultiComponentFactory >( m_xContext->getServiceManager(), UNO_QUERY );
  
  	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 = Reference< ucb::XSimpleFileAccess >( 
  		m_xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ),
  		m_xContext ), UNO_QUERY_THROW );
  }
  
  Databases::~Databases()
  {
  	// release stylesheet
  	
  	delete[] m_pCustomCSSDoc;
  	
  	// release errorDocument
  	
  	delete[] m_pErrorDoc;
      
  	// unload the databases
  	
  	{
  		// DatabasesTable 
  		DatabasesTable::iterator it = m_aDatabases.begin();
  		while( it != m_aDatabases.end() )
  		{
              delete it->second;
              ++it;
  		}
  	}
  	
  	{
  		//  ModInfoTable
  	
  		ModInfoTable::iterator it = m_aModInfo.begin();
  		while( it != m_aModInfo.end() )
  		{
  			delete it->second;
  			++it;
  		}
  	}
  	
  	{
  		// KeywordInfoTable
          
  		KeywordInfoTable::iterator it = m_aKeywordInfo.begin();
  		while( it != m_aKeywordInfo.end() )
  		{
  			delete it->second;
  			++it;
  		}
  	}
  }
  
  static bool impl_getZipFile(
          Sequence< rtl::OUString > & rImagesZipPaths,
          const rtl::OUString & rZipName,
          rtl::OUString & rFileName )
  {
      const rtl::OUString *pPathArray = rImagesZipPaths.getArray();
      for ( int i = 0; i < rImagesZipPaths.getLength(); ++i )
      {
          rFileName = pPathArray[ i ];
          if ( rFileName.getLength() )
          {
              if ( 1 + rFileName.lastIndexOf( '/' ) != rFileName.getLength() )
              {
                  rFileName += rtl::OUString::createFromAscii( "/" );
              }
              rFileName += rZipName;
  
              // test existence
              osl::DirectoryItem aDirItem;
              if ( osl::DirectoryItem::get( rFileName, aDirItem ) == osl::FileBase::E_None )
                  return true;
          }
      }
      return false;
  }
  
  rtl::OString Databases::getImagesZipFileURL()
  {
      //sal_Int16 nSymbolsStyle = SvtMiscOptions().GetCurrentSymbolsStyle();
      sal_Int16 nSymbolsStyle = 0;
      try
      {
          uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
              m_xSMgr ->createInstanceWithContext(::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider"), m_xContext), uno::UNO_QUERY_THROW);
  
          // set root path
          uno::Sequence < uno::Any > lParams(1);
          beans::PropertyValue                       aParam ;
          aParam.Name    = ::rtl::OUString::createFromAscii("nodepath");
          aParam.Value <<= ::rtl::OUString::createFromAscii("org.openoffice.Office.Common");
          lParams[0] = uno::makeAny(aParam);
  
          // open it
          uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
                      ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationAccess"),
                      lParams) );
  
          bool bChanged = false;
  		uno::Reference< container::XHierarchicalNameAccess > xAccess(xCFG, uno::UNO_QUERY_THROW);
          uno::Any aResult = xAccess->getByHierarchicalName(::rtl::OUString::createFromAscii("Misc/SymbolSet"));
          if ( (aResult >>= nSymbolsStyle) && m_nSymbolsStyle != nSymbolsStyle )
  		{
  			m_nSymbolsStyle = nSymbolsStyle;
  			bChanged = true;
  		}
  
          if ( !m_aImagesZipFileURL.getLength() || bChanged )
          {
              rtl::OUString aImageZip, aSymbolsStyleName;
  			aResult = xAccess->getByHierarchicalName(::rtl::OUString::createFromAscii("Misc/SymbolStyle"));
  			aResult >>= aSymbolsStyleName;
  
              bool bFound = false;
              if ( aSymbolsStyleName.getLength() != 0 )
              {
                  rtl::OUString aZipName = rtl::OUString::createFromAscii( "images_" );
                  aZipName += aSymbolsStyleName;
                  aZipName += rtl::OUString::createFromAscii( ".zip" );
  
                  bFound = impl_getZipFile( m_aImagesZipPaths, aZipName, aImageZip );
              }
  
              if ( ! bFound )
                  bFound = impl_getZipFile( m_aImagesZipPaths, rtl::OUString::createFromAscii( "images.zip" ), aImageZip );
  
              if ( ! bFound )
                  aImageZip = rtl::OUString();
  
              m_aImagesZipFileURL = rtl::OUStringToOString(
                          rtl::Uri::encode(
                              aImageZip,
                              rtl_UriCharClassPchar,
                              rtl_UriEncodeIgnoreEscapes,
                              RTL_TEXTENCODING_UTF8 ), RTL_TEXTENCODING_UTF8 );
          }
      }
      catch ( NoSuchElementException const & )
      {
      }
  
      return m_aImagesZipFileURL;
  }
  
  void Databases::replaceName( rtl::OUString& oustring ) const
  {
  	sal_Int32 idx = -1,idx1 = -1,idx2 = -1,k = 0,off;
  	bool cap = false;
  	rtl::OUStringBuffer aStrBuf( 0 );
  	
  	while( true )
  	{
          ++idx;
          idx1 = oustring.indexOf( sal_Unicode('%'),idx);
          idx2 = oustring.indexOf( sal_Unicode('$'),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();
  	}
  }
  
  
  	
  
  rtl::OUString Databases::getInstallPathAsSystemPath()
  {
  	osl::MutexGuard aGuard( m_aMutex );
  	
  	if( ! m_aInstallDirectoryAsSystemPath.getLength() )
  	{
  #ifdef DBG_UTIL
  		bool bla =
  			osl::FileBase::E_None ==
  			osl::FileBase::getSystemPathFromFileURL( m_aInstallDirectory,m_aInstallDirectoryAsSystemPath );
          VOS_ENSURE( bla,"HelpProvider, no installpath" );
  #else                    
          osl::FileBase::getSystemPathFromFileURL( m_aInstallDirectory,m_aInstallDirectoryAsSystemPath );		
  #endif
  	}
  	
  	return m_aInstallDirectoryAsSystemPath;
  }
  
  rtl::OUString Databases::getInstallPathAsURL()
  {
  	osl::MutexGuard aGuard( m_aMutex );
  	
    	return m_aInstallDirectory;
  }
  
  
  const std::vector< rtl::OUString >& Databases::getModuleList( const rtl::OUString& Language )
  {
  	if( m_avModules.size() == 0 )
  	{
  		rtl::OUString  fileName,dirName = getInstallPathAsURL() + processLang( Language );
  		osl::Directory dirFile( dirName );
  
  		osl::DirectoryItem aDirItem;			
  		osl::FileStatus    aStatus( FileStatusMask_FileName );
  				
  		sal_Int32 idx;
  
  		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( FileStatusMask_FileName ) )
  				continue;
  					
  			fileName = aStatus.getFileName();
  					
  			// Check, whether fileName is of the form *.cfg
  			idx = fileName.lastIndexOf(  sal_Unicode( '.' ) );
  					
  			if( idx == -1 )
  				continue;
  					
  			const sal_Unicode* str = fileName.getStr();
  					
  			if( fileName.getLength() == idx + 4                   && 
  				( str[idx + 1] == 'c' || str[idx + 1] == 'C' )    && 
  				( str[idx + 2] == 'f' || str[idx + 2] == 'F' )    &&
  				( str[idx + 3] == 'g' || str[idx + 3] == 'G' )    &&
  				( fileName = fileName.copy(0,idx).toAsciiLowerCase() ).compareToAscii( "picture" ) != 0 ) {
  			  if(! m_bShowBasic && fileName.compareToAscii("sbasic") == 0 )
  				continue;
  			  m_avModules.push_back( fileName );
  			}
  		}
  	}
  	return m_avModules;
  }		
  
  
  
  StaticModuleInformation* Databases::getStaticInformationForModule( const rtl::OUString& Module,
  																   const rtl::OUString& Language )
  {
  	osl::MutexGuard aGuard( m_aMutex );
  
  	rtl::OUString key = processLang(Language) + rtl::OUString::createFromAscii( "/" ) + Module;
  
  	std::pair< ModInfoTable::iterator,bool > aPair = 
  		m_aModInfo.insert( ModInfoTable::value_type( key,0 ) );		
  
  	ModInfoTable::iterator it = aPair.first;
  	
  	if( aPair.second && ! it->second )
  	{
  		osl::File cfgFile( getInstallPathAsURL() +
  						   key +
  						   rtl::OUString::createFromAscii( ".cfg" ) );
  		
  		if( osl::FileBase::E_None != cfgFile.open( OpenFlag_Read ) )
  			it->second = 0;
  		else
  		{
  			sal_uInt32 pos = 0;
  			sal_uInt64 nRead;
  			sal_Char buffer[2048];
  			sal_Unicode lineBuffer[1028];
  			rtl::OUString fileContent;
  			
  			while( osl::FileBase::E_None == cfgFile.read( &buffer,2048,nRead ) && nRead )
  				fileContent += rtl::OUString( buffer,sal_Int32( nRead ),RTL_TEXTENCODING_UTF8 );
  			
  			cfgFile.close();
  			
  			const sal_Unicode* str = fileContent.getStr();
  			rtl::OUString current,lang_,program,startid,title,heading,fulltext;
              rtl::OUString order = rtl::OUString::createFromAscii( "1" );
  			
  			for( sal_Int32 i = 0;i < fileContent.getLength();i++ )
  			{
  				sal_Unicode ch = str[ i ];
  				if( ch == sal_Unicode( '\n' ) || ch == sal_Unicode( '\r' ) )
  				{
  					if( pos )
  					{
  						current = rtl::OUString( lineBuffer,pos );
  						
  						if( current.compareToAscii( "Title",5 ) == 0 )
  						{
  							title = current.copy( current.indexOf(sal_Unicode( '=' ) ) + 1 );
  						}
  						else if( current.compareToAscii( "Start",5 ) == 0 )
  						{
  							startid = current.copy( current.indexOf('=') + 1 );
  						}
  						else if( current.compareToAscii( "Language",8 ) == 0 )
  						{
  							lang_ = current.copy( current.indexOf('=') + 1 );
  						}
  						else if( current.compareToAscii( "Program",7 ) == 0 )
  						{
  							program = current.copy( current.indexOf('=') + 1 );
  						}
  						else if( current.compareToAscii( "Heading",7 ) == 0 )
  						{
  							heading = current.copy( current.indexOf('=') + 1 );
  						}
  						else if( current.compareToAscii( "FullText",8 ) == 0 )
  						{
  							fulltext = current.copy( current.indexOf('=') + 1 );
  						}
  						else if( current.compareToAscii( "Order",5 ) == 0 )
  						{
  							order = current.copy( current.indexOf('=') + 1 );
  						}
  					}
  					pos = 0;
  				}
  				else
  					lineBuffer[ pos++ ] = ch;
  			}
  			replaceName( title );
  			it->second = new StaticModuleInformation( title,
  													  startid,
  													  program,
  													  heading,
  													  fulltext,
  													  order );			
  		}
  	}
  	
  	return it->second;
  }
  
  
  
  
  rtl::OUString Databases::processLang( const rtl::OUString& Language )
  {
  	osl::MutexGuard aGuard( m_aMutex );
  
  	rtl::OUString ret;
  	LangSetTable::iterator it = m_aLangSet.find( Language );
  	
  	if( it == m_aLangSet.end() )
  	{
  		sal_Int32 idx;
  		osl::DirectoryItem aDirItem;
  		
  		if( osl::FileBase::E_None == osl::DirectoryItem::get( getInstallPathAsURL() + Language,aDirItem ) )
  		{
  			ret = Language;
  			m_aLangSet[ Language ] = ret;
  		}
  		else if( ( ( idx = Language.indexOf( '-' ) ) != -1 ||
  				   ( idx = Language.indexOf( '_' ) ) != -1 ) &&
  					osl::FileBase::E_None == osl::DirectoryItem::get( getInstallPathAsURL() + Language.copy( 0,idx ),
  																   aDirItem ) )
  		{
  			ret = Language.copy( 0,idx );
  			m_aLangSet[ Language ] = ret;
  		}
  	}
  	else
  		ret = it->second;
  
  	return ret;
  }		
  
  
  rtl::OUString Databases::country( const rtl::OUString& Language )
  {
  	sal_Int32 idx;
  	if( ( idx = Language.indexOf( '-' ) ) != -1 ||
  		( idx = Language.indexOf( '_' ) ) != -1 )
  		return Language.copy( 1+idx );
  	
  	return rtl::OUString();
  }
  
  
  
  helpdatafileproxy::Hdf* Databases::getHelpDataFile( const rtl::OUString& Database,
  							const rtl::OUString& Language, bool helpText,
  							const rtl::OUString* pExtensionPath )
  {
  	if( ! Database.getLength() || ! Language.getLength() )
  		return 0;
  	
  	osl::MutexGuard aGuard( m_aMutex );
  
  
  	rtl::OUString aFileExt( rtl::OUString::createFromAscii( helpText ? ".ht" : ".db" ) );
  	rtl::OUString dbFileName = aSlash + Database + aFileExt;
  	rtl::OUString key;
  	if( pExtensionPath == NULL )
  		key = processLang( Language ) + dbFileName;
  	else
  		key = *pExtensionPath + Language + dbFileName;		// make unique, don't change language
  
  	std::pair< DatabasesTable::iterator,bool > aPair = 
  		m_aDatabases.insert( DatabasesTable::value_type( key,0 ) );
  	
  	DatabasesTable::iterator it = aPair.first;	
  	
  	if( aPair.second && ! it->second )
  	{
  		helpdatafileproxy::Hdf* pHdf = 0;
  		
  		rtl::OUString fileURL;
  		if( pExtensionPath )
              fileURL = expandURL(*pExtensionPath) + Language + dbFileName;
  		else
              fileURL = getInstallPathAsURL() + key;
  
  		rtl::OUString fileNameHDFHelp( fileURL );
          //Extensions always use the new format
  		if( pExtensionPath != NULL )
  			fileNameHDFHelp += rtl::OUString::createFromAscii( "_" );
          //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 = new helpdatafileproxy::Hdf( fileNameHDFHelp, m_xSFA );
  		}
  		
  		it->second = pHdf;
  	}
  
  	return it->second;
  }
  
  Reference< XCollator >
  Databases::getCollator( const rtl::OUString& Language,
  						const rtl::OUString& System )
  {
  	(void)System;
  
  	rtl::OUString key = Language;
  	
  	osl::MutexGuard aGuard( m_aMutex );
  	
  	CollatorTable::iterator it =
  		m_aCollatorTable.insert( CollatorTable::value_type( key,0 ) ).first;
  	
  	if( ! it->second.is() )
  	{
  		it->second = 
  			Reference< XCollator > ( 
  				m_xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.i18n.Collator" ),
  				m_xContext ), UNO_QUERY );
  		rtl::OUString langStr = processLang(Language);
  		rtl::OUString countryStr = country(Language);
  		if( !countryStr.getLength() )
  		{
  			if( langStr.compareToAscii("de") == 0 )
  				countryStr = rtl::OUString::createFromAscii("DE");
  			else if( langStr.compareToAscii("en") == 0 )
  				countryStr = rtl::OUString::createFromAscii("US");
  			else if( langStr.compareToAscii("es") == 0 )
  				countryStr = rtl::OUString::createFromAscii("ES");
  			else if( langStr.compareToAscii("it") == 0 )
  				countryStr = rtl::OUString::createFromAscii("IT");
  			else if( langStr.compareToAscii("fr") == 0 )
  				countryStr = rtl::OUString::createFromAscii("FR");
  			else if( langStr.compareToAscii("sv") == 0 )
  				countryStr = rtl::OUString::createFromAscii("SE");
  			else if( langStr.compareToAscii("ja") == 0 )
  				countryStr = rtl::OUString::createFromAscii("JP");
  			else if( langStr.compareToAscii("ko") == 0 )
  				countryStr = rtl::OUString::createFromAscii("KR");
  		}
  		it->second->loadDefaultCollator(  Locale( langStr,
  												  countryStr,
  												  rtl::OUString() ),
  										  0 );
  	}
  	
  	return it->second;
  }
  
  
  
  namespace chelp {
      
      struct KeywordElementComparator
      {
          KeywordElementComparator( const Reference< XCollator >& xCollator )
              : m_xCollator( xCollator )
          { }
          
          bool operator()( const KeywordInfo::KeywordElement& la,
                           const KeywordInfo::KeywordElement& ra ) const
          {
              const rtl::OUString& l = la.key;
              const rtl::OUString& r = ra.key;
              
              bool ret;
              
              if( m_xCollator.is() )
              {
                  sal_Int32 l1 = l.indexOf( sal_Unicode( ';' ) );
                  sal_Int32 l3 = ( l1 == -1 ? l.getLength() : l1 );
          
                  sal_Int32 r1 = r.indexOf( sal_Unicode( ';' ) );
                  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 = bool( l < r );
              
              return ret;
          }
          
          Reference< XCollator > m_xCollator;
      }; // end struct KeywordElementComparator
      
  }
  
  
  
  KeywordInfo::KeywordElement::KeywordElement( Databases *pDatabases,
  											 helpdatafileproxy::Hdf* pHdf,
  											 rtl::OUString& ky,
  											 rtl::OUString& data )
  	: key( ky )
  {
  	pDatabases->replaceName( key );
  	init( pDatabases,pHdf,data );
  }
  
  
  
  void KeywordInfo::KeywordElement::init( Databases *pDatabases,helpdatafileproxy::Hdf* pHdf,const rtl::OUString& ids )
  {
  	const sal_Unicode* idstr = ids.getStr();
  	std::vector< rtl::OUString > id,anchor;
  	int idx = -1,k;
  	while( ( idx = ids.indexOf( ';',k = ++idx ) ) != -1 )
  	{
  		int h = ids.indexOf( sal_Unicode( '#' ),k );
  		if( h < idx )
  		{
  			// found an anchor
  			id.push_back( rtl::OUString( &idstr[k],h-k ) );
  			anchor.push_back( rtl::OUString( &idstr[h+1],idx-h-1 ) );
  		}
  		else
  		{
  			id.push_back( rtl::OUString( &idstr[k],idx-k ) );
  			anchor.push_back( rtl::OUString() );
  		}
  	}
  	
  	listId.realloc( id.size() );
  	listAnchor.realloc( id.size() );
  	listTitle.realloc( id.size() );
  
  	int nSize = 0;
  	const sal_Char* pData = NULL;
  	const sal_Char pEmpty[] = "";
  
  	for( sal_uInt32 i = 0; i < id.size(); ++i )
  	{
  		listId[i] = id[i];
  		listAnchor[i] = anchor[i];
  
  		nSize = 0;
  		pData = pEmpty;
  		if( pHdf )
  		{
  			rtl::OString idi( id[i].getStr(),id[i].getLength(),RTL_TEXTENCODING_UTF8 );		
  			helpdatafileproxy::HDFData aHDFData;
  			bool bSuccess = pHdf->getValueForKey( idi, aHDFData );
  			if( bSuccess )
  			{
  				nSize = aHDFData.getSize();
  				pData = aHDFData.getData();
  			}
  		}
  		
  		DbtToStringConverter converter( pData, nSize );
  		
  		rtl::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( unsigned int 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 rtl::OUString& Database, const rtl::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( sal_Unicode(';') );
  	if( nLastFound == -1 )
  		nLastFound = nLen;
  	const sal_Unicode* pStr = doclist.getStr();
  	sal_Int32 nFound = doclist.lastIndexOf( sal_Unicode('_') );
  	while( nFound != -1 )
  	{
  		// Simple optimization, stop if '_' is followed by "id"
  		if( nLen - nFound > 2 )
  		{
  			if( pStr[ nFound + 1 ] == sal_Unicode('i') &&
  				pStr[ nFound + 2 ] == sal_Unicode('d') )
  					break;
  		}
  
  		rtl::OUString aModule = doclist.copy( nFound + 1, nLastFound - nFound - 1 );
  		std::vector< rtl::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( sal_Unicode('_'), nLastFound - 1 );
  	}
  
  	if( bFoundAtLeastOneModule && !bModuleMatch )
  		bBelongsToDatabase = false;
  
  	return bBelongsToDatabase;
  }
  
  
  KeywordInfo* Databases::getKeyword( const rtl::OUString& Database,
  									const rtl::OUString& Language )
  {
  	osl::MutexGuard aGuard( m_aMutex );
  	
  	rtl::OUString key = processLang(Language) + rtl::OUString::createFromAscii( "/" ) + Database;
  	
      std::pair< KeywordInfoTable::iterator,bool > aPair = 
  		m_aKeywordInfo.insert( KeywordInfoTable::value_type( key,0 ) );		
  	
  	KeywordInfoTable::iterator it = aPair.first;	
  	
  	if( aPair.second && ! it->second )
  	{
  		std::vector<KeywordInfo::KeywordElement> aVector;
  
  		KeyDataBaseFileIterator aDbFileIt( m_xContext, *this, Database, Language );
  		rtl::OUString fileURL;
  		bool bExtension = false;
  		while( (fileURL = aDbFileIt.nextDbFile( bExtension )).getLength() > 0 )
  		{
  			rtl::OUString fileNameHDFHelp( fileURL );
  			if( bExtension )
  				fileNameHDFHelp += rtl::OUString::createFromAscii( "_" );
  			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 != NULL )
  					{
  						bool bOptimizeForPerformance = true;
  						pHdf->releaseHashMap();
  						pHdf->createHashMap( bOptimizeForPerformance );
  					}
  
  					while( aHdf.getNextKeyAndValue( aKey, aValue ) )
  					{
  						rtl::OUString keyword( aKey.getData(), aKey.getSize(),
  											   RTL_TEXTENCODING_UTF8 );
  						rtl::OUString doclist( aValue.getData(), aValue.getSize(),
  											   RTL_TEXTENCODING_UTF8 );
  
  						bool bBelongsToDatabase = true;
  						if( bExtension )
  							bBelongsToDatabase = checkModuleMatchForExtension( Database, doclist );
  
  						if( !bBelongsToDatabase )
  							continue;
  					
  						aVector.push_back( KeywordInfo::KeywordElement( this,
  																		pHdf,
  																		keyword,
  																		doclist ) );
  					}
  					aHdf.stopIteration();
  
  					if( pHdf != NULL )
  						pHdf->releaseHashMap();
  				}
  			}
  		}
  
  		// sorting
  		Reference< XCollator > xCollator = getCollator( Language,rtl::OUString());
  		KeywordElementComparator aComparator( xCollator );
  		std::sort(aVector.begin(),aVector.end(),aComparator);
  		
  		KeywordInfo* pInfo = it->second = new KeywordInfo( aVector );
  		(void)pInfo;
  	}
  	
  	return it->second;
  }
  
  Reference< XHierarchicalNameAccess > Databases::jarFile( const rtl::OUString& jar,
  														 const rtl::OUString& Language )
  {
  	if( ! jar.getLength() ||
  		! Language.getLength() )
      {
  		return Reference< XHierarchicalNameAccess >( 0 );
      }
  	rtl::OUString key = processLang(Language) + aSlash + jar;
  
  	osl::MutexGuard aGuard( m_aMutex );
  	
  	ZipFileTable::iterator it =
  		m_aZipFileTable.insert( ZipFileTable::value_type( key,Reference< XHierarchicalNameAccess >(0) ) ).first;
  	
  	if( ! it->second.is() )
  	{
          rtl::OUString zipFile;
  		try
  		{
  			// Extension jar file? Search for ?
  	        sal_Int32 nQuestionMark1 = jar.indexOf( sal_Unicode('?') );
  	        sal_Int32 nQuestionMark2 = jar.lastIndexOf( sal_Unicode('?') );
  			if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 )
  			{
  				::rtl::OUString aExtensionPath = jar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
  				::rtl::OUString aPureJar = jar.copy( nQuestionMark2 + 1 );
  
  				rtl::OUStringBuffer aStrBuf;
  				aStrBuf.append( aExtensionPath );
  				aStrBuf.append( aSlash );
  				aStrBuf.append( aPureJar );
  
  				zipFile = expandURL( aStrBuf.makeStringAndClear() );
  			}
  			else
  			{
  	            zipFile = getInstallPathAsURL() + key;
  			}
  
              Sequence< Any > aArguments( 2 );
              
              XInputStream_impl* p = new XInputStream_impl( zipFile );
              if( p->CtorSuccess() )
              {
                  Reference< XInputStream > xInputStream( p );
                  aArguments[ 0 ] <<= xInputStream;
              }
              else
              {
                  delete p;
                  aArguments[ 0 ] <<= zipFile;
              }
  			
              // let ZipPackage be used ( no manifest.xml is required )
              beans::NamedValue aArg;
              aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
              aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
              aArguments[ 1 ] <<= aArg;
  
  			Reference< XInterface > xIfc
  				= m_xSMgr->createInstanceWithArgumentsAndContext(
  					rtl::OUString::createFromAscii(
  						"com.sun.star.packages.comp.ZipPackage" ),
  					aArguments, m_xContext );
  			
  			if ( xIfc.is() )
  			{
  				it->second = Reference< XHierarchicalNameAccess >( xIfc, UNO_QUERY );
  				
  				VOS_ENSURE( it->second.is(),
  							"ContentProvider::createPackage - "
  							"Got no hierarchical name access!" );
  				
  			}            
  		}
  		catch ( RuntimeException & )
  		{
  		}
  		catch ( Exception & )
  		{
  		}
  	}
  	
  	return it->second;
  }
  
  Reference< XHierarchicalNameAccess > Databases::findJarFileForPath
  	( const rtl::OUString& jar, const rtl::OUString& Language,
  	  const rtl::OUString& path, rtl::OUString* o_pExtensionPath,
        rtl::OUString* o_pExtensionRegistryPath )
  {
  	Reference< XHierarchicalNameAccess > xNA;
  	if( ! jar.getLength() ||
  		! Language.getLength() )
      {
  		return xNA;
      }
  
  	JarFileIterator aJarFileIt( m_xContext, *this, jar, Language );
  	Reference< XHierarchicalNameAccess > xTestNA;
  	Reference< deployment::XPackage > xParentPackageBundle;
  	while( (xTestNA = aJarFileIt.nextJarFile( xParentPackageBundle, o_pExtensionPath, o_pExtensionRegistryPath )).is() )
  	{
  		if( xTestNA.is() && xTestNA->hasByHierarchicalName( path ) )
  		{
  			bool bSuccess = true;
  			if( xParentPackageBundle.is() )
  			{
  				rtl::OUString aIdentifierInPath;
  				sal_Int32 nFindSlash = path.indexOf( '/' );
  				if( nFindSlash != -1 )
  					aIdentifierInPath = path.copy( 0, nFindSlash );
  
  				beans::Optional<rtl::OUString> aIdentifierOptional = xParentPackageBundle->getIdentifier();
  				if( aIdentifierInPath.getLength() && aIdentifierOptional.IsPresent )
  				{
  					rtl::OUString aUnencodedIdentifier = aIdentifierOptional.Value;
  					rtl::OUString aIdentifier = rtl::Uri::encode( aUnencodedIdentifier, 
  						rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 );
  
  					if( !aIdentifierInPath.equals( 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::popupDocument( URLParameter* urlPar,char **buffer,int *byteCount )
  {
  	const char* pop1 =
  		" <html>                                                                "
  		" <head>                                                                "
  		" <help:css-file-link xmlns:help=\"http://openoffice.org/2000/help\"/>  "
  		" </head>                                                               "
  		" <body>                                                                "
  		" <help:popup-cut Id=\"";
  	const sal_Int32 l1 = strlen( pop1 );
  	
  	const char* pop3 = "\" Eid=\"";
  	const sal_Int32 l3 = strlen( pop3 );
  
  	const char* pop5 = 
  		"\" xmlns:help=\"http://openoffice.org/2000/help\"></help:popup-cut>  "
  		" </body>                                                             "
  		" </html>";
  	const sal_Int32 l5 = strlen( pop5 );
  	sal_Int32 l2,l4;
  	
  	rtl::OUString val = urlPar->get_id();
  	rtl::OString pop2O( val.getStr(),l2 = val.getLength(),RTL_TEXTENCODING_UTF8 );
  	const char* pop2 = pop2O.getStr();
  
  	val = urlPar->get_eid();
  	rtl::OString pop4O( val.getStr(),l4 = val.getLength(),RTL_TEXTENCODING_UTF8 );
  	const char* pop4 = pop4O.getStr();
  	
  	(*byteCount) = l1 + l2 + l3 + l4 + l5;
  		
  	*buffer = new char[ 1+*byteCount ];
  
  	rtl_copyMemory( *buffer,pop1,l1 );
  	rtl_copyMemory( *buffer+l1,pop2,l2 );
  	rtl_copyMemory( *buffer+(l1+l2),pop3,l3 );
  	rtl_copyMemory( *buffer+(l1+l2+l3),pop4,l4 );
  	rtl_copyMemory( *buffer+(l1+l2+l3+l4),pop5,l5 );
  	(*buffer)[*byteCount] = 0;
  }
  
  
  void Databases::changeCSS(const rtl::OUString& newStyleSheet)
  {
  	m_aCSS = newStyleSheet.toAsciiLowerCase();
  	delete[] m_pCustomCSSDoc, m_pCustomCSSDoc = 0,m_nCustomCSSDocLength = 0;
  }
  
  
  
  void Databases::cascadingStylesheet( const rtl::OUString& Language,
  									 char** buffer,
  									 int* byteCount )
  {	
  	if( ! m_pCustomCSSDoc )
  	{
  		int retry = 2;
  		bool error = true;
  		rtl::OUString fileURL;
  
          sal_Bool bHighContrastMode = sal_False;
          rtl::OUString aCSS( m_aCSS );
          if ( aCSS.compareToAscii( "default" ) == 0 )
          {
              // #i50760: "default" needs to adapt HC mode
              uno::Reference< awt::XToolkit > xToolkit = uno::Reference< awt::XToolkit >(
                      ::comphelper::getProcessServiceFactory()->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ), uno::UNO_QUERY );
              if ( xToolkit.is() )
              {
                  uno::Reference< awt::XExtendedToolkit > xExtToolkit( xToolkit, uno::UNO_QUERY );
                  if ( xExtToolkit.is() )
                  {
                      uno::Reference< awt::XTopWindow > xTopWindow = xExtToolkit->getActiveTopWindow();
                      if ( xTopWindow.is() )
                      {
                          uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( xTopWindow, uno::UNO_QUERY );
                          if ( xVclWindowPeer.is() )
                          {
                              uno::Any aHCMode = xVclWindowPeer->getProperty( rtl::OUString::createFromAscii( "HighContrastMode" ) );
                              if ( ( aHCMode >>= bHighContrastMode ) && bHighContrastMode )
                                  aCSS = rtl::OUString::createFromAscii( "highcontrastblack" );
                          }
                      }
                  }
              }
          }
  		
  		while( error && retry )
  		{
  		    
  			if( retry == 2 )
  				fileURL = 
  					getInstallPathAsURL()  +
  					processLang( Language )       +
  					rtl::OUString::createFromAscii( "/" ) +
  					aCSS +
  					rtl::OUString::createFromAscii( ".css" );
  			else if( retry == 1 )
  				fileURL = 
  					getInstallPathAsURL()  +
  					aCSS +
  					rtl::OUString::createFromAscii( ".css" );
  			
  			osl::DirectoryItem aDirItem;
  			osl::File aFile( fileURL );
  			osl::FileStatus aStatus( FileStatusMask_FileSize );
  			
  			if( osl::FileBase::E_None == osl::DirectoryItem::get( fileURL,aDirItem ) &&
  				osl::FileBase::E_None == aFile.open( OpenFlag_Read )                 &&
  				osl::FileBase::E_None == aDirItem.getFileStatus( aStatus ) )
  			{
  				m_nCustomCSSDocLength = int( aStatus.getFileSize() );
  				m_pCustomCSSDoc = new char[ 1 + m_nCustomCSSDocLength ];
  				m_pCustomCSSDoc[ m_nCustomCSSDocLength ] = 0;
  				sal_uInt64 a = m_nCustomCSSDocLength,b = m_nCustomCSSDocLength;
  				aFile.read( m_pCustomCSSDoc,a,b );
  				aFile.close();
  				error = false;
  			}
  
  			--retry;
  			if ( !retry && error && bHighContrastMode )
  			{
  			    // fall back to default css
  			    aCSS = rtl::OUString::createFromAscii( "default" );
  			    retry = 2;
  			    bHighContrastMode = sal_False;
  			}
  		}
  		
  		if( error )
  		{
  			m_nCustomCSSDocLength = 0;
  			m_pCustomCSSDoc = new char[ 1 ]; // Initialize with 1 to avoid gcc compiler warning
  		}
  	}
  	
  	*byteCount = m_nCustomCSSDocLength;
  	*buffer = new char[ 1 + *byteCount ];
  	(*buffer)[*byteCount] = 0;
  	rtl_copyMemory( *buffer,m_pCustomCSSDoc,m_nCustomCSSDocLength );
  	
  }
  
  
  void Databases::setActiveText( const rtl::OUString& Module,
  							   const rtl::OUString& Language,
  							   const rtl::OUString& Id,
  							   char** buffer,
  							   int* byteCount )
  {
  	DataBaseIterator aDbIt( m_xContext, *this, Module, Language, true );
  
  	// #i84550 Cache information about failed ids
  	rtl::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 sal_Char* pData = NULL;
  
  	bool bSuccess = false;
  	if( !bFoundAsEmpty )
  	{
  		helpdatafileproxy::Hdf* pHdf = 0;
  		while( !bSuccess && (pHdf = aDbIt.nextHdf()) != NULL )
  		{
  			bSuccess = pHdf->getValueForKey( id, aHDFData );
  			nSize = aHDFData.getSize();
  			pData = aHDFData.getData();
  		}
  	}
  
  	if( bSuccess )
  	{
  		// ensure existence of tmp after for 
  		rtl::OString tmp;
  		for( int i = 0; i < nSize; ++i )
  			if( pData[i] == '%' || pData[i] == '$' )
  			{
  				// need of replacement
  				rtl::OUString temp = rtl::OUString( pData, nSize, RTL_TEXTENCODING_UTF8 );
  				replaceName( temp );
  				tmp = rtl::OString( temp.getStr(),
  									temp.getLength(),
  									RTL_TEXTENCODING_UTF8 );
  				nSize = tmp.getLength();
  				pData = tmp.getStr();
  				break;
  			}
  
  		*byteCount = nSize;
  		*buffer = new char[ 1 + nSize ];
  		(*buffer)[nSize] = 0;
  		rtl_copyMemory( *buffer, pData, nSize );
  	}
  	else
  	{
  		*byteCount = 0;
  		*buffer = new char[1]; // Initialize with 1 to avoid compiler warnings
  		if( !bFoundAsEmpty )
  			m_aEmptyActiveTextSet.insert( id );
  	}
  }
  
  
  void Databases::setInstallPath( const rtl::OUString& aInstDir )
  {
  	osl::MutexGuard aGuard( m_aMutex );
  	    
      osl::FileBase::getFileURLFromSystemPath( aInstDir,m_aInstallDirectory );
          //TODO: check returned error code
  	
  	if( m_aInstallDirectory.lastIndexOf( sal_Unicode( '/' ) ) != m_aInstallDirectory.getLength() - 1 )
  		m_aInstallDirectory += rtl::OUString::createFromAscii( "/" );
  	
  	m_aInstallDirectoryWithoutEncoding = rtl::Uri::decode( m_aInstallDirectory,
  														   rtl_UriDecodeWithCharset,
  														   RTL_TEXTENCODING_UTF8 );
  }
  
  
  //===================================================================
  // class ExtensionIteratorBase
  
  ExtensionHelpExistanceMap ExtensionIteratorBase::aHelpExistanceMap;
  
  ExtensionIteratorBase::ExtensionIteratorBase( Reference< XComponentContext > xContext, 
  	Databases& rDatabases, const rtl::OUString& aInitialModule, const rtl::OUString& aLanguage )
  		: m_xContext( xContext )
  		, m_rDatabases( rDatabases )
  		, m_eState( INITIAL_MODULE )
  		, m_aInitialModule( aInitialModule )
  		, m_aLanguage( aLanguage )
  {
  	init();
  }
  
  ExtensionIteratorBase::ExtensionIteratorBase( Databases& rDatabases,
  	const rtl::OUString& aInitialModule, const rtl::OUString& aLanguage )
  		: m_rDatabases( rDatabases )
  		, m_eState( INITIAL_MODULE )
  		, m_aInitialModule( aInitialModule )
  		, m_aLanguage( aLanguage )
  {
  	init();
  }
  
  void ExtensionIteratorBase::init()
  {
  	if( !m_xContext.is() )
  	{
  		Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();
  		Reference< XPropertySet > xProps( xFactory, UNO_QUERY );
  		OSL_ASSERT( xProps.is() );
  		if (xProps.is())
  		{
  			xProps->getPropertyValue(
  				::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DefaultContext") ) ) >>= m_xContext;
  			OSL_ASSERT( m_xContext.is() );
  		}
  	}
  	if( !m_xContext.is() )
  	{
  		throw RuntimeException(
  			::rtl::OUString::createFromAscii( "ExtensionIteratorBase::init(), no XComponentContext" ),
  			Reference< XInterface >() );
  	}
  
  	Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
  	m_xSFA = Reference< ucb::XSimpleFileAccess >( 
  		xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ),
  		m_xContext ), UNO_QUERY_THROW );
  
  	m_bUserPackagesLoaded = false;
  	m_bSharedPackagesLoaded = false;
      m_bBundledPackagesLoaded = false;
  	m_iUserPackage = 0;
  	m_iSharedPackage = 0;
      m_iBundledPackage = 0;
  }
  
  Reference< deployment::XPackage > ExtensionIteratorBase::implGetHelpPackageFromPackage
  	( 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
  	rtl::OUString aExtensionPath = xPackage->getURL();
  	ExtensionHelpExistanceMap::iterator it = aHelpExistanceMap.find( aExtensionPath );
  	bool bFound = ( it != aHelpExistanceMap.end() );
  	bool bHasHelp = bFound ? it->second : false;
  	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 )
  	{
  		if( xPackage->isBundle() )
  		{
  			Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle
  				( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() );
  			sal_Int32 nPkgCount = aPkgSeq.getLength();
  			const Reference< deployment::XPackage >* pSeq = aPkgSeq.getConstArray();
  			for( sal_Int32 iPkg = 0 ; iPkg < nPkgCount ; ++iPkg )
  			{
  				const Reference< deployment::XPackage > xSubPkg = pSeq[ iPkg ];
  				const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType();
  				rtl::OUString aMediaType = xPackageTypeInfo->getMediaType();
  				if( aMediaType.equals( aHelpMediaType ) )
  				{
  					xHelpPackage = xSubPkg;
  					o_xParentPackageBundle = xPackage;
  					break;
  				}
  			}
  		}
  		else
  		{
  			const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
  			rtl::OUString aMediaType = xPackageTypeInfo->getMediaType();
  			if( aMediaType.equals( aHelpMediaType ) )
  				xHelpPackage = xPackage;
  		}
  	}
  
  	if( !bFound )
  		aHelpExistanceMap[ 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
  			( rtl::OUString::createFromAscii("user"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
  		m_bUserPackagesLoaded = true;
  	}
  
  	if( m_iUserPackage == m_aUserPackagesSeq.getLength() )
  	{
  		m_eState = SHARED_EXTENSIONS;		// Later: SHARED_MODULE
  	}
  	else
  	{
  		const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray();
  		Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ];
  		VOS_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
  			( rtl::OUString::createFromAscii("shared"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
  		m_bSharedPackagesLoaded = true;
  	}
  
  	if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() )
  	{
  		m_eState = BUNDLED_EXTENSIONS;
  	}
  	else
  	{
  		const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray();
  		Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ];
  		VOS_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
  			( rtl::OUString::createFromAscii("bundled"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
  		m_bBundledPackagesLoaded = true;
  	}
  
  	if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() )
  	{
  		m_eState = END_REACHED;
  	}
  	else
  	{
  		const Reference< deployment::XPackage >* pBundledPackages =
              m_aBundledPackagesSeq.getConstArray();
  		Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ];
  		VOS_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextBundledHelpPackage(): Invalid package" );
  		xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
  	}
  
  	return xHelpPackage;
  }
  
  rtl::OUString ExtensionIteratorBase::implGetFileFromPackage(
  	const rtl::OUString& rFileExtension, Reference< deployment::XPackage > xPackage )
  {
  	// No extension -> search for pure language folder
  	bool bLangFolderOnly = (rFileExtension.getLength() == 0);
  
  	rtl::OUString aFile;
  	rtl::OUString aLanguage = m_aLanguage;
  	for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
  	{
  		rtl::OUStringBuffer aStrBuf;
  		aStrBuf.append( xPackage->getRegistrationDataURL().Value);
  		aStrBuf.append( aSlash );
  		aStrBuf.append( aLanguage );
  		if( !bLangFolderOnly )
  		{
  			aStrBuf.append( aSlash );
  			aStrBuf.append( aHelpFilesBaseName );
  			aStrBuf.append( rFileExtension );
  		}
  
  		aFile = m_rDatabases.expandURL( aStrBuf.makeStringAndClear() );
  		if( iPass == 0 )
  		{
  			if( m_xSFA->exists( aFile ) )
  				break;
  
  			::std::vector< ::rtl::OUString > av;
  			implGetLanguageVectorFromPackage( av, xPackage );
  			::std::vector< ::rtl::OUString >::const_iterator pFound = av.end();
  			try
  			{
  				pFound = ::comphelper::Locale::getFallback( av, m_aLanguage );
  			}
  			catch( ::comphelper::Locale::MalFormedLocaleException& )
  			{}
  			if( pFound != av.end() )
  				aLanguage = *pFound;
  		}
  	}
  	return aFile;
  }
  
  inline bool isLetter( sal_Unicode c )
  {
  	bool bLetter = ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
  	return bLetter;
  }
  
  void ExtensionIteratorBase::implGetLanguageVectorFromPackage( ::std::vector< ::rtl::OUString > &rv,
  	com::sun::star::uno::Reference< com::sun::star::deployment::XPackage > xPackage )
  {
  	rv.clear();
  	rtl::OUString aExtensionPath = xPackage->getURL();
  	Sequence< rtl::OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true );
  
  	const rtl::OUString* pSeq = aEntrySeq.getConstArray();
      sal_Int32 nCount = aEntrySeq.getLength();
  	for( sal_Int32 i = 0 ; i < nCount ; ++i )
  	{
  		rtl::OUString aEntry = pSeq[i];
  		if( m_xSFA->isFolder( aEntry ) )
  		{
  			sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' );
  			if( nLastSlash != -1 )
  			{
  				rtl::OUString aPureEntry = aEntry.copy( nLastSlash + 1 );
  
  				// Check language sceme
  				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 );
  			}
  		}
  	}
  }
  
  
  //===================================================================
  // class DataBaseIterator
  
  helpdatafileproxy::Hdf* DataBaseIterator::nextHdf( rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
  {
  	helpdatafileproxy::Hdf* pRetHdf = NULL;
  
  	while( !pRetHdf && m_eState != END_REACHED )
  	{
  		switch( m_eState )
  		{
  			case INITIAL_MODULE:
  				pRetHdf = m_rDatabases.getHelpDataFile( m_aInitialModule, m_aLanguage, m_bHelpText );
  				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
  				break;
  
  			// Later:
  			//case SHARED_MODULE
  				//...
  
  			case USER_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  				pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
  			case SHARED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
     			case BUNDLED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
  			case END_REACHED:
  				VOS_ENSURE( false, "DataBaseIterator::nextDb(): Invalid case END_REACHED" );
  				break;
  		}
  	}
  
  	return pRetHdf;
  }
  
  helpdatafileproxy::Hdf* DataBaseIterator::implGetHdfFromPackage( Reference< deployment::XPackage > xPackage,
              rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
  {
      
      beans::Optional< ::rtl::OUString> optRegData;
      try
      {
          optRegData = xPackage->getRegistrationDataURL();
      }
      catch ( deployment::ExtensionRemovedException&)
      {
          return NULL;
      }
  
      helpdatafileproxy::Hdf* pRetHdf = NULL;
      if (optRegData.IsPresent && optRegData.Value.getLength() > 0)
      {
          rtl::OUString aRegDataUrl(optRegData.Value); 
          aRegDataUrl += aSlash;
  
          rtl::OUString aUsedLanguage = m_aLanguage;
          pRetHdf = m_rDatabases.getHelpDataFile(
              aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl);
  
          // Language fallback
          if( !pRetHdf )
          {
              ::std::vector< ::rtl::OUString > av;
              implGetLanguageVectorFromPackage( av, xPackage );
              ::std::vector< ::rtl::OUString >::const_iterator pFound = av.end();
              try
              {
                  pFound = ::comphelper::Locale::getFallback( av, m_aLanguage );
              }
              catch( ::comphelper::Locale::MalFormedLocaleException& )
              {}
              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() + aSlash + aUsedLanguage;
      }
      
  	return pRetHdf;
  }
  
  
  //===================================================================
  // class KeyDataBaseFileIterator
  
  //returns a file URL
  rtl::OUString KeyDataBaseFileIterator::nextDbFile( bool& o_rbExtension )
  {
  	rtl::OUString aRetFile;
  
  	while( !aRetFile.getLength() && m_eState != END_REACHED )
  	{
  		switch( m_eState )
  		{
  			case INITIAL_MODULE:
  				aRetFile = 
                      m_rDatabases.getInstallPathAsURL() +
  					m_rDatabases.processLang( m_aLanguage ) + aSlash + m_aInitialModule +
  					rtl::OUString::createFromAscii( ".key" );
  
  				o_rbExtension = false;
  
  				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
  				break;
  
  			// Later:
  			//case SHARED_MODULE
  				//...
  
  			case USER_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aRetFile = implGetDbFileFromPackage( xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
  
  			case SHARED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aRetFile = implGetDbFileFromPackage( xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
  
              case BUNDLED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aRetFile = implGetDbFileFromPackage( xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
  
              case END_REACHED:
  				VOS_ENSURE( false, "DataBaseIterator::nextDbFile(): Invalid case END_REACHED" );
  				break;
  		}
  	}
  
  	return aRetFile;
  }
  
  //Returns a file URL, that does not contain macros
  rtl::OUString KeyDataBaseFileIterator::implGetDbFileFromPackage
  	( Reference< deployment::XPackage > xPackage )
  {
  	rtl::OUString aExpandedURL =
  		implGetFileFromPackage( rtl::OUString::createFromAscii( ".key" ), xPackage );
  
      return aExpandedURL;
  }
  
  
  //===================================================================
  // class JarFileIterator
  
  Reference< XHierarchicalNameAccess > JarFileIterator::nextJarFile
  	( Reference< deployment::XPackage >& o_xParentPackageBundle,
          rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
  {
  	Reference< XHierarchicalNameAccess > xNA;
  
  	while( !xNA.is() && m_eState != END_REACHED )
  	{
  		switch( m_eState )
  		{
  			case INITIAL_MODULE:
  				xNA = m_rDatabases.jarFile( m_aInitialModule, m_aLanguage );
  				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
  				break;
  
  			// Later:
  			//case SHARED_MODULE
  				//...
  
  			case USER_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( o_xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
  			case SHARED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( o_xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
  			case BUNDLED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( o_xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
  				break;
  			}
  
              case END_REACHED:
  				VOS_ENSURE( false, "JarFileIterator::nextJarFile(): Invalid case END_REACHED" );
  				break;
  		}
  	}
  
  	return xNA;
  }
  
  Reference< XHierarchicalNameAccess > JarFileIterator::implGetJarFromPackage
  ( Reference< deployment::XPackage > xPackage, rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
  {
  	Reference< XHierarchicalNameAccess > xNA;
  
  	rtl::OUString zipFile =
  		implGetFileFromPackage( rtl::OUString::createFromAscii( ".jar" ), xPackage );
  
  	try
  	{
          Sequence< Any > aArguments( 2 );
          aArguments[ 0 ] <<= zipFile;
  
          // let ZipPackage be used ( no manifest.xml is required )
          beans::NamedValue aArg;
          aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
          aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
          aArguments[ 1 ] <<= aArg;
  
  		Reference< XMultiComponentFactory >xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
  		Reference< XInterface > xIfc
  			= xSMgr->createInstanceWithArgumentsAndContext(
  				rtl::OUString::createFromAscii(
  					"com.sun.star.packages.comp.ZipPackage" ),
  				aArguments, m_xContext );
  		
  		if ( xIfc.is() )
  		{
  			xNA = Reference< XHierarchicalNameAccess >( xIfc, UNO_QUERY );
  			
  			VOS_ENSURE( xNA.is(),
  				"JarFileIterator::implGetJarFromPackage() - "
  				"Got no hierarchical name access!" );
  		}            
  	}
  	catch ( RuntimeException & )
  	{}
  	catch ( Exception & )
  	{}
  
  	if( xNA.is() && o_pExtensionPath != NULL )
  	{
  		// Extract path including language from file name
  		sal_Int32 nLastSlash = zipFile.lastIndexOf( '/' );
  		if( nLastSlash != -1 )
  			*o_pExtensionPath = zipFile.copy( 0, nLastSlash );
          
          if( o_pExtensionRegistryPath != NULL )
          {
              rtl::OUString& rPath = *o_pExtensionPath;
              sal_Int32 nLastSlashInPath = rPath.lastIndexOf( '/', rPath.getLength() - 1 );
  
              *o_pExtensionRegistryPath = xPackage->getURL();
              *o_pExtensionRegistryPath += rPath.copy( nLastSlashInPath);
          }
  	}
  	
  	return xNA;
  }
  
  
  //===================================================================
  // class IndexFolderIterator
  
  rtl::OUString IndexFolderIterator::nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary )
  {
  	rtl::OUString aIndexFolder;
  
  	while( !aIndexFolder.getLength() && m_eState != END_REACHED )
  	{
  		switch( m_eState )
  		{
  			case INITIAL_MODULE:
  				aIndexFolder = 
  					m_rDatabases.getInstallPathAsURL() +
  					m_rDatabases.processLang( m_aLanguage ) + aSlash + m_aInitialModule +
  					rtl::OUString::createFromAscii( ".idxl" );
  
  				o_rbTemporary = false;
  				o_rbExtension = false;
  
  				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
  				break;
  
  			// Later:
  			//case SHARED_MODULE
  				//...
  
  			case USER_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
  
  			case SHARED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
              
  			case BUNDLED_EXTENSIONS:
  			{
  				Reference< deployment::XPackage > xParentPackageBundle;
  				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
  				if( !xHelpPackage.is() )
  					break;
  
  				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
  				o_rbExtension = true;
  				break;
  			}
  
              case END_REACHED:
  				VOS_ENSURE( false, "IndexFolderIterator::nextIndexFolder(): Invalid case END_REACHED" );
  				break;
  		}
  	}
  
  	return aIndexFolder;
  }
  
  rtl::OUString IndexFolderIterator::implGetIndexFolderFromPackage( bool& o_rbTemporary, Reference< deployment::XPackage > xPackage )
  {
  	rtl::OUString aIndexFolder =
  		implGetFileFromPackage( rtl::OUString::createFromAscii( ".idxl" ), xPackage );
  
  	o_rbTemporary = false;
  	if( !m_xSFA->isFolder( aIndexFolder ) )
  	{
  		// i98680: Missing index? Try to generate now
  		rtl::OUString aLangURL = implGetFileFromPackage( rtl::OUString(), xPackage );
  		if( m_xSFA->isFolder( aLangURL ) )
  		{
  			// Test write access (shared extension may be read only)
  			bool bIsWriteAccess = false;
  			try
  			{
  				rtl::OUString aCreateTestFolder = aLangURL + rtl::OUString::createFromAscii( "CreateTestFolder" );
  				m_xSFA->createFolder( aCreateTestFolder );
  				if( m_xSFA->isFolder( aCreateTestFolder  ) )
  					bIsWriteAccess = true;
  
  				m_xSFA->kill( aCreateTestFolder );
  			}
  			catch (Exception &)
  			{}
  
  			// TEST
  			//bIsWriteAccess = false;
  
  			Reference< script::XInvocation > xInvocation;
  			Reference< XMultiComponentFactory >xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
  			try
  			{
  				xInvocation = Reference< script::XInvocation >( 
  					m_xContext->getServiceManager()->createInstanceWithContext( rtl::OUString::createFromAscii(
  					"com.sun.star.help.HelpIndexer" ), m_xContext ) , UNO_QUERY );
  
  				if( xInvocation.is() )
  				{
  					Sequence<uno::Any> aParamsSeq( bIsWriteAccess ? 6 : 8 );
  
  					aParamsSeq[0] = uno::makeAny( rtl::OUString::createFromAscii( "-lang" ) );
  
  					rtl::OUString aLang;
  					sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
  					if( nLastSlash != -1 )
  						aLang = aLangURL.copy( nLastSlash + 1 );
  					else
  						aLang = rtl::OUString::createFromAscii( "en" );
  					aParamsSeq[1] = uno::makeAny( aLang );
  
  					aParamsSeq[2] = uno::makeAny( rtl::OUString::createFromAscii( "-mod" ) );
  					aParamsSeq[3] = uno::makeAny( rtl::OUString::createFromAscii( "help" ) );
  
  					rtl::OUString aZipDir = aLangURL;
  					if( !bIsWriteAccess )
  					{
  						rtl::OUString aTempFileURL;
  						::osl::FileBase::RC eErr = ::osl::File::createTempFile( 0, 0, &aTempFileURL );
  						if( eErr == ::osl::FileBase::E_None )
  						{
  							rtl::OUString aTempDirURL = aTempFileURL;
  							try
  							{
  								m_xSFA->kill( aTempDirURL );
  							}
  							catch (Exception &)
  							{}
  							m_xSFA->createFolder( aTempDirURL );
  
  							aZipDir = aTempDirURL;
  							o_rbTemporary = true;
  						}
  					}
  
  					aParamsSeq[4] = uno::makeAny( rtl::OUString::createFromAscii( "-zipdir" ) );
  					rtl::OUString aSystemPath;
  					osl::FileBase::getSystemPathFromFileURL( aZipDir, aSystemPath );
  					aParamsSeq[5] = uno::makeAny( aSystemPath );
  
  					if( !bIsWriteAccess )
  					{
  						aParamsSeq[6] = uno::makeAny( rtl::OUString::createFromAscii( "-srcdir" ) );
  						rtl::OUString aSrcDirVal;
  						osl::FileBase::getSystemPathFromFileURL( aLangURL, aSrcDirVal );
  						aParamsSeq[7] = uno::makeAny( aSrcDirVal );
  					}
  
  					Sequence< sal_Int16 > aOutParamIndex;
  					Sequence< uno::Any > aOutParam;
  					uno::Any aRet = xInvocation->invoke( rtl::OUString::createFromAscii( "createIndex" ),
  						aParamsSeq, aOutParamIndex, aOutParam );
  
  					if( bIsWriteAccess )
  						aIndexFolder = implGetFileFromPackage( rtl::OUString::createFromAscii( ".idxl" ), xPackage );
  					else
  						aIndexFolder = aZipDir + rtl::OUString::createFromAscii( "/help.idxl" );
  				}
  			}
  			catch (Exception &)
  			{}
  		}
  	}
  
  	return aIndexFolder;
  }
  
  void IndexFolderIterator::deleteTempIndexFolder( const rtl::OUString& aIndexFolder )
  {
  	sal_Int32 nLastSlash = aIndexFolder.lastIndexOf( '/' );
  	if( nLastSlash != -1 )
  	{
  		rtl::OUString aTmpFolder = aIndexFolder.copy( 0, nLastSlash );
  		try
  		{
  			m_xSFA->kill( aTmpFolder );
  		}
  		catch (Exception &)
  		{}
  	}
  }
  