/**************************************************************
 * 
 * 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 <string.h>
#include <rtl/ustrbuf.hxx>
#ifndef _VOS_DIAGNOSE_HXX_
#include <vos/diagnose.hxx>
#endif
#include "tvread.hxx"
#include <expat.h>
#include <osl/file.hxx>
#include <com/sun/star/frame/XConfigManager.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>

#include <comphelper/processfactory.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include "com/sun/star/deployment/thePackageManagerFactory.hpp"
#include <com/sun/star/util/XMacroExpander.hpp>
#include <com/sun/star/uri/XUriReferenceFactory.hpp>
#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
#include <comphelper/locale.hxx>

namespace treeview {

	
	class TVDom
	{
		friend class TVChildTarget;
		friend class TVRead;
	
	public:
		
		TVDom( TVDom* arent = 0 )
			: kind( other ),
              parent( arent ),
			  childs( 0 )
		{
		}

		~TVDom()
		{
			for( unsigned i = 0; i < childs.size(); ++i )
				delete childs[i];
		}
	
	
		TVDom* newChild()
		{
			childs.push_back( new TVDom( this ) );
			return childs.back();
		}
	
	
		TVDom* getParent() const
		{
			if( parent )
				return parent;
			else
				return const_cast<TVDom*>(this);    // I am my own parent, if I am the root
		}

		enum Kind {
			tree_view,
			tree_node,
			tree_leaf,
			other
		};
	
		bool isLeaf() const { return kind == TVDom::tree_leaf; }
		void setKind( Kind ind ) { kind = ind; }
		Kind getKind( ) const { return kind; }
	
	
		void setApplication( const char* appl )
		{
			application = rtl::OUString( (sal_Char*)(appl),
										 strlen( appl ),
										 RTL_TEXTENCODING_UTF8 );
		}
	
		void setTitle( const char* itle )
		{
			title += rtl::OUString( (sal_Char*)(itle),
                                    strlen( itle ),
                                    RTL_TEXTENCODING_UTF8 );
		}
        
		void setTitle( const XML_Char* itle,int len )
		{
			title += rtl::OUString( (sal_Char*)(itle),
                                    len,
                                    RTL_TEXTENCODING_UTF8 );
		}
	
		void setId( const char* d )
		{
			id = rtl::OUString( (sal_Char*)(d),
								strlen( d ),
								RTL_TEXTENCODING_UTF8 );
		}	

		void setAnchor( const char* nchor )
		{
			anchor = rtl::OUString( (sal_Char*)(nchor),
									strlen( nchor ),
									RTL_TEXTENCODING_UTF8 );
		}

		rtl::OUString getTargetURL()
		{
			if( ! targetURL.getLength() )
			{
				sal_Int32 len;
                for ( const TVDom* p = this;; p = p->parent )
                {
                    len = p->application.getLength();
                    if ( len != 0 )
                        break;
                }
				
				rtl::OUStringBuffer strBuff( 22 + len + id.getLength() );
				strBuff.appendAscii(
									"vnd.sun.star.help://"
									).append(id);
				
				targetURL = strBuff.makeStringAndClear();
			}
			
			return targetURL;
		}
	
	private:
	
		Kind   kind;
		rtl::OUString  application;
		rtl::OUString  title;
		rtl::OUString  id;
		rtl::OUString  anchor;
		rtl::OUString  targetURL;
		
		TVDom *parent;
		std::vector< TVDom* > childs;
	};

}



using namespace treeview;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::beans;
using namespace com::sun::star::lang;
using namespace com::sun::star::util;
using namespace com::sun::star::frame;
using namespace com::sun::star::container;
using namespace com::sun::star::deployment;


ConfigData::ConfigData()
	: 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" ) )
{
}

void SAL_CALL ConfigData::replaceName( rtl::OUString& oustring ) const
{
	sal_Int32 idx = -1,k = 0,off;
	bool cap = false;
	rtl::OUStringBuffer aStrBuf( 0 );
	
	while( ( idx = oustring.indexOf( sal_Unicode('%'),++idx ) ) != -1 )
	{
		if( oustring.indexOf( prodName,idx ) == idx )
			off = PRODUCTNAME;
		else if( oustring.indexOf( prodVersion,idx ) == idx )
			off = PRODUCTVERSION;
		else if( oustring.indexOf( vendName,idx ) == idx )
			off = VENDORNAME;
		else if( oustring.indexOf( vendVersion,idx ) == idx )
			off = VENDORVERSION;
		else if( oustring.indexOf( vendShort,idx ) == idx )
			off = VENDORSHORT;
		else
			off = -1;
		
		if( off != -1 )
		{
			if( ! cap )
			{
				cap = true;
				aStrBuf.ensureCapacity( 256 );
			}
			
			aStrBuf.append( &oustring.getStr()[k],idx - k );
			aStrBuf.append( m_vReplacement[off] );
			k = idx + m_vAdd[off];
		}
	}
	
	if( cap )
	{
		if( k < oustring.getLength() )
			aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
		oustring = aStrBuf.makeStringAndClear();
	}
}




//////////////////////////////////////////////////////////////////////////
// XInterface
//////////////////////////////////////////////////////////////////////////


void SAL_CALL
TVBase::acquire(
	void )
	throw()
{
  OWeakObject::acquire();
}


void SAL_CALL
TVBase::release(
		      void )
  throw()
{
  OWeakObject::release();
}


Any SAL_CALL
TVBase::queryInterface(
	const Type& rType )
	throw( RuntimeException )
{
	Any aRet = cppu::queryInterface( rType,
									 SAL_STATIC_CAST( XTypeProvider*, this ),
									 SAL_STATIC_CAST( XNameAccess*, this ),
									 SAL_STATIC_CAST( XHierarchicalNameAccess*, this ),
									 SAL_STATIC_CAST( XChangesNotifier*, this ),
									 SAL_STATIC_CAST( XComponent*, this ) );
	
	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}


////////////////////////////////////////////////////////////////////////////////
//
// XTypeProvider methods.

XTYPEPROVIDER_IMPL_5( TVBase,
				   	  XTypeProvider,
					  XNameAccess,
					  XHierarchicalNameAccess,
					  XChangesNotifier,
					  XComponent );






// TVRead


TVRead::TVRead( const ConfigData& configData,TVDom* tvDom )
{
	if( ! tvDom )
		return;
	
	Title = tvDom->title;
	configData.replaceName( Title );
	if( tvDom->isLeaf() )
	{
		TargetURL = ( tvDom->getTargetURL() + configData.appendix );
		if( tvDom->anchor.getLength() )
			TargetURL += ( rtl::OUString::createFromAscii( "#" ) +
						   tvDom->anchor );
	}
	else
		Children = new TVChildTarget( configData,tvDom );
}



TVRead::~TVRead()
{
}






// XNameAccess

Any SAL_CALL
TVRead::getByName( const rtl::OUString& aName )
	throw( NoSuchElementException,
		   WrappedTargetException,
		   RuntimeException )
{
	bool found( true );
	Any aAny;
	if( aName.compareToAscii( "Title" ) == 0 )
		aAny <<= Title;
	else if( aName.compareToAscii( "TargetURL" ) == 0 )
		aAny <<= TargetURL;
	else if( aName.compareToAscii( "Children" ) == 0 )
	{			   
		cppu::OWeakObject* p = Children.get();
		aAny <<= Reference< XInterface >( p );
	}
	else
		found = false;
	
	if( found )
		return aAny;

	throw NoSuchElementException();
}




Sequence< rtl::OUString > SAL_CALL
TVRead::getElementNames( )
	throw( RuntimeException )
{
	Sequence< rtl::OUString > seq( 3 );
	
	seq[0] = rtl::OUString::createFromAscii( "Title" );
	seq[1] = rtl::OUString::createFromAscii( "TargetURL" );
	seq[2] = rtl::OUString::createFromAscii( "Children" );

	return seq;
}



sal_Bool SAL_CALL
TVRead::hasByName( const rtl::OUString& aName )
	throw( RuntimeException )
{
	if( aName.compareToAscii( "Title" ) == 0        ||
		aName.compareToAscii( "TargetURL" ) == 0    ||
		aName.compareToAscii( "Children" ) == 0 )
		return true;
	
	return false;
}


// XHierarchicalNameAccess

Any SAL_CALL
TVRead::getByHierarchicalName( const rtl::OUString& aName )
	throw( NoSuchElementException,
		   RuntimeException )
{
	sal_Int32 idx;
	rtl::OUString name( aName );
	
	if( ( idx = name.indexOf( sal_Unicode( '/' ) ) ) != -1  &&
		name.copy( 0,idx ).compareToAscii( "Children" ) == 0 )
		return Children->getByHierarchicalName( name.copy( 1 + idx ) );
	
	return getByName( name );
}




sal_Bool SAL_CALL
TVRead::hasByHierarchicalName( const rtl::OUString& aName )
	throw( RuntimeException )
{
	sal_Int32 idx;
	rtl::OUString name( aName );
	
   	if( ( idx = name.indexOf( sal_Unicode( '/' ) ) ) != -1  &&
		name.copy( 0,idx ).compareToAscii( "Children" ) == 0 )
		return Children->hasByHierarchicalName( name.copy( 1 + idx ) );
	
	return hasByName( name );
}



/**************************************************************************/
/*                                                                        */
/*                      TVChildTarget                                     */
/*                                                                        */
/**************************************************************************/




extern "C" void start_handler(void *userData,
				   const XML_Char *name,
				   const XML_Char **atts)
{	
	TVDom::Kind kind;
	
	if( strcmp( name,"help_section" ) == 0  ||
		strcmp( name,"node" ) == 0 )
		kind = TVDom::tree_node;
	else if( strcmp( name,"topic" ) == 0 )
		kind = TVDom::tree_leaf;
	else
		return;
	
	TVDom **tvDom = static_cast< TVDom** >( userData );
	TVDom  *p;
	p = *tvDom;

	*tvDom = p->newChild();
	p = *tvDom;
	
	p->setKind( kind );
	while( *atts )
	{
		if( strcmp( *atts,"application" ) == 0 )
			p->setApplication( *(atts+1) );
		else if( strcmp( *atts,"title" ) == 0 )
			p->setTitle( *(atts+1) );
		else if( strcmp( *atts,"id" ) == 0 )
			p->setId( *(atts+1) );
		else if( strcmp( *atts,"anchor" ) == 0 )
			p->setAnchor( *(atts+1) );
		
		atts+=2;
	}
}


extern "C" void end_handler(void *userData,
				 const XML_Char *name )
{
	(void)name;

	TVDom **tvDom = static_cast< TVDom** >( userData );
	*tvDom = (*tvDom)->getParent();
}


extern "C" void data_handler( void *userData,
				   const XML_Char *s,
				   int len)
{
	TVDom **tvDom = static_cast< TVDom** >( userData );
	if( (*tvDom)->isLeaf() )
		(*tvDom)->setTitle( s,len );
}



TVChildTarget::TVChildTarget( const ConfigData& configData,TVDom* tvDom )
{
	Elements.resize( tvDom->childs.size() );
	for( unsigned i = 0; i < Elements.size(); ++i )
		Elements[i] = new TVRead( configData,tvDom->childs[i] );
}
	
							  



TVChildTarget::TVChildTarget( const Reference< XMultiServiceFactory >& xMSF )
{	
	ConfigData configData = init( xMSF );

	if( ! configData.locale.getLength()  ||
		! configData.system.getLength() )
		return;
	
	sal_uInt64 	ret,len = 0;
	int j = configData.vFileURL.size();
	
	TVDom tvDom;
	TVDom* pTVDom = &tvDom;

	while( j )
	{
		len = configData.vFileLen[--j];
		char* s = new char[ int(len) ];  // the buffer to hold the installed files
		osl::File aFile( configData.vFileURL[j] );
		aFile.open( OpenFlag_Read );
		aFile.read( s,len,ret );
		aFile.close();
		
		XML_Parser parser = XML_ParserCreate( 0 );
		XML_SetElementHandler( parser,
							   start_handler,
							   end_handler );
		XML_SetCharacterDataHandler( parser,
									 data_handler);
		XML_SetUserData( parser,&pTVDom ); // does not return this
		
		int parsed = XML_Parse( parser,s,int( len ),j==0 );
        (void)parsed;
		OSL_ENSURE( parsed, "TVChildTarget::TVChildTarget(): Tree file parsing failed" );
		
		XML_ParserFree( parser );		
		delete[] s;
	}	
	
	// now TVDom holds the relevant information

	Elements.resize( tvDom.childs.size() );
	for( unsigned i = 0; i < Elements.size(); ++i )
		Elements[i] = new TVRead( configData,tvDom.childs[i] );
}


TVChildTarget::~TVChildTarget()
{
}



Any SAL_CALL
TVChildTarget::getByName( const rtl::OUString& aName )
	throw( NoSuchElementException,
		   WrappedTargetException,
		   RuntimeException )
{
	rtl::OUString num( aName.getStr()+2,aName.getLength()-4 ); 
	sal_Int32 idx = num.toInt32() - 1;
	if( idx < 0 || Elements.size() <= sal_uInt32( idx ) )
		throw NoSuchElementException();
	
	Any aAny;
	cppu::OWeakObject* p = Elements[idx].get();
	aAny <<= Reference< XInterface >( p );
	return aAny;
}




Sequence< rtl::OUString > SAL_CALL
TVChildTarget::getElementNames( )
	throw( RuntimeException )
{
	Sequence< rtl::OUString > seq( Elements.size() );
	for( unsigned i = 0; i < Elements.size(); ++i )
		seq[i] = rtl::OUString::valueOf( sal_Int32( 1+i ) );
	
	return seq;
}



sal_Bool SAL_CALL
TVChildTarget::hasByName( const rtl::OUString& aName )
	throw( RuntimeException )
{
	rtl::OUString num( aName.getStr()+2,aName.getLength()-4 ); 
	sal_Int32 idx = num.toInt32() - 1;
	if( idx < 0 || Elements.size() <= sal_uInt32( idx ) )
		return false;
	
	return true;
}



// XHierarchicalNameAccess

Any SAL_CALL
TVChildTarget::getByHierarchicalName( const rtl::OUString& aName )
	throw( NoSuchElementException,
		   RuntimeException )
{
	sal_Int32 idx;
	rtl::OUString name( aName );
	
	if( ( idx = name.indexOf( sal_Unicode( '/' ) ) ) != -1 )
	{
		rtl::OUString num( name.getStr()+2,idx-4 ); 
		sal_Int32 pref = num.toInt32() - 1;
		
		if( pref < 0 || Elements.size() <= sal_uInt32( pref ) )
			throw NoSuchElementException();
		
		return Elements[pref]->getByHierarchicalName( name.copy( 1 + idx ) );
	}
	else
		return getByName( name );
}



sal_Bool SAL_CALL
TVChildTarget::hasByHierarchicalName( const rtl::OUString& aName )
	throw( RuntimeException )
{
	sal_Int32 idx;
	rtl::OUString name( aName );
	
   	if( ( idx = name.indexOf( sal_Unicode( '/' ) ) ) != -1 )
	{
		rtl::OUString num( name.getStr()+2,idx-4 );
		sal_Int32 pref = num.toInt32() - 1;		
		if( pref < 0 || Elements.size() <= sal_uInt32( pref ) )
			return false;
		
		return Elements[pref]->hasByHierarchicalName( name.copy( 1 + idx ) );
	}
	else
		return hasByName( name );
}






ConfigData TVChildTarget::init( const Reference< XMultiServiceFactory >& xSMgr )
{
	ConfigData configData;
	Reference< XMultiServiceFactory >  sProvider( getConfiguration(xSMgr) );
	
	/**********************************************************************/
	/*                       reading Office.Common                        */
	/**********************************************************************/
	
	Reference< XHierarchicalNameAccess > xHierAccess( getHierAccess( sProvider,
																	 "org.openoffice.Office.Common" ) );
	rtl::OUString system( getKey( xHierAccess,"Help/System" ) );	
	sal_Bool showBasic( getBooleanKey(xHierAccess,"Help/ShowBasic") );
	rtl::OUString instPath( getKey( xHierAccess,"Path/Current/Help" ) );
	if( ! instPath.getLength() )
	  // try to determine path from default
	  instPath = rtl::OUString::createFromAscii( "$(instpath)/help" );
	
	// replace anything like $(instpath);
	subst( xSMgr,instPath );

	/**********************************************************************/
	/*                       reading setup                                */
	/**********************************************************************/

	xHierAccess = getHierAccess( sProvider,
								 "org.openoffice.Setup" );
	
	rtl::OUString productName( getKey(  xHierAccess,"Product/ooName" ) );
    rtl::OUString setupversion( getKey( xHierAccess,"Product/ooSetupVersion" ) );
    rtl::OUString setupextension;

    try
    {
        uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
	          xSMgr ->createInstance(::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider")), uno::UNO_QUERY_THROW);

        uno::Sequence < uno::Any > lParams(1);
        beans::PropertyValue                       aParam ;
        aParam.Name    = ::rtl::OUString::createFromAscii("nodepath");
        aParam.Value <<= ::rtl::OUString::createFromAscii("/org.openoffice.Setup/Product");
        lParams[0] = uno::makeAny(aParam);

        // open it
        uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
                    ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationAccess"),
                    lParams) );

        uno::Reference< container::XNameAccess > xDirectAccess(xCFG, uno::UNO_QUERY);
        uno::Any aRet = xDirectAccess->getByName(::rtl::OUString::createFromAscii("ooSetupExtension"));

        aRet >>= setupextension;
    }
    catch ( uno::Exception& )
    {
    }

	rtl::OUString productVersion( setupversion + 
								  rtl::OUString::createFromAscii( " " ) +
								  setupextension );
	rtl::OUString locale( getKey( xHierAccess,"L10N/ooLocale" ) );

	
	// Determine fileurl from url and locale
	rtl::OUString url;
	osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url );
	if( errFile != osl::FileBase::E_None ) return configData;
	if( url.lastIndexOf( sal_Unicode( '/' ) ) != url.getLength() - 1 )
		url += rtl::OUString::createFromAscii( "/" );
	rtl::OUString ret;
	sal_Int32 idx;
	osl::DirectoryItem aDirItem;
	if( osl::FileBase::E_None == osl::DirectoryItem::get( url + locale,aDirItem ) )
		ret = locale;
	else if( ( ( idx = locale.indexOf( '-' ) ) != -1 ||
			   ( idx = locale.indexOf( '_' ) ) != -1 ) &&
			 osl::FileBase::E_None == osl::DirectoryItem::get( url + locale.copy( 0,idx ),
															   aDirItem ) )
		ret = locale.copy( 0,idx );
    else
        {
        locale = rtl::OUString::createFromAscii( "en-US" );
        ret = rtl::OUString::createFromAscii("en");
        }
	url = url + ret;
	
	// first of all, try do determine whether there are any *.tree files present

	// Start with extensions to set them at the end of the list
	TreeFileIterator aTreeIt( locale );
	rtl::OUString aTreeFile;
	sal_Int32 nFileSize;
	while( (aTreeFile = aTreeIt.nextTreeFile( nFileSize ) ).getLength() > 0 )
	{
		configData.vFileLen.push_back( nFileSize );
		configData.vFileURL.push_back( aTreeFile );
	}

	osl::Directory aDirectory( url );
	osl::FileStatus aFileStatus( FileStatusMask_FileName | FileStatusMask_FileSize | FileStatusMask_FileURL );
	if( osl::Directory::E_None == aDirectory.open() )
	{
		int idx_ = 0;
		rtl::OUString aFileUrl, aFileName;
		while( aDirectory.getNextItem( aDirItem ) == osl::FileBase::E_None &&
			   aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
			   aFileStatus.isValid( FileStatusMask_FileURL ) && 
			   aFileStatus.isValid( FileStatusMask_FileName ) )
		  {
			aFileUrl = aFileStatus.getFileURL();
			aFileName = aFileStatus.getFileName();
			idx_ = aFileName.lastIndexOf( sal_Unicode( '.' ) );
			if( idx_ == -1 )
			  continue;
			
			const sal_Unicode* str = aFileName.getStr();
			
			if( aFileName.getLength() == idx_ + 5                   && 
				( str[idx_ + 1] == 't' || str[idx_ + 1] == 'T' )    && 
				( str[idx_ + 2] == 'r' || str[idx_ + 2] == 'R' )    &&
				( str[idx_ + 3] == 'e' || str[idx_ + 3] == 'E' )    &&
				( str[idx_ + 4] == 'e' || str[idx_ + 4] == 'E' ) )
			  {
				OSL_ENSURE( aFileStatus.isValid( FileStatusMask_FileSize ),
                            "invalid file size" );			
				
				rtl::OUString baseName = aFileName.copy(0,idx_).toAsciiLowerCase();
				if(! showBasic && baseName.compareToAscii("sbasic") == 0 )
				  continue;
				
				configData.vFileLen.push_back( aFileStatus.getFileSize() );
				configData.vFileURL.push_back( aFileUrl );
			  }
		  }
		aDirectory.close();
	}

	configData.m_vAdd[0] = 12;
	configData.m_vAdd[1] = 15;
	configData.m_vAdd[2] = 11;
	configData.m_vAdd[3] = 14;
	configData.m_vAdd[4] = 12;
	configData.m_vReplacement[0] = productName;
	configData.m_vReplacement[1] = productVersion;
	// m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings

   	configData.system = system;
	configData.locale = locale;		
	configData.appendix =
		rtl::OUString::createFromAscii( "?Language=" ) +
		configData.locale +
		rtl::OUString::createFromAscii( "&System=" ) +
		configData.system +
        rtl::OUString::createFromAscii( "&UseDB=no" ) ;
	
	return configData;
}









Reference< XMultiServiceFactory >
TVChildTarget::getConfiguration(const Reference< XMultiServiceFactory >& m_xSMgr) const
{
	Reference< XMultiServiceFactory > sProvider;
	if( m_xSMgr.is() )
	{
		try
		{
			rtl::OUString sProviderService =
				rtl::OUString::createFromAscii( "com.sun.star.configuration.ConfigurationProvider" );
			sProvider =
				Reference< XMultiServiceFactory >(
					m_xSMgr->createInstance( sProviderService ),
					UNO_QUERY );
		}
		catch( const com::sun::star::uno::Exception& )
		{
			OSL_ENSURE( sProvider.is(),"can't instantiate configuration" );
		}
	}
	
	return sProvider;
}



Reference< XHierarchicalNameAccess >
TVChildTarget::getHierAccess( const Reference< XMultiServiceFactory >& sProvider,
							  const char* file ) const
{
	Reference< XHierarchicalNameAccess > xHierAccess;
	
	if( sProvider.is() )
	{
		Sequence< Any > seq(1);
		rtl::OUString sReaderService =
			rtl::OUString::createFromAscii( "com.sun.star.configuration.ConfigurationAccess" );
		
		seq[0] <<= rtl::OUString::createFromAscii( file );		
		
		try
		{
			xHierAccess = 
				Reference< XHierarchicalNameAccess >
				( sProvider->createInstanceWithArguments( sReaderService,seq ),
				  UNO_QUERY );
		}
		catch( const com::sun::star::uno::Exception& )
		{
		}
	}
	
	return xHierAccess;
}



rtl::OUString
TVChildTarget::getKey( const Reference< XHierarchicalNameAccess >& xHierAccess,
					   const char* key ) const
{
    rtl::OUString instPath;
	if( xHierAccess.is() )
	{
		Any aAny;
		try
		{
			aAny =
				xHierAccess->getByHierarchicalName( rtl::OUString::createFromAscii( key ) );
		}
		catch( const com::sun::star::container::NoSuchElementException& )
		{
		}
		aAny >>= instPath;
	}
	return instPath;
}


sal_Bool
TVChildTarget::getBooleanKey(const Reference<
							 XHierarchicalNameAccess >& xHierAccess,
							 const char* key) const
{
  sal_Bool ret = sal_False;
  if( xHierAccess.is() )
	{
	  Any aAny;
	  try
		{
		  aAny =
			xHierAccess->getByHierarchicalName(
											   rtl::OUString::createFromAscii(key));
		}
	  catch( const com::sun::star::container::NoSuchElementException& )
		{
		}
	  aAny >>= ret;
	}
  return ret;
}


void TVChildTarget::subst( const Reference< XMultiServiceFactory >& m_xSMgr,
						   rtl::OUString& instpath ) const
{
	Reference< XConfigManager >  xCfgMgr;
	if( m_xSMgr.is() )
	{
		try
		{
			xCfgMgr =
				Reference< XConfigManager >(
					m_xSMgr->createInstance( rtl::OUString::createFromAscii( "com.sun.star.config.SpecialConfigManager" ) ),
					UNO_QUERY );
		}
		catch( const com::sun::star::uno::Exception& )
		{
			OSL_ENSURE( xCfgMgr.is()," can't instantiate the special config manager " );
		}
    }
	
	OSL_ENSURE( xCfgMgr.is(), "specialconfigmanager not found\n" );
	
	if( xCfgMgr.is() )
        instpath = xCfgMgr->substituteVariables( instpath );
}


//===================================================================
// class ExtensionIteratorBase

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" ) );

ExtensionIteratorBase::ExtensionIteratorBase( const rtl::OUString& aLanguage )
		: m_eState( USER_EXTENSIONS )
		, m_aLanguage( aLanguage )
{
	init();
}

void ExtensionIteratorBase::init()
{
	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;

	// Check if parent package is registered
    beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered
		( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) );
	bool bRegistered = false;
    if( option.IsPresent )
    {
        beans::Ambiguous<sal_Bool> const & reg = option.Value;
        if( !reg.IsAmbiguous && reg.Value )
			bRegistered = true;
    }
	if( !bRegistered )
		return xHelpPackage;

	if( xPackage->isBundle() )
	{
		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;
	}

	return xHelpPackage;
}

Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextUserHelpPackage
	( Reference< deployment::XPackage >& o_xParentPackageBundle )
{
	Reference< deployment::XPackage > xHelpPackage;

	if( !m_bUserPackagesLoaded )
	{
		Reference< XPackageManager > xUserManager =
			thePackageManagerFactory::get( m_xContext )->getPackageManager( rtl::OUString::createFromAscii("user") );
		m_aUserPackagesSeq = xUserManager->getDeployedPackages
			( 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< XPackageManager > xSharedManager =
			thePackageManagerFactory::get( m_xContext )->getPackageManager( rtl::OUString::createFromAscii("shared") );
		m_aSharedPackagesSeq = xSharedManager->getDeployedPackages
			( 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< XPackageManager > xBundledManager =
			thePackageManagerFactory::get( m_xContext )->getPackageManager( rtl::OUString::createFromAscii("bundled") );
		m_aBundledPackagesSeq = xBundledManager->getDeployedPackages
			( 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;
}

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 TreeFileIterator

rtl::OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize )
{
	rtl::OUString aRetFile;

	while( !aRetFile.getLength() && m_eState != END_REACHED )
	{
		switch( m_eState )
		{
			case USER_EXTENSIONS:
			{
				Reference< deployment::XPackage > xParentPackageBundle;
				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
				if( !xHelpPackage.is() )
					break;

				aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
				break;
			}

			case SHARED_EXTENSIONS:
			{
				Reference< deployment::XPackage > xParentPackageBundle;
				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
				if( !xHelpPackage.is() )
					break;

				aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
				break;
			}
			case BUNDLED_EXTENSIONS:
			{
				Reference< deployment::XPackage > xParentPackageBundle;
				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
				if( !xHelpPackage.is() )
					break;

				aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
				break;
			}

        case END_REACHED:
				VOS_ENSURE( false, "DataBaseIterator::nextTreeFile(): Invalid case END_REACHED" );
				break;
		}
	}

	return aRetFile;
}

rtl::OUString TreeFileIterator::expandURL( const rtl::OUString& aURL )
{
	static Reference< util::XMacroExpander > xMacroExpander;
	static Reference< uri::XUriReferenceFactory > xFac;

	osl::MutexGuard aGuard( m_aMutex );

	if( !xMacroExpander.is() || !xFac.is() )
	{
		Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager(), UNO_QUERY );

		xFac = Reference< uri::XUriReferenceFactory >(
			xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii(
			"com.sun.star.uri.UriReferenceFactory"), m_xContext ) , UNO_QUERY );
		if( !xFac.is() )
		{
			throw RuntimeException(
				::rtl::OUString::createFromAscii( "Databases::expand(), could not instatiate UriReferenceFactory." ),
				Reference< XInterface >() );
		}

		xMacroExpander = Reference< util::XMacroExpander >(
			m_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;
}

rtl::OUString TreeFileIterator::implGetTreeFileFromPackage
	( sal_Int32& rnFileSize, Reference< deployment::XPackage > xPackage )
{
	rtl::OUString aRetFile;
	rtl::OUString aLanguage = m_aLanguage;
	for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
	{
		rtl::OUStringBuffer aStrBuf;
		aStrBuf.append( xPackage->getURL() );
		aStrBuf.append( aSlash );
		aStrBuf.append( aLanguage );
		aStrBuf.append( aSlash );
		aStrBuf.append( aHelpFilesBaseName );
		aStrBuf.appendAscii( ".tree" );

		aRetFile = expandURL( aStrBuf.makeStringAndClear() );
		if( iPass == 0 )
		{
			if( m_xSFA->exists( aRetFile ) )
				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;
		}
	}

	rnFileSize = 0;
	if( m_xSFA->exists( aRetFile ) )
		rnFileSize = m_xSFA->getSize( aRetFile );
	else
		aRetFile = rtl::OUString();

	return aRetFile;
}