Don't link against private CoreUI framework when sandboxed
[LibreOffice.git] / xmlhelp / source / treeview / tvread.cxx
blob1a56533fd27bcea8106ebe5fb978f44f22eecd30
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <string.h>
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>
27 #include <tvread.hxx>
28 #include <expat.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 <memory>
44 namespace treeview {
46 class TVDom
48 friend class TVChildTarget;
49 friend class TVRead;
51 public:
53 explicit TVDom( TVDom* arent = nullptr )
54 : kind( Kind::other ),
55 parent( arent ),
56 children( 0 )
60 TVDom* newChild()
62 children.emplace_back( new TVDom( this ) );
63 return children.back().get();
66 void newChild(std::unique_ptr<TVDom> p)
68 children.emplace_back(std::move(p));
69 children.back()->parent = this;
72 TVDom* getParent() const
74 if( parent )
75 return parent;
76 else
77 return const_cast<TVDom*>(this); // I am my own parent, if I am the root
80 enum class Kind {
81 tree_node,
82 tree_leaf,
83 other
86 bool isLeaf() const { return kind == TVDom::Kind::tree_leaf; }
87 void setKind( Kind ind ) { kind = ind; }
89 void setApplication( const char* appl )
91 application = OUString( appl,
92 strlen( appl ),
93 RTL_TEXTENCODING_UTF8 );
96 void setTitle( const char* itle )
98 title += OUString( itle,
99 strlen( itle ),
100 RTL_TEXTENCODING_UTF8 );
103 void setTitle( const XML_Char* itle,int len )
105 title += OUString( itle,
106 len,
107 RTL_TEXTENCODING_UTF8 );
110 void setId( const char* d )
112 id = OUString( d,
113 strlen( d ),
114 RTL_TEXTENCODING_UTF8 );
117 void setAnchor( const char* nchor )
119 anchor = OUString( nchor,
120 strlen( nchor ),
121 RTL_TEXTENCODING_UTF8 );
124 OUString const & getTargetURL()
126 if( targetURL.isEmpty() )
128 targetURL = "vnd.sun.star.help://" + id;
131 return targetURL;
134 private:
136 Kind kind;
137 OUString application;
138 OUString title;
139 OUString id;
140 OUString anchor;
141 OUString targetURL;
143 TVDom *parent;
144 std::vector< std::unique_ptr<TVDom> > children;
149 using namespace treeview;
150 using namespace com::sun::star;
151 using namespace com::sun::star::uno;
152 using namespace com::sun::star::beans;
153 using namespace com::sun::star::configuration;
154 using namespace com::sun::star::lang;
155 using namespace com::sun::star::util;
156 using namespace com::sun::star::container;
157 using namespace com::sun::star::deployment;
159 const char prodName[] = "%PRODUCTNAME";
160 const char vendName[] = "%VENDORNAME";
161 const char vendVersion[] = "%VENDORVERSION";
162 const char vendShort[] = "%VENDORSHORT";
163 const char prodVersion[] = "%PRODUCTVERSION";
165 ConfigData::ConfigData()
169 void ConfigData::replaceName( OUString& oustring ) const
171 sal_Int32 idx = -1,k = 0,off;
172 bool cap = false;
173 OUStringBuffer aStrBuf( 0 );
175 while( ( idx = oustring.indexOf( '%', ++idx ) ) != -1 )
177 if( oustring.indexOf( prodName,idx ) == idx )
178 off = PRODUCTNAME;
179 else if( oustring.indexOf( prodVersion,idx ) == idx )
180 off = PRODUCTVERSION;
181 else if( oustring.indexOf( vendName,idx ) == idx )
182 off = VENDORNAME;
183 else if( oustring.indexOf( vendVersion,idx ) == idx )
184 off = VENDORVERSION;
185 else if( oustring.indexOf( vendShort,idx ) == idx )
186 off = VENDORSHORT;
187 else
188 off = -1;
190 if( off != -1 )
192 if( ! cap )
194 cap = true;
195 aStrBuf.ensureCapacity( 256 );
198 aStrBuf.append( &oustring.getStr()[k],idx - k );
199 aStrBuf.append( m_vReplacement[off] );
200 k = idx + m_vAdd[off];
204 if( cap )
206 if( k < oustring.getLength() )
207 aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
208 oustring = aStrBuf.makeStringAndClear();
212 // TVRead
214 TVRead::TVRead( const ConfigData& configData,TVDom* tvDom )
216 if( ! tvDom )
217 return;
219 Title = tvDom->title;
220 configData.replaceName( Title );
221 if( tvDom->isLeaf() )
223 TargetURL = tvDom->getTargetURL() + configData.appendix;
224 if( !tvDom->anchor.isEmpty() )
225 TargetURL += "#" + tvDom->anchor;
227 else
228 Children = new TVChildTarget( configData,tvDom );
231 TVRead::~TVRead()
235 // XNameAccess
237 Any SAL_CALL
238 TVRead::getByName( const OUString& aName )
240 bool found( true );
241 Any aAny;
242 if( aName == "Title" )
243 aAny <<= Title;
244 else if( aName == "TargetURL" )
245 aAny <<= TargetURL;
246 else if( aName == "Children" )
248 cppu::OWeakObject* p = Children.get();
249 aAny <<= Reference< XInterface >( p );
251 else
252 found = false;
254 if( found )
255 return aAny;
257 throw NoSuchElementException();
260 Sequence< OUString > SAL_CALL
261 TVRead::getElementNames( )
263 return { "Title", "TargetURL", "Children" };
266 sal_Bool SAL_CALL
267 TVRead::hasByName( const OUString& aName )
269 if( aName == "Title" ||
270 aName == "TargetURL" ||
271 aName == "Children" )
272 return true;
274 return false;
277 // XHierarchicalNameAccess
279 Any SAL_CALL
280 TVRead::getByHierarchicalName( const OUString& aName )
282 OUString aRest;
283 if( aName.startsWith("Children/", &aRest) )
284 return Children->getByHierarchicalName( aRest );
286 return getByName( aName );
289 sal_Bool SAL_CALL
290 TVRead::hasByHierarchicalName( const OUString& aName )
292 OUString aRest;
293 if( aName.startsWith("Children/", &aRest) )
294 return Children->hasByHierarchicalName( aRest );
296 return hasByName( aName );
299 /**************************************************************************/
300 /* */
301 /* TVChildTarget */
302 /* */
303 /**************************************************************************/
305 extern "C" {
307 static void start_handler(void *userData,
308 const XML_Char *name,
309 const XML_Char **atts)
311 TVDom::Kind kind;
313 if( strcmp( name,"help_section" ) == 0 ||
314 strcmp( name,"node" ) == 0 )
315 kind = TVDom::Kind::tree_node;
316 else if( strcmp( name,"topic" ) == 0 )
317 kind = TVDom::Kind::tree_leaf;
318 else
319 return;
321 TVDom **tvDom = static_cast< TVDom** >( userData );
322 TVDom *p;
323 p = *tvDom;
325 *tvDom = p->newChild();
326 p = *tvDom;
328 p->setKind( kind );
329 while( *atts )
331 if( strcmp( *atts,"application" ) == 0 )
332 p->setApplication( *(atts+1) );
333 else if( strcmp( *atts,"title" ) == 0 )
334 p->setTitle( *(atts+1) );
335 else if( strcmp( *atts,"id" ) == 0 )
336 p->setId( *(atts+1) );
337 else if( strcmp( *atts,"anchor" ) == 0 )
338 p->setAnchor( *(atts+1) );
340 atts+=2;
344 static void end_handler(void *userData,
345 SAL_UNUSED_PARAMETER const XML_Char * )
347 TVDom **tvDom = static_cast< TVDom** >( userData );
348 *tvDom = (*tvDom)->getParent();
351 static void data_handler( void *userData,
352 const XML_Char *s,
353 int len)
355 TVDom **tvDom = static_cast< TVDom** >( userData );
356 if( (*tvDom)->isLeaf() )
357 (*tvDom)->setTitle( s,len );
362 TVChildTarget::TVChildTarget( const ConfigData& configData,TVDom* tvDom )
364 Elements.resize( tvDom->children.size() );
365 for( size_t i = 0; i < Elements.size(); ++i )
366 Elements[i] = new TVRead( configData,tvDom->children[i].get() );
369 TVChildTarget::TVChildTarget( const Reference< XComponentContext >& xContext )
371 ConfigData configData = init( xContext );
373 if( configData.locale.isEmpty() || configData.system.isEmpty() )
374 return;
376 sal_uInt64 ret,len = 0;
377 int j = configData.vFileURL.size();
379 TVDom tvDom;
380 TVDom* pTVDom = &tvDom;
382 while( j )
384 len = configData.vFileLen[--j];
385 std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files
386 osl::File aFile( configData.vFileURL[j] );
387 (void)aFile.open( osl_File_OpenFlag_Read );
388 aFile.read( s.get(),len,ret );
389 aFile.close();
391 XML_Parser parser = XML_ParserCreate( nullptr );
392 XML_SetElementHandler( parser,
393 start_handler,
394 end_handler );
395 XML_SetCharacterDataHandler( parser,
396 data_handler);
397 XML_SetUserData( parser,&pTVDom ); // does not return this
399 XML_Status const parsed = XML_Parse(parser, s.get(), int(len), j==0);
400 SAL_WARN_IF(XML_STATUS_ERROR == parsed, "xmlhelp",
401 "TVChildTarget::TVChildTarget(): Tree file parsing failed");
403 XML_ParserFree( parser );
405 Check(pTVDom);
407 // now TVDom holds the relevant information
409 Elements.resize( tvDom.children.size() );
410 for( size_t i = 0; i < Elements.size(); ++i )
411 Elements[i] = new TVRead( configData,tvDom.children[i].get() );
414 TVChildTarget::~TVChildTarget()
418 void TVChildTarget::Check(TVDom* tvDom)
420 if (tvDom->children.empty())
422 return;
425 unsigned i = 0;
426 bool h = false;
428 while((i<tvDom->children.size()-1) && (!h))
430 if (((tvDom->children[i])->application == (tvDom->children[tvDom->children.size()-1])->application) &&
431 ((tvDom->children[i])->id == (tvDom->children[tvDom->children.size()-1])->id))
433 TVDom* p = tvDom->children.back().get();
435 for(auto & k : p->children)
437 std::unique_ptr<TVDom> tmp(SearchAndInsert(std::move(k), tvDom->children[i].get()));
438 if (tmp)
440 tvDom->children[i]->newChild(std::move(tmp));
444 tvDom->children.pop_back();
445 h = true;
447 ++i;
451 std::unique_ptr<TVDom>
452 TVChildTarget::SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom)
454 if (p->isLeaf()) return p;
456 bool h = false;
457 sal_Int32 max = 0;
459 std::vector< std::unique_ptr<TVDom> >::iterator max_It, i;
460 max_It = tvDom->children.begin();
462 sal_Int32 c_int;
463 sal_Int32 p_int = p->id.toInt32();
465 for(i = tvDom->children.begin(); i!=tvDom->children.end(); ++i)
466 if (!((*i)->isLeaf()) &&
467 ((*i)->id.getLength() == p->id.getLength()) &&
468 (p->id.replaceAt((*i)->parent->id.getLength(), p->id.getLength()-(*i)->parent->id.getLength(), u"") == (*i)->parent->id)) //prefix check
470 h = true;
471 c_int = (*i)->id.toInt32();
473 if (p_int==c_int)
475 (*(tvDom->children.insert(i+1, std::move(p))))->parent = tvDom;
476 return nullptr;
478 else if(c_int>max && c_int < p_int)
480 max = c_int;
481 max_It = i+1;
484 if (h)
486 (*(tvDom->children.insert(max_It, std::move(p))))->parent = tvDom;
487 return nullptr;
489 else
491 for (auto& child : tvDom->children)
493 p = SearchAndInsert(std::move(p), child.get());
494 if (p == nullptr)
495 break;
497 return p;
501 Any SAL_CALL
502 TVChildTarget::getByName( const OUString& aName )
504 std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) );
505 sal_Int32 idx = o3tl::toInt32(num) - 1;
506 if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
507 throw NoSuchElementException();
509 cppu::OWeakObject* p = Elements[idx].get();
510 return Any( Reference< XInterface >( p ) );
513 Sequence< OUString > SAL_CALL
514 TVChildTarget::getElementNames( )
516 Sequence< OUString > seq( Elements.size() );
517 auto seqRange = asNonConstRange(seq);
518 for( size_t i = 0; i < Elements.size(); ++i )
519 seqRange[i] = OUString::number( 1+i );
521 return seq;
524 sal_Bool SAL_CALL
525 TVChildTarget::hasByName( const OUString& aName )
527 std::u16string_view num( aName.subView( 2, aName.getLength()-4 ) );
528 sal_Int32 idx = o3tl::toInt32(num) - 1;
529 if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
530 return false;
532 return true;
535 // XHierarchicalNameAccess
537 Any SAL_CALL
538 TVChildTarget::getByHierarchicalName( const OUString& aName )
540 sal_Int32 idx;
542 if( ( idx = aName.indexOf( '/' ) ) != -1 )
544 std::u16string_view num( aName.subView( 2, idx-4 ) );
545 sal_Int32 pref = o3tl::toInt32(num) - 1;
547 if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
548 throw NoSuchElementException();
550 return Elements[pref]->getByHierarchicalName( aName.copy( 1 + idx ) );
552 else
553 return getByName( aName );
556 sal_Bool SAL_CALL
557 TVChildTarget::hasByHierarchicalName( const OUString& aName )
559 sal_Int32 idx;
561 if( ( idx = aName.indexOf( '/' ) ) != -1 )
563 std::u16string_view num( aName.subView( 2, idx-4 ) );
564 sal_Int32 pref = o3tl::toInt32(num) - 1;
565 if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
566 return false;
568 return Elements[pref]->hasByHierarchicalName( aName.copy( 1 + idx ) );
570 else
571 return hasByName( aName );
574 ConfigData TVChildTarget::init( const Reference< XComponentContext >& xContext )
576 ConfigData configData;
577 Reference< XMultiServiceFactory > sProvider( getConfiguration(xContext) );
579 /**********************************************************************/
580 /* reading Office.Common */
581 /**********************************************************************/
583 Reference< XHierarchicalNameAccess > xHierAccess( getHierAccess( sProvider,
584 "org.openoffice.Office.Common" ) );
585 OUString system( getKey( xHierAccess,"Help/System" ) );
586 bool showBasic( getBooleanKey(xHierAccess,"Help/ShowBasic") );
587 OUString instPath( getKey( xHierAccess,"Path/Current/Help" ) );
588 if( instPath.isEmpty() )
589 // try to determine path from default
590 instPath = "$(instpath)/help";
592 // replace anything like $(instpath);
593 subst( instPath );
595 /**********************************************************************/
596 /* reading setup */
597 /**********************************************************************/
599 xHierAccess = getHierAccess( sProvider,
600 "org.openoffice.Setup" );
602 OUString setupversion( getKey( xHierAccess,"Product/ooSetupVersion" ) );
603 OUString setupextension;
607 Reference< lang::XMultiServiceFactory > xConfigProvider = theDefaultProvider::get( xContext );
609 uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence(
611 {"nodepath", uno::Any(OUString("/org.openoffice.Setup/Product"))}
612 }));
614 // open it
615 uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
616 "com.sun.star.configuration.ConfigurationAccess",
617 lParams) );
619 uno::Reference< container::XNameAccess > xDirectAccess(xCFG, uno::UNO_QUERY);
620 uno::Any aRet = xDirectAccess->getByName("ooSetupExtension");
622 aRet >>= setupextension;
624 catch ( uno::Exception& )
628 OUString productVersion( setupversion + " " + setupextension );
629 OUString locale( getKey( xHierAccess,"L10N/ooLocale" ) );
631 // Determine fileurl from url and locale
632 OUString url;
633 osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url );
634 if( errFile != osl::FileBase::E_None ) return configData;
635 if( !url.endsWith("/") )
636 url += "/";
637 OUString ret;
638 sal_Int32 idx;
639 osl::DirectoryItem aDirItem;
640 if( osl::FileBase::E_None == osl::DirectoryItem::get( url + locale,aDirItem ) )
641 ret = locale;
642 else if( ( ( idx = locale.indexOf( '-' ) ) != -1 ||
643 ( idx = locale.indexOf( '_' ) ) != -1 ) &&
644 osl::FileBase::E_None == osl::DirectoryItem::get( url + locale.subView( 0,idx ),
645 aDirItem ) )
646 ret = locale.copy( 0,idx );
647 else
649 locale = "en-US";
650 ret = "en";
652 url += ret;
654 // first of all, try do determine whether there are any *.tree files present
656 // Start with extensions to set them at the end of the list
657 TreeFileIterator aTreeIt( locale );
658 OUString aTreeFile;
659 sal_Int32 nFileSize;
660 for (;;)
662 aTreeFile = aTreeIt.nextTreeFile( nFileSize );
663 if( aTreeFile.isEmpty() )
664 break;
665 configData.vFileLen.push_back( nFileSize );
666 configData.vFileURL.push_back( aTreeFile );
669 osl::Directory aDirectory( url );
670 osl::FileStatus aFileStatus(
671 osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_FileURL );
672 if( osl::Directory::E_None == aDirectory.open() )
674 OUString aFileUrl, aFileName;
675 while( aDirectory.getNextItem( aDirItem ) == osl::FileBase::E_None &&
676 aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
677 aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) &&
678 aFileStatus.isValid( osl_FileStatus_Mask_FileName ) )
680 aFileUrl = aFileStatus.getFileURL();
681 aFileName = aFileStatus.getFileName();
682 int idx_ = aFileName.lastIndexOf( '.' );
683 if( idx_ == -1 )
684 continue;
686 const sal_Unicode* str = aFileName.getStr();
688 if( aFileName.getLength() == idx_ + 5 &&
689 ( str[idx_ + 1] == 't' || str[idx_ + 1] == 'T' ) &&
690 ( str[idx_ + 2] == 'r' || str[idx_ + 2] == 'R' ) &&
691 ( str[idx_ + 3] == 'e' || str[idx_ + 3] == 'E' ) &&
692 ( str[idx_ + 4] == 'e' || str[idx_ + 4] == 'E' ) )
694 OUString baseName = aFileName.copy(0,idx_).toAsciiLowerCase();
695 if(! showBasic && baseName == "sbasic" )
696 continue;
697 osl::File aFile( aFileUrl );
698 if( osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) )
700 // use the file size, not aFileStatus size, in case the
701 // tree file is a symlink
702 sal_uInt64 nSize;
703 aFile.getSize( nSize );
704 configData.vFileLen.push_back( nSize );
705 configData.vFileURL.push_back( aFileUrl );
706 aFile.close();
710 aDirectory.close();
713 configData.m_vAdd[0] = 12;
714 configData.m_vAdd[1] = 15;
715 configData.m_vAdd[2] = 11;
716 configData.m_vAdd[3] = 14;
717 configData.m_vAdd[4] = 12;
718 configData.m_vReplacement[0] = utl::ConfigManager::getProductName();
719 configData.m_vReplacement[1] = productVersion;
720 // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
722 configData.system = system;
723 configData.locale = locale;
724 configData.appendix =
725 "?Language=" +
726 configData.locale +
727 "&System=" +
728 configData.system +
729 "&UseDB=no";
731 return configData;
734 Reference< XMultiServiceFactory >
735 TVChildTarget::getConfiguration(const Reference< XComponentContext >& rxContext)
737 Reference< XMultiServiceFactory > xProvider;
738 if( rxContext.is() )
742 xProvider = theDefaultProvider::get( rxContext );
744 catch( const css::uno::Exception& )
746 OSL_ENSURE( xProvider.is(),"can not instantiate configuration" );
750 return xProvider;
753 Reference< XHierarchicalNameAccess >
754 TVChildTarget::getHierAccess( const Reference< XMultiServiceFactory >& sProvider,
755 const char* file )
757 Reference< XHierarchicalNameAccess > xHierAccess;
759 if( sProvider.is() )
763 xHierAccess =
764 Reference< XHierarchicalNameAccess >
765 ( sProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", { Any(OUString::createFromAscii(file)) }),
766 UNO_QUERY );
768 catch( const css::uno::Exception& )
773 return xHierAccess;
776 OUString
777 TVChildTarget::getKey( const Reference< XHierarchicalNameAccess >& xHierAccess,
778 const char* key )
780 OUString instPath;
781 if( xHierAccess.is() )
783 Any aAny;
786 aAny =
787 xHierAccess->getByHierarchicalName( OUString::createFromAscii( key ) );
789 catch( const css::container::NoSuchElementException& )
792 aAny >>= instPath;
794 return instPath;
797 bool
798 TVChildTarget::getBooleanKey(const Reference<
799 XHierarchicalNameAccess >& xHierAccess,
800 const char* key)
802 bool ret = false;
803 if( xHierAccess.is() )
805 Any aAny;
808 aAny =
809 xHierAccess->getByHierarchicalName(
810 OUString::createFromAscii(key));
812 catch( const css::container::NoSuchElementException& )
815 aAny >>= ret;
817 return ret;
820 void TVChildTarget::subst( OUString& instpath )
822 SvtPathOptions aOptions;
823 instpath = aOptions.SubstituteVariable( instpath );
827 const char aHelpMediaType[] = "application/vnd.sun.star.help";
829 TreeFileIterator::TreeFileIterator( const OUString& aLanguage )
830 : m_eState( IteratorState::UserExtensions )
831 , m_aLanguage( aLanguage )
833 m_xContext = ::comphelper::getProcessComponentContext();
834 if( !m_xContext.is() )
836 throw RuntimeException( "TreeFileIterator::TreeFileIterator(), no XComponentContext" );
839 m_xSFA = ucb::SimpleFileAccess::create(m_xContext);
841 m_bUserPackagesLoaded = false;
842 m_bSharedPackagesLoaded = false;
843 m_bBundledPackagesLoaded = false;
844 m_iUserPackage = 0;
845 m_iSharedPackage = 0;
846 m_iBundledPackage = 0;
849 Reference< deployment::XPackage > TreeFileIterator::implGetHelpPackageFromPackage
850 ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle )
852 o_xParentPackageBundle.clear();
854 Reference< deployment::XPackage > xHelpPackage;
855 if( !xPackage.is() )
856 return xHelpPackage;
858 // Check if parent package is registered
859 beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered
860 ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) );
861 bool bRegistered = false;
862 if( option.IsPresent )
864 beans::Ambiguous<sal_Bool> const & reg = option.Value;
865 if( !reg.IsAmbiguous && reg.Value )
866 bRegistered = true;
868 if( !bRegistered )
869 return xHelpPackage;
871 if( xPackage->isBundle() )
873 const Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle
874 ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() );
875 auto pSubPkg = std::find_if(aPkgSeq.begin(), aPkgSeq.end(),
876 [](const Reference< deployment::XPackage >& xSubPkg) {
877 const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType();
878 OUString aMediaType = xPackageTypeInfo->getMediaType();
879 return aMediaType == aHelpMediaType;
881 if (pSubPkg != aPkgSeq.end())
883 xHelpPackage = *pSubPkg;
884 o_xParentPackageBundle = xPackage;
887 else
889 const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
890 OUString aMediaType = xPackageTypeInfo->getMediaType();
891 if( aMediaType == aHelpMediaType )
892 xHelpPackage = xPackage;
895 return xHelpPackage;
898 Reference< deployment::XPackage > TreeFileIterator::implGetNextUserHelpPackage
899 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
901 Reference< deployment::XPackage > xHelpPackage;
903 if( !m_bUserPackagesLoaded )
905 Reference< XPackageManager > xUserManager =
906 thePackageManagerFactory::get( m_xContext )->getPackageManager("user");
907 m_aUserPackagesSeq = xUserManager->getDeployedPackages
908 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
910 m_bUserPackagesLoaded = true;
913 if( m_iUserPackage == m_aUserPackagesSeq.getLength() )
915 m_eState = IteratorState::SharedExtensions; // Later: SHARED_MODULE
917 else
919 const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray();
920 Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ];
921 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextUserHelpPackage(): Invalid package" );
922 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
925 return xHelpPackage;
928 Reference< deployment::XPackage > TreeFileIterator::implGetNextSharedHelpPackage
929 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
931 Reference< deployment::XPackage > xHelpPackage;
933 if( !m_bSharedPackagesLoaded )
935 Reference< XPackageManager > xSharedManager =
936 thePackageManagerFactory::get( m_xContext )->getPackageManager("shared");
937 m_aSharedPackagesSeq = xSharedManager->getDeployedPackages
938 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
940 m_bSharedPackagesLoaded = true;
943 if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() )
945 m_eState = IteratorState::BundledExtensions;
947 else
949 const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray();
950 Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ];
951 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextSharedHelpPackage(): Invalid package" );
952 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
955 return xHelpPackage;
958 Reference< deployment::XPackage > TreeFileIterator::implGetNextBundledHelpPackage
959 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
961 Reference< deployment::XPackage > xHelpPackage;
963 if( !m_bBundledPackagesLoaded )
965 Reference< XPackageManager > xBundledManager =
966 thePackageManagerFactory::get( m_xContext )->getPackageManager("bundled");
967 m_aBundledPackagesSeq = xBundledManager->getDeployedPackages
968 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
970 m_bBundledPackagesLoaded = true;
973 if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() )
975 m_eState = IteratorState::EndReached;
977 else
979 const Reference< deployment::XPackage >* pBundledPackages = m_aBundledPackagesSeq.getConstArray();
980 Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ];
981 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextBundledHelpPackage(): Invalid package" );
982 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
985 return xHelpPackage;
988 static bool isLetter( sal_Unicode c )
990 return rtl::isAsciiAlpha(c);
993 void TreeFileIterator::implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv,
994 const css::uno::Reference< css::deployment::XPackage >& xPackage )
996 rv.clear();
997 OUString aExtensionPath = xPackage->getURL();
998 const Sequence< OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true );
1000 for( const OUString& aEntry : aEntrySeq )
1002 if( m_xSFA->isFolder( aEntry ) )
1004 sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' );
1005 if( nLastSlash != -1 )
1007 OUString aPureEntry = aEntry.copy( nLastSlash + 1 );
1009 // Check language scheme
1010 int nLen = aPureEntry.getLength();
1011 const sal_Unicode* pc = aPureEntry.getStr();
1012 bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) );
1013 bool bIsLanguage = bStartCanBeLanguage &&
1014 ( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) );
1015 if( bIsLanguage )
1016 rv.push_back( aPureEntry );
1023 OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize )
1025 OUString aRetFile;
1027 while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached )
1029 switch( m_eState )
1031 case IteratorState::UserExtensions:
1033 Reference< deployment::XPackage > xParentPackageBundle;
1034 Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
1035 if( !xHelpPackage.is() )
1036 break;
1038 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1039 break;
1042 case IteratorState::SharedExtensions:
1044 Reference< deployment::XPackage > xParentPackageBundle;
1045 Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
1046 if( !xHelpPackage.is() )
1047 break;
1049 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1050 break;
1052 case IteratorState::BundledExtensions:
1054 Reference< deployment::XPackage > xParentPackageBundle;
1055 Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
1056 if( !xHelpPackage.is() )
1057 break;
1059 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1060 break;
1063 case IteratorState::EndReached:
1064 OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" );
1065 break;
1069 return aRetFile;
1072 OUString TreeFileIterator::expandURL( const OUString& aURL )
1074 static Reference< util::XMacroExpander > xMacroExpander;
1075 static Reference< uri::XUriReferenceFactory > xFac;
1077 std::scoped_lock aGuard( m_aMutex );
1079 if( !xMacroExpander.is() || !xFac.is() )
1081 xFac = uri::UriReferenceFactory::create( m_xContext );
1083 xMacroExpander = util::theMacroExpander::get(m_xContext);
1086 OUString aRetURL = aURL;
1087 Reference< uri::XUriReference > uriRef;
1088 for (;;)
1090 uriRef = xFac->parse( aRetURL );
1091 if ( uriRef.is() )
1093 Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
1094 if( !sxUri.is() )
1095 break;
1097 aRetURL = sxUri->expand( xMacroExpander );
1100 return aRetURL;
1103 OUString TreeFileIterator::implGetTreeFileFromPackage
1104 ( sal_Int32& rnFileSize, const Reference< deployment::XPackage >& xPackage )
1106 OUString aRetFile;
1107 OUString aLanguage = m_aLanguage;
1108 for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
1110 aRetFile = expandURL( xPackage->getURL() + "/" + aLanguage + "/help.tree" );
1111 if( iPass == 0 )
1113 if( m_xSFA->exists( aRetFile ) )
1114 break;
1116 ::std::vector< OUString > av;
1117 implGetLanguageVectorFromPackage( av, xPackage );
1118 ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage );
1119 if( pFound != av.end() )
1120 aLanguage = *pFound;
1124 rnFileSize = 0;
1125 if( m_xSFA->exists( aRetFile ) )
1126 rnFileSize = m_xSFA->getSize( aRetFile );
1127 else
1128 aRetFile.clear();
1130 return aRetFile;
1133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */