diff options
Diffstat (limited to '')
-rw-r--r-- | xmlhelp/source/treeview/tvread.cxx | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/xmlhelp/source/treeview/tvread.cxx b/xmlhelp/source/treeview/tvread.cxx new file mode 100644 index 000000000..1a56533fd --- /dev/null +++ b/xmlhelp/source/treeview/tvread.cxx @@ -0,0 +1,1133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tvread.hxx> +#include <expat.h> +#include <osl/file.hxx> +#include <unotools/configmgr.hxx> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/deployment/thePackageManagerFactory.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp> +#include <i18nlangtag/languagetag.hxx> +#include <unotools/pathoptions.hxx> +#include <memory> + +namespace treeview { + + class TVDom + { + friend class TVChildTarget; + friend class TVRead; + + public: + + explicit TVDom( TVDom* arent = nullptr ) + : kind( Kind::other ), + parent( arent ), + children( 0 ) + { + } + + TVDom* newChild() + { + children.emplace_back( new TVDom( this ) ); + return children.back().get(); + } + + void newChild(std::unique_ptr<TVDom> p) + { + children.emplace_back(std::move(p)); + children.back()->parent = this; + } + + TVDom* getParent() const + { + if( parent ) + return parent; + else + return const_cast<TVDom*>(this); // I am my own parent, if I am the root + } + + enum class Kind { + tree_node, + tree_leaf, + other + }; + + bool isLeaf() const { return kind == TVDom::Kind::tree_leaf; } + void setKind( Kind ind ) { kind = ind; } + + void setApplication( const char* appl ) + { + application = OUString( appl, + strlen( appl ), + RTL_TEXTENCODING_UTF8 ); + } + + void setTitle( const char* itle ) + { + title += OUString( itle, + strlen( itle ), + RTL_TEXTENCODING_UTF8 ); + } + + void setTitle( const XML_Char* itle,int len ) + { + title += OUString( itle, + len, + RTL_TEXTENCODING_UTF8 ); + } + + void setId( const char* d ) + { + id = OUString( d, + strlen( d ), + RTL_TEXTENCODING_UTF8 ); + } + + void setAnchor( const char* nchor ) + { + anchor = OUString( nchor, + strlen( nchor ), + RTL_TEXTENCODING_UTF8 ); + } + + OUString const & getTargetURL() + { + if( targetURL.isEmpty() ) + { + targetURL = "vnd.sun.star.help://" + id; + } + + return targetURL; + } + + private: + + Kind kind; + OUString application; + OUString title; + OUString id; + OUString anchor; + OUString targetURL; + + TVDom *parent; + std::vector< std::unique_ptr<TVDom> > children; + }; + +} + +using namespace treeview; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::lang; +using namespace com::sun::star::util; +using namespace com::sun::star::container; +using namespace com::sun::star::deployment; + +const char prodName[] = "%PRODUCTNAME"; +const char vendName[] = "%VENDORNAME"; +const char vendVersion[] = "%VENDORVERSION"; +const char vendShort[] = "%VENDORSHORT"; +const char prodVersion[] = "%PRODUCTVERSION"; + +ConfigData::ConfigData() +{ +} + +void ConfigData::replaceName( OUString& oustring ) const +{ + sal_Int32 idx = -1,k = 0,off; + bool cap = false; + OUStringBuffer aStrBuf( 0 ); + + while( ( idx = oustring.indexOf( '%', ++idx ) ) != -1 ) + { + if( oustring.indexOf( prodName,idx ) == idx ) + off = PRODUCTNAME; + else if( oustring.indexOf( prodVersion,idx ) == idx ) + off = PRODUCTVERSION; + else if( oustring.indexOf( vendName,idx ) == idx ) + off = VENDORNAME; + else if( oustring.indexOf( vendVersion,idx ) == idx ) + off = VENDORVERSION; + else if( oustring.indexOf( vendShort,idx ) == idx ) + off = VENDORSHORT; + else + off = -1; + + if( off != -1 ) + { + if( ! cap ) + { + cap = true; + aStrBuf.ensureCapacity( 256 ); + } + + aStrBuf.append( &oustring.getStr()[k],idx - k ); + aStrBuf.append( m_vReplacement[off] ); + k = idx + m_vAdd[off]; + } + } + + if( cap ) + { + if( k < oustring.getLength() ) + aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k ); + oustring = aStrBuf.makeStringAndClear(); + } +} + +// TVRead + +TVRead::TVRead( const ConfigData& configData,TVDom* tvDom ) +{ + if( ! tvDom ) + return; + + Title = tvDom->title; + configData.replaceName( Title ); + if( tvDom->isLeaf() ) + { + TargetURL = tvDom->getTargetURL() + configData.appendix; + if( !tvDom->anchor.isEmpty() ) + TargetURL += "#" + tvDom->anchor; + } + else + Children = new TVChildTarget( configData,tvDom ); +} + +TVRead::~TVRead() +{ +} + +// XNameAccess + +Any SAL_CALL +TVRead::getByName( const OUString& aName ) +{ + bool found( true ); + Any aAny; + if( aName == "Title" ) + aAny <<= Title; + else if( aName == "TargetURL" ) + aAny <<= TargetURL; + else if( aName == "Children" ) + { + cppu::OWeakObject* p = Children.get(); + aAny <<= Reference< XInterface >( p ); + } + else + found = false; + + if( found ) + return aAny; + + throw NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL +TVRead::getElementNames( ) +{ + return { "Title", "TargetURL", "Children" }; +} + +sal_Bool SAL_CALL +TVRead::hasByName( const OUString& aName ) +{ + if( aName == "Title" || + aName == "TargetURL" || + aName == "Children" ) + return true; + + return false; +} + +// XHierarchicalNameAccess + +Any SAL_CALL +TVRead::getByHierarchicalName( const OUString& aName ) +{ + OUString aRest; + if( aName.startsWith("Children/", &aRest) ) + return Children->getByHierarchicalName( aRest ); + + return getByName( aName ); +} + +sal_Bool SAL_CALL +TVRead::hasByHierarchicalName( const OUString& aName ) +{ + OUString aRest; + if( aName.startsWith("Children/", &aRest) ) + return Children->hasByHierarchicalName( aRest ); + + return hasByName( aName ); +} + +/**************************************************************************/ +/* */ +/* TVChildTarget */ +/* */ +/**************************************************************************/ + +extern "C" { + +static void start_handler(void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + TVDom::Kind kind; + + if( strcmp( name,"help_section" ) == 0 || + strcmp( name,"node" ) == 0 ) + kind = TVDom::Kind::tree_node; + else if( strcmp( name,"topic" ) == 0 ) + kind = TVDom::Kind::tree_leaf; + else + return; + + TVDom **tvDom = static_cast< TVDom** >( userData ); + TVDom *p; + p = *tvDom; + + *tvDom = p->newChild(); + p = *tvDom; + + p->setKind( kind ); + while( *atts ) + { + if( strcmp( *atts,"application" ) == 0 ) + p->setApplication( *(atts+1) ); + else if( strcmp( *atts,"title" ) == 0 ) + p->setTitle( *(atts+1) ); + else if( strcmp( *atts,"id" ) == 0 ) + p->setId( *(atts+1) ); + else if( strcmp( *atts,"anchor" ) == 0 ) + p->setAnchor( *(atts+1) ); + + atts+=2; + } +} + +static void end_handler(void *userData, + SAL_UNUSED_PARAMETER const XML_Char * ) +{ + TVDom **tvDom = static_cast< TVDom** >( userData ); + *tvDom = (*tvDom)->getParent(); +} + +static void data_handler( void *userData, + const XML_Char *s, + int len) +{ + TVDom **tvDom = static_cast< TVDom** >( userData ); + if( (*tvDom)->isLeaf() ) + (*tvDom)->setTitle( s,len ); +} + +} + +TVChildTarget::TVChildTarget( const ConfigData& configData,TVDom* tvDom ) +{ + Elements.resize( tvDom->children.size() ); + for( size_t i = 0; i < Elements.size(); ++i ) + Elements[i] = new TVRead( configData,tvDom->children[i].get() ); +} + +TVChildTarget::TVChildTarget( const Reference< XComponentContext >& xContext ) +{ + ConfigData configData = init( xContext ); + + if( configData.locale.isEmpty() || configData.system.isEmpty() ) + return; + + sal_uInt64 ret,len = 0; + int j = configData.vFileURL.size(); + + TVDom tvDom; + TVDom* pTVDom = &tvDom; + + while( j ) + { + len = configData.vFileLen[--j]; + std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files + osl::File aFile( configData.vFileURL[j] ); + (void)aFile.open( osl_File_OpenFlag_Read ); + aFile.read( s.get(),len,ret ); + aFile.close(); + + XML_Parser parser = XML_ParserCreate( nullptr ); + XML_SetElementHandler( parser, + start_handler, + end_handler ); + XML_SetCharacterDataHandler( parser, + data_handler); + XML_SetUserData( parser,&pTVDom ); // does not return this + + XML_Status const parsed = XML_Parse(parser, s.get(), int(len), j==0); + SAL_WARN_IF(XML_STATUS_ERROR == parsed, "xmlhelp", + "TVChildTarget::TVChildTarget(): Tree file parsing failed"); + + XML_ParserFree( parser ); + + Check(pTVDom); + } + // now TVDom holds the relevant information + + Elements.resize( tvDom.children.size() ); + for( size_t i = 0; i < Elements.size(); ++i ) + Elements[i] = new TVRead( configData,tvDom.children[i].get() ); +} + +TVChildTarget::~TVChildTarget() +{ +} + +void TVChildTarget::Check(TVDom* tvDom) +{ + if (tvDom->children.empty()) + { + return; + } + + unsigned i = 0; + bool h = false; + + while((i<tvDom->children.size()-1) && (!h)) + { + if (((tvDom->children[i])->application == (tvDom->children[tvDom->children.size()-1])->application) && + ((tvDom->children[i])->id == (tvDom->children[tvDom->children.size()-1])->id)) + { + TVDom* p = tvDom->children.back().get(); + + for(auto & k : p->children) + { + std::unique_ptr<TVDom> tmp(SearchAndInsert(std::move(k), tvDom->children[i].get())); + if (tmp) + { + tvDom->children[i]->newChild(std::move(tmp)); + } + } + + tvDom->children.pop_back(); + h = true; + } + ++i; + } +} + +std::unique_ptr<TVDom> +TVChildTarget::SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom) +{ + if (p->isLeaf()) return p; + + bool h = false; + sal_Int32 max = 0; + + std::vector< std::unique_ptr<TVDom> >::iterator max_It, i; + max_It = tvDom->children.begin(); + + sal_Int32 c_int; + sal_Int32 p_int = p->id.toInt32(); + + for(i = tvDom->children.begin(); i!=tvDom->children.end(); ++i) + if (!((*i)->isLeaf()) && + ((*i)->id.getLength() == p->id.getLength()) && + (p->id.replaceAt((*i)->parent->id.getLength(), p->id.getLength()-(*i)->parent->id.getLength(), u"") == (*i)->parent->id)) //prefix check + { + h = true; + c_int = (*i)->id.toInt32(); + + if (p_int==c_int) + { + (*(tvDom->children.insert(i+1, std::move(p))))->parent = tvDom; + return nullptr; + } + else if(c_int>max && c_int < p_int) + { + max = c_int; + max_It = i+1; + } + } + if (h) + { + (*(tvDom->children.insert(max_It, std::move(p))))->parent = tvDom; + return nullptr; + } + else + { + for (auto& child : tvDom->children) + { + p = SearchAndInsert(std::move(p), child.get()); + if (p == nullptr) + break; + } + return p; + } +} + +Any SAL_CALL +TVChildTarget::getByName( const OUString& aName ) +{ + std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) ); + sal_Int32 idx = o3tl::toInt32(num) - 1; + if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) ) + throw NoSuchElementException(); + + cppu::OWeakObject* p = Elements[idx].get(); + return Any( Reference< XInterface >( p ) ); +} + +Sequence< OUString > SAL_CALL +TVChildTarget::getElementNames( ) +{ + Sequence< OUString > seq( Elements.size() ); + auto seqRange = asNonConstRange(seq); + for( size_t i = 0; i < Elements.size(); ++i ) + seqRange[i] = OUString::number( 1+i ); + + return seq; +} + +sal_Bool SAL_CALL +TVChildTarget::hasByName( const OUString& aName ) +{ + std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) ); + sal_Int32 idx = o3tl::toInt32(num) - 1; + if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) ) + return false; + + return true; +} + +// XHierarchicalNameAccess + +Any SAL_CALL +TVChildTarget::getByHierarchicalName( const OUString& aName ) +{ + sal_Int32 idx; + + if( ( idx = aName.indexOf( '/' ) ) != -1 ) + { + std::u16string_view num( aName.subView( 2, idx-4 ) ); + sal_Int32 pref = o3tl::toInt32(num) - 1; + + if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) ) + throw NoSuchElementException(); + + return Elements[pref]->getByHierarchicalName( aName.copy( 1 + idx ) ); + } + else + return getByName( aName ); +} + +sal_Bool SAL_CALL +TVChildTarget::hasByHierarchicalName( const OUString& aName ) +{ + sal_Int32 idx; + + if( ( idx = aName.indexOf( '/' ) ) != -1 ) + { + std::u16string_view num( aName.subView( 2, idx-4 ) ); + sal_Int32 pref = o3tl::toInt32(num) - 1; + if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) ) + return false; + + return Elements[pref]->hasByHierarchicalName( aName.copy( 1 + idx ) ); + } + else + return hasByName( aName ); +} + +ConfigData TVChildTarget::init( const Reference< XComponentContext >& xContext ) +{ + ConfigData configData; + Reference< XMultiServiceFactory > sProvider( getConfiguration(xContext) ); + + /**********************************************************************/ + /* reading Office.Common */ + /**********************************************************************/ + + Reference< XHierarchicalNameAccess > xHierAccess( getHierAccess( sProvider, + "org.openoffice.Office.Common" ) ); + OUString system( getKey( xHierAccess,"Help/System" ) ); + bool showBasic( getBooleanKey(xHierAccess,"Help/ShowBasic") ); + OUString instPath( getKey( xHierAccess,"Path/Current/Help" ) ); + if( instPath.isEmpty() ) + // try to determine path from default + instPath = "$(instpath)/help"; + + // replace anything like $(instpath); + subst( instPath ); + + /**********************************************************************/ + /* reading setup */ + /**********************************************************************/ + + xHierAccess = getHierAccess( sProvider, + "org.openoffice.Setup" ); + + OUString setupversion( getKey( xHierAccess,"Product/ooSetupVersion" ) ); + OUString setupextension; + + try + { + Reference< lang::XMultiServiceFactory > xConfigProvider = theDefaultProvider::get( xContext ); + + uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Setup/Product"))} + })); + + // open it + uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + lParams) ); + + uno::Reference< container::XNameAccess > xDirectAccess(xCFG, uno::UNO_QUERY); + uno::Any aRet = xDirectAccess->getByName("ooSetupExtension"); + + aRet >>= setupextension; + } + catch ( uno::Exception& ) + { + } + + OUString productVersion( setupversion + " " + setupextension ); + OUString locale( getKey( xHierAccess,"L10N/ooLocale" ) ); + + // Determine fileurl from url and locale + OUString url; + osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url ); + if( errFile != osl::FileBase::E_None ) return configData; + if( !url.endsWith("/") ) + url += "/"; + OUString ret; + sal_Int32 idx; + osl::DirectoryItem aDirItem; + if( osl::FileBase::E_None == osl::DirectoryItem::get( url + locale,aDirItem ) ) + ret = locale; + else if( ( ( idx = locale.indexOf( '-' ) ) != -1 || + ( idx = locale.indexOf( '_' ) ) != -1 ) && + osl::FileBase::E_None == osl::DirectoryItem::get( url + locale.subView( 0,idx ), + aDirItem ) ) + ret = locale.copy( 0,idx ); + else + { + locale = "en-US"; + ret = "en"; + } + url += ret; + + // first of all, try do determine whether there are any *.tree files present + + // Start with extensions to set them at the end of the list + TreeFileIterator aTreeIt( locale ); + OUString aTreeFile; + sal_Int32 nFileSize; + for (;;) + { + aTreeFile = aTreeIt.nextTreeFile( nFileSize ); + if( aTreeFile.isEmpty() ) + break; + configData.vFileLen.push_back( nFileSize ); + configData.vFileURL.push_back( aTreeFile ); + } + + osl::Directory aDirectory( url ); + osl::FileStatus aFileStatus( + osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_FileURL ); + if( osl::Directory::E_None == aDirectory.open() ) + { + OUString aFileUrl, aFileName; + while( aDirectory.getNextItem( aDirItem ) == osl::FileBase::E_None && + aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None && + aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) && + aFileStatus.isValid( osl_FileStatus_Mask_FileName ) ) + { + aFileUrl = aFileStatus.getFileURL(); + aFileName = aFileStatus.getFileName(); + int idx_ = aFileName.lastIndexOf( '.' ); + if( idx_ == -1 ) + continue; + + const sal_Unicode* str = aFileName.getStr(); + + if( aFileName.getLength() == idx_ + 5 && + ( str[idx_ + 1] == 't' || str[idx_ + 1] == 'T' ) && + ( str[idx_ + 2] == 'r' || str[idx_ + 2] == 'R' ) && + ( str[idx_ + 3] == 'e' || str[idx_ + 3] == 'E' ) && + ( str[idx_ + 4] == 'e' || str[idx_ + 4] == 'E' ) ) + { + OUString baseName = aFileName.copy(0,idx_).toAsciiLowerCase(); + if(! showBasic && baseName == "sbasic" ) + continue; + osl::File aFile( aFileUrl ); + if( osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) ) + { + // use the file size, not aFileStatus size, in case the + // tree file is a symlink + sal_uInt64 nSize; + aFile.getSize( nSize ); + configData.vFileLen.push_back( nSize ); + configData.vFileURL.push_back( aFileUrl ); + aFile.close(); + } + } + } + aDirectory.close(); + } + + configData.m_vAdd[0] = 12; + configData.m_vAdd[1] = 15; + configData.m_vAdd[2] = 11; + configData.m_vAdd[3] = 14; + configData.m_vAdd[4] = 12; + configData.m_vReplacement[0] = utl::ConfigManager::getProductName(); + configData.m_vReplacement[1] = productVersion; + // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings + + configData.system = system; + configData.locale = locale; + configData.appendix = + "?Language=" + + configData.locale + + "&System=" + + configData.system + + "&UseDB=no"; + + return configData; +} + +Reference< XMultiServiceFactory > +TVChildTarget::getConfiguration(const Reference< XComponentContext >& rxContext) +{ + Reference< XMultiServiceFactory > xProvider; + if( rxContext.is() ) + { + try + { + xProvider = theDefaultProvider::get( rxContext ); + } + catch( const css::uno::Exception& ) + { + OSL_ENSURE( xProvider.is(),"can not instantiate configuration" ); + } + } + + return xProvider; +} + +Reference< XHierarchicalNameAccess > +TVChildTarget::getHierAccess( const Reference< XMultiServiceFactory >& sProvider, + const char* file ) +{ + Reference< XHierarchicalNameAccess > xHierAccess; + + if( sProvider.is() ) + { + try + { + xHierAccess = + Reference< XHierarchicalNameAccess > + ( sProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", { Any(OUString::createFromAscii(file)) }), + UNO_QUERY ); + } + catch( const css::uno::Exception& ) + { + } + } + + return xHierAccess; +} + +OUString +TVChildTarget::getKey( const Reference< XHierarchicalNameAccess >& xHierAccess, + const char* key ) +{ + OUString instPath; + if( xHierAccess.is() ) + { + Any aAny; + try + { + aAny = + xHierAccess->getByHierarchicalName( OUString::createFromAscii( key ) ); + } + catch( const css::container::NoSuchElementException& ) + { + } + aAny >>= instPath; + } + return instPath; +} + +bool +TVChildTarget::getBooleanKey(const Reference< + XHierarchicalNameAccess >& xHierAccess, + const char* key) +{ + bool ret = false; + if( xHierAccess.is() ) + { + Any aAny; + try + { + aAny = + xHierAccess->getByHierarchicalName( + OUString::createFromAscii(key)); + } + catch( const css::container::NoSuchElementException& ) + { + } + aAny >>= ret; + } + return ret; +} + +void TVChildTarget::subst( OUString& instpath ) +{ + SvtPathOptions aOptions; + instpath = aOptions.SubstituteVariable( instpath ); +} + + +const char aHelpMediaType[] = "application/vnd.sun.star.help"; + +TreeFileIterator::TreeFileIterator( const OUString& aLanguage ) + : m_eState( IteratorState::UserExtensions ) + , m_aLanguage( aLanguage ) +{ + m_xContext = ::comphelper::getProcessComponentContext(); + if( !m_xContext.is() ) + { + throw RuntimeException( "TreeFileIterator::TreeFileIterator(), no XComponentContext" ); + } + + m_xSFA = ucb::SimpleFileAccess::create(m_xContext); + + m_bUserPackagesLoaded = false; + m_bSharedPackagesLoaded = false; + m_bBundledPackagesLoaded = false; + m_iUserPackage = 0; + m_iSharedPackage = 0; + m_iBundledPackage = 0; +} + +Reference< deployment::XPackage > TreeFileIterator::implGetHelpPackageFromPackage + ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle ) +{ + o_xParentPackageBundle.clear(); + + Reference< deployment::XPackage > xHelpPackage; + if( !xPackage.is() ) + return xHelpPackage; + + // Check if parent package is registered + beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered + ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) ); + bool bRegistered = false; + if( option.IsPresent ) + { + beans::Ambiguous<sal_Bool> const & reg = option.Value; + if( !reg.IsAmbiguous && reg.Value ) + bRegistered = true; + } + if( !bRegistered ) + return xHelpPackage; + + if( xPackage->isBundle() ) + { + const Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle + ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ); + auto pSubPkg = std::find_if(aPkgSeq.begin(), aPkgSeq.end(), + [](const Reference< deployment::XPackage >& xSubPkg) { + const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType(); + OUString aMediaType = xPackageTypeInfo->getMediaType(); + return aMediaType == aHelpMediaType; + }); + if (pSubPkg != aPkgSeq.end()) + { + xHelpPackage = *pSubPkg; + o_xParentPackageBundle = xPackage; + } + } + else + { + const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType(); + OUString aMediaType = xPackageTypeInfo->getMediaType(); + if( aMediaType == aHelpMediaType ) + xHelpPackage = xPackage; + } + + return xHelpPackage; +} + +Reference< deployment::XPackage > TreeFileIterator::implGetNextUserHelpPackage + ( Reference< deployment::XPackage >& o_xParentPackageBundle ) +{ + Reference< deployment::XPackage > xHelpPackage; + + if( !m_bUserPackagesLoaded ) + { + Reference< XPackageManager > xUserManager = + thePackageManagerFactory::get( m_xContext )->getPackageManager("user"); + m_aUserPackagesSeq = xUserManager->getDeployedPackages + ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); + + m_bUserPackagesLoaded = true; + } + + if( m_iUserPackage == m_aUserPackagesSeq.getLength() ) + { + m_eState = IteratorState::SharedExtensions; // Later: SHARED_MODULE + } + else + { + const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ]; + OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextUserHelpPackage(): Invalid package" ); + xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); + } + + return xHelpPackage; +} + +Reference< deployment::XPackage > TreeFileIterator::implGetNextSharedHelpPackage + ( Reference< deployment::XPackage >& o_xParentPackageBundle ) +{ + Reference< deployment::XPackage > xHelpPackage; + + if( !m_bSharedPackagesLoaded ) + { + Reference< XPackageManager > xSharedManager = + thePackageManagerFactory::get( m_xContext )->getPackageManager("shared"); + m_aSharedPackagesSeq = xSharedManager->getDeployedPackages + ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); + + m_bSharedPackagesLoaded = true; + } + + if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() ) + { + m_eState = IteratorState::BundledExtensions; + } + else + { + const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ]; + OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextSharedHelpPackage(): Invalid package" ); + xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); + } + + return xHelpPackage; +} + +Reference< deployment::XPackage > TreeFileIterator::implGetNextBundledHelpPackage + ( Reference< deployment::XPackage >& o_xParentPackageBundle ) +{ + Reference< deployment::XPackage > xHelpPackage; + + if( !m_bBundledPackagesLoaded ) + { + Reference< XPackageManager > xBundledManager = + thePackageManagerFactory::get( m_xContext )->getPackageManager("bundled"); + m_aBundledPackagesSeq = xBundledManager->getDeployedPackages + ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); + + m_bBundledPackagesLoaded = true; + } + + if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() ) + { + m_eState = IteratorState::EndReached; + } + else + { + const Reference< deployment::XPackage >* pBundledPackages = m_aBundledPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ]; + OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextBundledHelpPackage(): Invalid package" ); + xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); + } + + return xHelpPackage; +} + +static bool isLetter( sal_Unicode c ) +{ + return rtl::isAsciiAlpha(c); +} + +void TreeFileIterator::implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv, + const css::uno::Reference< css::deployment::XPackage >& xPackage ) +{ + rv.clear(); + OUString aExtensionPath = xPackage->getURL(); + const Sequence< OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true ); + + for( const OUString& aEntry : aEntrySeq ) + { + if( m_xSFA->isFolder( aEntry ) ) + { + sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' ); + if( nLastSlash != -1 ) + { + OUString aPureEntry = aEntry.copy( nLastSlash + 1 ); + + // Check language scheme + int nLen = aPureEntry.getLength(); + const sal_Unicode* pc = aPureEntry.getStr(); + bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) ); + bool bIsLanguage = bStartCanBeLanguage && + ( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) ); + if( bIsLanguage ) + rv.push_back( aPureEntry ); + } + } + } +} + + +OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize ) +{ + OUString aRetFile; + + while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached ) + { + switch( m_eState ) + { + case IteratorState::UserExtensions: + { + Reference< deployment::XPackage > xParentPackageBundle; + Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage ); + break; + } + + case IteratorState::SharedExtensions: + { + Reference< deployment::XPackage > xParentPackageBundle; + Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage ); + break; + } + case IteratorState::BundledExtensions: + { + Reference< deployment::XPackage > xParentPackageBundle; + Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle ); + if( !xHelpPackage.is() ) + break; + + aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage ); + break; + } + + case IteratorState::EndReached: + OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" ); + break; + } + } + + return aRetFile; +} + +OUString TreeFileIterator::expandURL( const OUString& aURL ) +{ + static Reference< util::XMacroExpander > xMacroExpander; + static Reference< uri::XUriReferenceFactory > xFac; + + std::scoped_lock aGuard( m_aMutex ); + + if( !xMacroExpander.is() || !xFac.is() ) + { + xFac = uri::UriReferenceFactory::create( m_xContext ); + + xMacroExpander = util::theMacroExpander::get(m_xContext); + } + + OUString aRetURL = aURL; + Reference< uri::XUriReference > uriRef; + for (;;) + { + uriRef = xFac->parse( aRetURL ); + if ( uriRef.is() ) + { + Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY ); + if( !sxUri.is() ) + break; + + aRetURL = sxUri->expand( xMacroExpander ); + } + } + return aRetURL; +} + +OUString TreeFileIterator::implGetTreeFileFromPackage + ( sal_Int32& rnFileSize, const Reference< deployment::XPackage >& xPackage ) +{ + OUString aRetFile; + OUString aLanguage = m_aLanguage; + for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass ) + { + aRetFile = expandURL( xPackage->getURL() + "/" + aLanguage + "/help.tree" ); + if( iPass == 0 ) + { + if( m_xSFA->exists( aRetFile ) ) + break; + + ::std::vector< OUString > av; + implGetLanguageVectorFromPackage( av, xPackage ); + ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage ); + if( pFound != av.end() ) + aLanguage = *pFound; + } + } + + rnFileSize = 0; + if( m_xSFA->exists( aRetFile ) ) + rnFileSize = m_xSFA->getSize( aRetFile ); + else + aRetFile.clear(); + + return aRetFile; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |