1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <rtl/character.hxx>
22 #include <rtl/ustrbuf.hxx>
23 #include <sal/log.hxx>
24 #include <o3tl/safeint.hxx>
25 #include <o3tl/string_view.hxx>
26 #include <osl/diagnose.h>
29 #include <osl/file.hxx>
30 #include <unotools/configmgr.hxx>
31 #include <com/sun/star/configuration/theDefaultProvider.hpp>
32 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/propertysequence.hxx>
36 #include <com/sun/star/deployment/thePackageManagerFactory.hpp>
37 #include <com/sun/star/util/theMacroExpander.hpp>
38 #include <com/sun/star/uri/UriReferenceFactory.hpp>
39 #include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <unotools/pathoptions.hxx>
42 #include <officecfg/Office/Common.hxx>
43 #include <officecfg/Setup.hxx>
51 friend class TVChildTarget
;
56 explicit TVDom( TVDom
* arent
= nullptr )
57 : kind( Kind::other
),
65 children
.emplace_back( new TVDom( this ) );
66 return children
.back().get();
69 void newChild(std::unique_ptr
<TVDom
> p
)
71 children
.emplace_back(std::move(p
));
72 children
.back()->parent
= this;
75 TVDom
* getParent() const
80 return const_cast<TVDom
*>(this); // I am my own parent, if I am the root
89 bool isLeaf() const { return kind
== TVDom::Kind::tree_leaf
; }
90 void setKind( Kind ind
) { kind
= ind
; }
92 void setApplication( const char* appl
)
94 application
= OUString( appl
,
96 RTL_TEXTENCODING_UTF8
);
99 void setTitle( const char* itle
)
101 title
+= OUString( itle
,
103 RTL_TEXTENCODING_UTF8
);
106 void setTitle( const XML_Char
* itle
,int len
)
108 title
+= OUString( itle
,
110 RTL_TEXTENCODING_UTF8
);
113 void setId( const char* d
)
117 RTL_TEXTENCODING_UTF8
);
120 void setAnchor( const char* nchor
)
122 anchor
= OUString( nchor
,
124 RTL_TEXTENCODING_UTF8
);
127 OUString
const & getTargetURL()
129 if( targetURL
.isEmpty() )
131 targetURL
= "vnd.sun.star.help://" + id
;
140 OUString application
;
147 std::vector
< std::unique_ptr
<TVDom
> > children
;
152 using namespace treeview
;
153 using namespace com::sun::star
;
154 using namespace com::sun::star::uno
;
155 using namespace com::sun::star::beans
;
156 using namespace com::sun::star::configuration
;
157 using namespace com::sun::star::lang
;
158 using namespace com::sun::star::util
;
159 using namespace com::sun::star::container
;
160 using namespace com::sun::star::deployment
;
162 constexpr OUStringLiteral prodName
= u
"%PRODUCTNAME";
163 constexpr OUStringLiteral vendName
= u
"%VENDORNAME";
164 constexpr OUStringLiteral vendVersion
= u
"%VENDORVERSION";
165 constexpr OUStringLiteral vendShort
= u
"%VENDORSHORT";
166 constexpr OUStringLiteral prodVersion
= u
"%PRODUCTVERSION";
168 ConfigData::ConfigData()
172 void ConfigData::replaceName( OUString
& oustring
) const
174 sal_Int32 idx
= -1,k
= 0,off
;
176 OUStringBuffer
aStrBuf( 0 );
178 while( ( idx
= oustring
.indexOf( '%', ++idx
) ) != -1 )
180 if( oustring
.indexOf( prodName
,idx
) == idx
)
182 else if( oustring
.indexOf( prodVersion
,idx
) == idx
)
183 off
= PRODUCTVERSION
;
184 else if( oustring
.indexOf( vendName
,idx
) == idx
)
186 else if( oustring
.indexOf( vendVersion
,idx
) == idx
)
188 else if( oustring
.indexOf( vendShort
,idx
) == idx
)
198 aStrBuf
.ensureCapacity( 256 );
201 aStrBuf
.append( &oustring
.getStr()[k
],idx
- k
);
202 aStrBuf
.append( m_vReplacement
[off
] );
203 k
= idx
+ m_vAdd
[off
];
209 if( k
< oustring
.getLength() )
210 aStrBuf
.append( &oustring
.getStr()[k
],oustring
.getLength()-k
);
211 oustring
= aStrBuf
.makeStringAndClear();
217 TVRead::TVRead( const ConfigData
& configData
,TVDom
* tvDom
)
222 Title
= tvDom
->title
;
223 configData
.replaceName( Title
);
224 if( tvDom
->isLeaf() )
226 TargetURL
= tvDom
->getTargetURL() + configData
.appendix
;
227 if( !tvDom
->anchor
.isEmpty() )
228 TargetURL
+= "#" + tvDom
->anchor
;
231 Children
= new TVChildTarget( configData
,tvDom
);
241 TVRead::getByName( const OUString
& aName
)
245 if( aName
== "Title" )
247 else if( aName
== "TargetURL" )
249 else if( aName
== "Children" )
251 cppu::OWeakObject
* p
= Children
.get();
252 aAny
<<= Reference
< XInterface
>( p
);
260 throw NoSuchElementException();
263 Sequence
< OUString
> SAL_CALL
264 TVRead::getElementNames( )
266 return { u
"Title"_ustr
, u
"TargetURL"_ustr
, u
"Children"_ustr
};
270 TVRead::hasByName( const OUString
& aName
)
272 if( aName
== "Title" ||
273 aName
== "TargetURL" ||
274 aName
== "Children" )
280 // XHierarchicalNameAccess
283 TVRead::getByHierarchicalName( const OUString
& aName
)
286 if( aName
.startsWith("Children/", &aRest
) )
287 return Children
->getByHierarchicalName( aRest
);
289 return getByName( aName
);
293 TVRead::hasByHierarchicalName( const OUString
& aName
)
296 if( aName
.startsWith("Children/", &aRest
) )
297 return Children
->hasByHierarchicalName( aRest
);
299 return hasByName( aName
);
302 /**************************************************************************/
306 /**************************************************************************/
310 static void start_handler(void *userData
,
311 const XML_Char
*name
,
312 const XML_Char
**atts
)
316 if( strcmp( name
,"help_section" ) == 0 ||
317 strcmp( name
,"node" ) == 0 )
318 kind
= TVDom::Kind::tree_node
;
319 else if( strcmp( name
,"topic" ) == 0 )
320 kind
= TVDom::Kind::tree_leaf
;
324 TVDom
**tvDom
= static_cast< TVDom
** >( userData
);
328 *tvDom
= p
->newChild();
334 if( strcmp( *atts
,"application" ) == 0 )
335 p
->setApplication( *(atts
+1) );
336 else if( strcmp( *atts
,"title" ) == 0 )
337 p
->setTitle( *(atts
+1) );
338 else if( strcmp( *atts
,"id" ) == 0 )
339 p
->setId( *(atts
+1) );
340 else if( strcmp( *atts
,"anchor" ) == 0 )
341 p
->setAnchor( *(atts
+1) );
347 static void end_handler(void *userData
,
348 SAL_UNUSED_PARAMETER
const XML_Char
* )
350 TVDom
**tvDom
= static_cast< TVDom
** >( userData
);
351 *tvDom
= (*tvDom
)->getParent();
354 static void data_handler( void *userData
,
358 TVDom
**tvDom
= static_cast< TVDom
** >( userData
);
359 if( (*tvDom
)->isLeaf() )
360 (*tvDom
)->setTitle( s
,len
);
365 TVChildTarget::TVChildTarget( const ConfigData
& configData
,TVDom
* tvDom
)
367 Elements
.resize( tvDom
->children
.size() );
368 for( size_t i
= 0; i
< Elements
.size(); ++i
)
369 Elements
[i
] = new TVRead( configData
,tvDom
->children
[i
].get() );
372 TVChildTarget::TVChildTarget()
374 ConfigData configData
= init();
376 if( configData
.locale
.isEmpty() || configData
.system
.isEmpty() )
379 sal_uInt64 ret
,len
= 0;
380 int j
= configData
.vFileURL
.size();
383 TVDom
* pTVDom
= &tvDom
;
387 len
= configData
.vFileLen
[--j
];
388 std::unique_ptr
<char[]> s(new char[ int(len
) ]); // the buffer to hold the installed files
389 osl::File
aFile( configData
.vFileURL
[j
] );
390 (void)aFile
.open( osl_File_OpenFlag_Read
);
391 aFile
.read( s
.get(),len
,ret
);
394 XML_Parser parser
= XML_ParserCreate( nullptr );
395 XML_SetElementHandler( parser
,
398 XML_SetCharacterDataHandler( parser
,
400 XML_SetUserData( parser
,&pTVDom
); // does not return this
402 XML_Status
const parsed
= XML_Parse(parser
, s
.get(), int(len
), j
==0);
403 SAL_WARN_IF(XML_STATUS_ERROR
== parsed
, "xmlhelp",
404 "TVChildTarget::TVChildTarget(): Tree file parsing failed");
406 XML_ParserFree( parser
);
410 // now TVDom holds the relevant information
412 Elements
.resize( tvDom
.children
.size() );
413 for( size_t i
= 0; i
< Elements
.size(); ++i
)
414 Elements
[i
] = new TVRead( configData
,tvDom
.children
[i
].get() );
417 TVChildTarget::~TVChildTarget()
421 void TVChildTarget::Check(TVDom
* tvDom
)
423 if (tvDom
->children
.empty())
431 while((i
<tvDom
->children
.size()-1) && (!h
))
433 if (((tvDom
->children
[i
])->application
== (tvDom
->children
[tvDom
->children
.size()-1])->application
) &&
434 ((tvDom
->children
[i
])->id
== (tvDom
->children
[tvDom
->children
.size()-1])->id
))
436 TVDom
* p
= tvDom
->children
.back().get();
438 for(auto & k
: p
->children
)
440 std::unique_ptr
<TVDom
> tmp(SearchAndInsert(std::move(k
), tvDom
->children
[i
].get()));
443 tvDom
->children
[i
]->newChild(std::move(tmp
));
447 tvDom
->children
.pop_back();
454 std::unique_ptr
<TVDom
>
455 TVChildTarget::SearchAndInsert(std::unique_ptr
<TVDom
> p
, TVDom
* tvDom
)
457 if (p
->isLeaf()) return p
;
462 std::vector
< std::unique_ptr
<TVDom
> >::iterator max_It
, i
;
463 max_It
= tvDom
->children
.begin();
466 sal_Int32 p_int
= p
->id
.toInt32();
468 for(i
= tvDom
->children
.begin(); i
!=tvDom
->children
.end(); ++i
)
469 if (!((*i
)->isLeaf()) &&
470 ((*i
)->id
.getLength() == p
->id
.getLength()) &&
471 (p
->id
.replaceAt((*i
)->parent
->id
.getLength(), p
->id
.getLength()-(*i
)->parent
->id
.getLength(), u
"") == (*i
)->parent
->id
)) //prefix check
474 c_int
= (*i
)->id
.toInt32();
478 (*(tvDom
->children
.insert(i
+1, std::move(p
))))->parent
= tvDom
;
481 else if(c_int
>max
&& c_int
< p_int
)
489 (*(tvDom
->children
.insert(max_It
, std::move(p
))))->parent
= tvDom
;
494 for (auto& child
: tvDom
->children
)
496 p
= SearchAndInsert(std::move(p
), child
.get());
505 TVChildTarget::getByName( const OUString
& aName
)
507 std::u16string_view
num( aName
.subView( 2, aName
.getLength()-4 ) );
508 sal_Int32 idx
= o3tl::toInt32(num
) - 1;
509 if( idx
< 0 || Elements
.size() <= o3tl::make_unsigned( idx
) )
510 throw NoSuchElementException();
512 cppu::OWeakObject
* p
= Elements
[idx
].get();
513 return Any( Reference
< XInterface
>( p
) );
516 Sequence
< OUString
> SAL_CALL
517 TVChildTarget::getElementNames( )
519 Sequence
< OUString
> seq( Elements
.size() );
520 auto seqRange
= asNonConstRange(seq
);
521 for( size_t i
= 0; i
< Elements
.size(); ++i
)
522 seqRange
[i
] = OUString::number( 1+i
);
528 TVChildTarget::hasByName( const OUString
& aName
)
530 std::u16string_view
num( aName
.subView( 2, aName
.getLength()-4 ) );
531 sal_Int32 idx
= o3tl::toInt32(num
) - 1;
532 if( idx
< 0 || Elements
.size() <= o3tl::make_unsigned( idx
) )
538 // XHierarchicalNameAccess
541 TVChildTarget::getByHierarchicalName( const OUString
& aName
)
545 if( ( idx
= aName
.indexOf( '/' ) ) != -1 )
547 std::u16string_view
num( aName
.subView( 2, idx
-4 ) );
548 sal_Int32 pref
= o3tl::toInt32(num
) - 1;
550 if( pref
< 0 || Elements
.size() <= o3tl::make_unsigned( pref
) )
551 throw NoSuchElementException();
553 return Elements
[pref
]->getByHierarchicalName( aName
.copy( 1 + idx
) );
556 return getByName( aName
);
560 TVChildTarget::hasByHierarchicalName( const OUString
& aName
)
564 if( ( idx
= aName
.indexOf( '/' ) ) != -1 )
566 std::u16string_view
num( aName
.subView( 2, idx
-4 ) );
567 sal_Int32 pref
= o3tl::toInt32(num
) - 1;
568 if( pref
< 0 || Elements
.size() <= o3tl::make_unsigned( pref
) )
571 return Elements
[pref
]->hasByHierarchicalName( aName
.copy( 1 + idx
) );
574 return hasByName( aName
);
577 ConfigData
TVChildTarget::init()
579 ConfigData configData
;
581 /**********************************************************************/
582 /* reading Office.Common */
583 /**********************************************************************/
585 configData
.system
= officecfg::Office::Common::Help::System::get();
586 bool showBasic
= officecfg::Office::Common::Help::ShowBasic::get();
587 OUString instPath
= officecfg::Office::Common::Path::Current::Help::get();
588 if( instPath
.isEmpty() )
589 // try to determine path from default
590 instPath
= "$(instpath)/help";
592 // replace anything like $(instpath);
595 /**********************************************************************/
597 /**********************************************************************/
599 OUString setupversion
= officecfg::Setup::Product::ooSetupVersion::get();
600 OUString setupextension
= officecfg::Setup::Product::ooSetupExtension::get();
602 configData
.m_vReplacement
[0] = utl::ConfigManager::getProductName();
603 configData
.m_vReplacement
[1] = setupversion
+ " " + setupextension
; // productVersion
604 // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
606 configData
.locale
= officecfg::Setup::L10N::ooLocale::get();
608 // Determine fileurl from url and locale
610 osl::FileBase::RC errFile
= osl::FileBase::getFileURLFromSystemPath( instPath
,url
);
611 if( errFile
!= osl::FileBase::E_None
) return configData
;
612 if( !url
.endsWith("/") )
616 osl::DirectoryItem aDirItem
;
617 if( osl::FileBase::E_None
== osl::DirectoryItem::get( url
+ configData
.locale
, aDirItem
) )
618 ret
= configData
.locale
;
619 else if( ( ( idx
= configData
.locale
.indexOf( '-' ) ) != -1 ||
620 ( idx
= configData
.locale
.indexOf( '_' ) ) != -1 ) &&
621 osl::FileBase::E_None
== osl::DirectoryItem::get( url
+ configData
.locale
.subView( 0,idx
),
623 ret
= configData
.locale
.copy( 0,idx
);
626 configData
.locale
= "en-US";
631 // first of all, try do determine whether there are any *.tree files present
633 // Start with extensions to set them at the end of the list
634 TreeFileIterator
aTreeIt(configData
.locale
);
639 aTreeFile
= aTreeIt
.nextTreeFile( nFileSize
);
640 if( aTreeFile
.isEmpty() )
642 configData
.vFileLen
.push_back( nFileSize
);
643 configData
.vFileURL
.push_back( aTreeFile
);
646 osl::Directory
aDirectory( url
);
647 osl::FileStatus
aFileStatus(
648 osl_FileStatus_Mask_FileName
| osl_FileStatus_Mask_FileURL
);
649 if( osl::Directory::E_None
== aDirectory
.open() )
651 OUString aFileUrl
, aFileName
;
652 while( aDirectory
.getNextItem( aDirItem
) == osl::FileBase::E_None
&&
653 aDirItem
.getFileStatus( aFileStatus
) == osl::FileBase::E_None
&&
654 aFileStatus
.isValid( osl_FileStatus_Mask_FileURL
) &&
655 aFileStatus
.isValid( osl_FileStatus_Mask_FileName
) )
657 aFileUrl
= aFileStatus
.getFileURL();
658 aFileName
= aFileStatus
.getFileName();
659 int idx_
= aFileName
.lastIndexOf( '.' );
663 const sal_Unicode
* str
= aFileName
.getStr();
665 if( aFileName
.getLength() == idx_
+ 5 &&
666 ( str
[idx_
+ 1] == 't' || str
[idx_
+ 1] == 'T' ) &&
667 ( str
[idx_
+ 2] == 'r' || str
[idx_
+ 2] == 'R' ) &&
668 ( str
[idx_
+ 3] == 'e' || str
[idx_
+ 3] == 'E' ) &&
669 ( str
[idx_
+ 4] == 'e' || str
[idx_
+ 4] == 'E' ) )
671 OUString baseName
= aFileName
.copy(0,idx_
).toAsciiLowerCase();
672 if(! showBasic
&& baseName
== "sbasic" )
674 osl::File
aFile( aFileUrl
);
675 if( osl::FileBase::E_None
== aFile
.open( osl_File_OpenFlag_Read
) )
677 // use the file size, not aFileStatus size, in case the
678 // tree file is a symlink
680 aFile
.getSize( nSize
);
681 configData
.vFileLen
.push_back( nSize
);
682 configData
.vFileURL
.push_back( aFileUrl
);
690 configData
.m_vAdd
[0] = 12;
691 configData
.m_vAdd
[1] = 15;
692 configData
.m_vAdd
[2] = 11;
693 configData
.m_vAdd
[3] = 14;
694 configData
.m_vAdd
[4] = 12;
696 configData
.appendix
=
706 void TVChildTarget::subst( OUString
& instpath
)
708 SvtPathOptions aOptions
;
709 instpath
= aOptions
.SubstituteVariable( instpath
);
713 const char aHelpMediaType
[] = "application/vnd.sun.star.help";
715 TreeFileIterator::TreeFileIterator( OUString aLanguage
)
716 : m_eState( IteratorState::UserExtensions
)
717 , m_aLanguage(std::move( aLanguage
))
719 m_xContext
= ::comphelper::getProcessComponentContext();
720 if( !m_xContext
.is() )
722 throw RuntimeException( u
"TreeFileIterator::TreeFileIterator(), no XComponentContext"_ustr
);
725 m_xSFA
= ucb::SimpleFileAccess::create(m_xContext
);
727 m_bUserPackagesLoaded
= false;
728 m_bSharedPackagesLoaded
= false;
729 m_bBundledPackagesLoaded
= false;
731 m_iSharedPackage
= 0;
732 m_iBundledPackage
= 0;
735 Reference
< deployment::XPackage
> TreeFileIterator::implGetHelpPackageFromPackage
736 ( const Reference
< deployment::XPackage
>& xPackage
, Reference
< deployment::XPackage
>& o_xParentPackageBundle
)
738 o_xParentPackageBundle
.clear();
740 Reference
< deployment::XPackage
> xHelpPackage
;
744 // Check if parent package is registered
745 beans::Optional
< beans::Ambiguous
<sal_Bool
> > option( xPackage
->isRegistered
746 ( Reference
<task::XAbortChannel
>(), Reference
<ucb::XCommandEnvironment
>() ) );
747 bool bRegistered
= false;
748 if( option
.IsPresent
)
750 beans::Ambiguous
<sal_Bool
> const & reg
= option
.Value
;
751 if( !reg
.IsAmbiguous
&& reg
.Value
)
757 if( xPackage
->isBundle() )
759 const Sequence
< Reference
< deployment::XPackage
> > aPkgSeq
= xPackage
->getBundle
760 ( Reference
<task::XAbortChannel
>(), Reference
<ucb::XCommandEnvironment
>() );
761 auto pSubPkg
= std::find_if(aPkgSeq
.begin(), aPkgSeq
.end(),
762 [](const Reference
< deployment::XPackage
>& xSubPkg
) {
763 const Reference
< deployment::XPackageTypeInfo
> xPackageTypeInfo
= xSubPkg
->getPackageType();
764 OUString aMediaType
= xPackageTypeInfo
->getMediaType();
765 return aMediaType
== aHelpMediaType
;
767 if (pSubPkg
!= aPkgSeq
.end())
769 xHelpPackage
= *pSubPkg
;
770 o_xParentPackageBundle
= xPackage
;
775 const Reference
< deployment::XPackageTypeInfo
> xPackageTypeInfo
= xPackage
->getPackageType();
776 OUString aMediaType
= xPackageTypeInfo
->getMediaType();
777 if( aMediaType
== aHelpMediaType
)
778 xHelpPackage
= xPackage
;
784 Reference
< deployment::XPackage
> TreeFileIterator::implGetNextUserHelpPackage
785 ( Reference
< deployment::XPackage
>& o_xParentPackageBundle
)
787 Reference
< deployment::XPackage
> xHelpPackage
;
789 if( !m_bUserPackagesLoaded
)
791 Reference
< XPackageManager
> xUserManager
=
792 thePackageManagerFactory::get( m_xContext
)->getPackageManager(u
"user"_ustr
);
793 m_aUserPackagesSeq
= xUserManager
->getDeployedPackages
794 ( Reference
< task::XAbortChannel
>(), Reference
< ucb::XCommandEnvironment
>() );
796 m_bUserPackagesLoaded
= true;
799 if( m_iUserPackage
== m_aUserPackagesSeq
.getLength() )
801 m_eState
= IteratorState::SharedExtensions
; // Later: SHARED_MODULE
805 const Reference
< deployment::XPackage
>* pUserPackages
= m_aUserPackagesSeq
.getConstArray();
806 Reference
< deployment::XPackage
> xPackage
= pUserPackages
[ m_iUserPackage
++ ];
807 OSL_ENSURE( xPackage
.is(), "TreeFileIterator::implGetNextUserHelpPackage(): Invalid package" );
808 xHelpPackage
= implGetHelpPackageFromPackage( xPackage
, o_xParentPackageBundle
);
814 Reference
< deployment::XPackage
> TreeFileIterator::implGetNextSharedHelpPackage
815 ( Reference
< deployment::XPackage
>& o_xParentPackageBundle
)
817 Reference
< deployment::XPackage
> xHelpPackage
;
819 if( !m_bSharedPackagesLoaded
)
821 Reference
< XPackageManager
> xSharedManager
=
822 thePackageManagerFactory::get( m_xContext
)->getPackageManager(u
"shared"_ustr
);
823 m_aSharedPackagesSeq
= xSharedManager
->getDeployedPackages
824 ( Reference
< task::XAbortChannel
>(), Reference
< ucb::XCommandEnvironment
>() );
826 m_bSharedPackagesLoaded
= true;
829 if( m_iSharedPackage
== m_aSharedPackagesSeq
.getLength() )
831 m_eState
= IteratorState::BundledExtensions
;
835 const Reference
< deployment::XPackage
>* pSharedPackages
= m_aSharedPackagesSeq
.getConstArray();
836 Reference
< deployment::XPackage
> xPackage
= pSharedPackages
[ m_iSharedPackage
++ ];
837 OSL_ENSURE( xPackage
.is(), "TreeFileIterator::implGetNextSharedHelpPackage(): Invalid package" );
838 xHelpPackage
= implGetHelpPackageFromPackage( xPackage
, o_xParentPackageBundle
);
844 Reference
< deployment::XPackage
> TreeFileIterator::implGetNextBundledHelpPackage
845 ( Reference
< deployment::XPackage
>& o_xParentPackageBundle
)
847 Reference
< deployment::XPackage
> xHelpPackage
;
849 if( !m_bBundledPackagesLoaded
)
851 Reference
< XPackageManager
> xBundledManager
=
852 thePackageManagerFactory::get( m_xContext
)->getPackageManager(u
"bundled"_ustr
);
853 m_aBundledPackagesSeq
= xBundledManager
->getDeployedPackages
854 ( Reference
< task::XAbortChannel
>(), Reference
< ucb::XCommandEnvironment
>() );
856 m_bBundledPackagesLoaded
= true;
859 if( m_iBundledPackage
== m_aBundledPackagesSeq
.getLength() )
861 m_eState
= IteratorState::EndReached
;
865 const Reference
< deployment::XPackage
>* pBundledPackages
= m_aBundledPackagesSeq
.getConstArray();
866 Reference
< deployment::XPackage
> xPackage
= pBundledPackages
[ m_iBundledPackage
++ ];
867 OSL_ENSURE( xPackage
.is(), "TreeFileIterator::implGetNextBundledHelpPackage(): Invalid package" );
868 xHelpPackage
= implGetHelpPackageFromPackage( xPackage
, o_xParentPackageBundle
);
874 static bool isLetter( sal_Unicode c
)
876 return rtl::isAsciiAlpha(c
);
879 void TreeFileIterator::implGetLanguageVectorFromPackage( ::std::vector
< OUString
> &rv
,
880 const css::uno::Reference
< css::deployment::XPackage
>& xPackage
)
883 OUString aExtensionPath
= xPackage
->getURL();
884 const Sequence
< OUString
> aEntrySeq
= m_xSFA
->getFolderContents( aExtensionPath
, true );
886 for( const OUString
& aEntry
: aEntrySeq
)
888 if( m_xSFA
->isFolder( aEntry
) )
890 sal_Int32 nLastSlash
= aEntry
.lastIndexOf( '/' );
891 if( nLastSlash
!= -1 )
893 OUString aPureEntry
= aEntry
.copy( nLastSlash
+ 1 );
895 // Check language scheme
896 int nLen
= aPureEntry
.getLength();
897 const sal_Unicode
* pc
= aPureEntry
.getStr();
898 bool bStartCanBeLanguage
= ( nLen
>= 2 && isLetter( pc
[0] ) && isLetter( pc
[1] ) );
899 bool bIsLanguage
= bStartCanBeLanguage
&&
900 ( nLen
== 2 || (nLen
== 5 && pc
[2] == '-' && isLetter( pc
[3] ) && isLetter( pc
[4] )) );
902 rv
.push_back( aPureEntry
);
909 OUString
TreeFileIterator::nextTreeFile( sal_Int32
& rnFileSize
)
913 while( aRetFile
.isEmpty() && m_eState
!= IteratorState::EndReached
)
917 case IteratorState::UserExtensions
:
919 Reference
< deployment::XPackage
> xParentPackageBundle
;
920 Reference
< deployment::XPackage
> xHelpPackage
= implGetNextUserHelpPackage( xParentPackageBundle
);
921 if( !xHelpPackage
.is() )
924 aRetFile
= implGetTreeFileFromPackage( rnFileSize
, xHelpPackage
);
928 case IteratorState::SharedExtensions
:
930 Reference
< deployment::XPackage
> xParentPackageBundle
;
931 Reference
< deployment::XPackage
> xHelpPackage
= implGetNextSharedHelpPackage( xParentPackageBundle
);
932 if( !xHelpPackage
.is() )
935 aRetFile
= implGetTreeFileFromPackage( rnFileSize
, xHelpPackage
);
938 case IteratorState::BundledExtensions
:
940 Reference
< deployment::XPackage
> xParentPackageBundle
;
941 Reference
< deployment::XPackage
> xHelpPackage
= implGetNextBundledHelpPackage( xParentPackageBundle
);
942 if( !xHelpPackage
.is() )
945 aRetFile
= implGetTreeFileFromPackage( rnFileSize
, xHelpPackage
);
949 case IteratorState::EndReached
:
950 OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" );
958 OUString
TreeFileIterator::expandURL( const OUString
& aURL
)
960 static Reference
< util::XMacroExpander
> xMacroExpander
;
961 static Reference
< uri::XUriReferenceFactory
> xFac
;
963 std::scoped_lock
aGuard( m_aMutex
);
965 if( !xMacroExpander
.is() || !xFac
.is() )
967 xFac
= uri::UriReferenceFactory::create( m_xContext
);
969 xMacroExpander
= util::theMacroExpander::get(m_xContext
);
972 OUString aRetURL
= aURL
;
973 Reference
< uri::XUriReference
> uriRef
;
976 uriRef
= xFac
->parse( aRetURL
);
979 Reference
< uri::XVndSunStarExpandUrl
> sxUri( uriRef
, UNO_QUERY
);
983 aRetURL
= sxUri
->expand( xMacroExpander
);
989 OUString
TreeFileIterator::implGetTreeFileFromPackage
990 ( sal_Int32
& rnFileSize
, const Reference
< deployment::XPackage
>& xPackage
)
993 OUString aLanguage
= m_aLanguage
;
994 for( sal_Int32 iPass
= 0 ; iPass
< 2 ; ++iPass
)
996 aRetFile
= expandURL( xPackage
->getURL() + "/" + aLanguage
+ "/help.tree" );
999 if( m_xSFA
->exists( aRetFile
) )
1002 ::std::vector
< OUString
> av
;
1003 implGetLanguageVectorFromPackage( av
, xPackage
);
1004 ::std::vector
< OUString
>::const_iterator pFound
= LanguageTag::getFallback( av
, m_aLanguage
);
1005 if( pFound
!= av
.end() )
1006 aLanguage
= *pFound
;
1011 if( m_xSFA
->exists( aRetFile
) )
1012 rnFileSize
= m_xSFA
->getSize( aRetFile
);
1019 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */