jsdialog: add const
[LibreOffice.git] / xmlhelp / source / treeview / tvread.cxx
blob1e815fe6a3935b6dcc5c17c1f205db26148b2549
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 <officecfg/Office/Common.hxx>
43 #include <officecfg/Setup.hxx>
44 #include <memory>
45 #include <utility>
47 namespace treeview {
49 class TVDom
51 friend class TVChildTarget;
52 friend class TVRead;
54 public:
56 explicit TVDom( TVDom* arent = nullptr )
57 : kind( Kind::other ),
58 parent( arent ),
59 children( 0 )
63 TVDom* newChild()
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
77 if( parent )
78 return parent;
79 else
80 return const_cast<TVDom*>(this); // I am my own parent, if I am the root
83 enum class Kind {
84 tree_node,
85 tree_leaf,
86 other
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,
95 strlen( appl ),
96 RTL_TEXTENCODING_UTF8 );
99 void setTitle( const char* itle )
101 title += OUString( itle,
102 strlen( itle ),
103 RTL_TEXTENCODING_UTF8 );
106 void setTitle( const XML_Char* itle,int len )
108 title += OUString( itle,
109 len,
110 RTL_TEXTENCODING_UTF8 );
113 void setId( const char* d )
115 id = OUString( d,
116 strlen( d ),
117 RTL_TEXTENCODING_UTF8 );
120 void setAnchor( const char* nchor )
122 anchor = OUString( nchor,
123 strlen( nchor ),
124 RTL_TEXTENCODING_UTF8 );
127 OUString const & getTargetURL()
129 if( targetURL.isEmpty() )
131 targetURL = "vnd.sun.star.help://" + id;
134 return targetURL;
137 private:
139 Kind kind;
140 OUString application;
141 OUString title;
142 OUString id;
143 OUString anchor;
144 OUString targetURL;
146 TVDom *parent;
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;
175 bool cap = false;
176 OUStringBuffer aStrBuf( 0 );
178 while( ( idx = oustring.indexOf( '%', ++idx ) ) != -1 )
180 if( oustring.indexOf( prodName,idx ) == idx )
181 off = PRODUCTNAME;
182 else if( oustring.indexOf( prodVersion,idx ) == idx )
183 off = PRODUCTVERSION;
184 else if( oustring.indexOf( vendName,idx ) == idx )
185 off = VENDORNAME;
186 else if( oustring.indexOf( vendVersion,idx ) == idx )
187 off = VENDORVERSION;
188 else if( oustring.indexOf( vendShort,idx ) == idx )
189 off = VENDORSHORT;
190 else
191 off = -1;
193 if( off != -1 )
195 if( ! cap )
197 cap = true;
198 aStrBuf.ensureCapacity( 256 );
201 aStrBuf.append( &oustring.getStr()[k],idx - k );
202 aStrBuf.append( m_vReplacement[off] );
203 k = idx + m_vAdd[off];
207 if( cap )
209 if( k < oustring.getLength() )
210 aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
211 oustring = aStrBuf.makeStringAndClear();
215 // TVRead
217 TVRead::TVRead( const ConfigData& configData,TVDom* tvDom )
219 if( ! tvDom )
220 return;
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;
230 else
231 Children = new TVChildTarget( configData,tvDom );
234 TVRead::~TVRead()
238 // XNameAccess
240 Any SAL_CALL
241 TVRead::getByName( const OUString& aName )
243 bool found( true );
244 Any aAny;
245 if( aName == "Title" )
246 aAny <<= Title;
247 else if( aName == "TargetURL" )
248 aAny <<= TargetURL;
249 else if( aName == "Children" )
251 cppu::OWeakObject* p = Children.get();
252 aAny <<= Reference< XInterface >( p );
254 else
255 found = false;
257 if( found )
258 return aAny;
260 throw NoSuchElementException();
263 Sequence< OUString > SAL_CALL
264 TVRead::getElementNames( )
266 return { u"Title"_ustr, u"TargetURL"_ustr, u"Children"_ustr };
269 sal_Bool SAL_CALL
270 TVRead::hasByName( const OUString& aName )
272 if( aName == "Title" ||
273 aName == "TargetURL" ||
274 aName == "Children" )
275 return true;
277 return false;
280 // XHierarchicalNameAccess
282 Any SAL_CALL
283 TVRead::getByHierarchicalName( const OUString& aName )
285 OUString aRest;
286 if( aName.startsWith("Children/", &aRest) )
287 return Children->getByHierarchicalName( aRest );
289 return getByName( aName );
292 sal_Bool SAL_CALL
293 TVRead::hasByHierarchicalName( const OUString& aName )
295 OUString aRest;
296 if( aName.startsWith("Children/", &aRest) )
297 return Children->hasByHierarchicalName( aRest );
299 return hasByName( aName );
302 /**************************************************************************/
303 /* */
304 /* TVChildTarget */
305 /* */
306 /**************************************************************************/
308 extern "C" {
310 static void start_handler(void *userData,
311 const XML_Char *name,
312 const XML_Char **atts)
314 TVDom::Kind kind;
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;
321 else
322 return;
324 TVDom **tvDom = static_cast< TVDom** >( userData );
325 TVDom *p;
326 p = *tvDom;
328 *tvDom = p->newChild();
329 p = *tvDom;
331 p->setKind( kind );
332 while( *atts )
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) );
343 atts+=2;
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,
355 const XML_Char *s,
356 int len)
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() )
377 return;
379 sal_uInt64 ret,len = 0;
380 int j = configData.vFileURL.size();
382 TVDom tvDom;
383 TVDom* pTVDom = &tvDom;
385 while( j )
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 );
392 aFile.close();
394 XML_Parser parser = XML_ParserCreate( nullptr );
395 XML_SetElementHandler( parser,
396 start_handler,
397 end_handler );
398 XML_SetCharacterDataHandler( parser,
399 data_handler);
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 );
408 Check(pTVDom);
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())
425 return;
428 unsigned i = 0;
429 bool h = false;
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()));
441 if (tmp)
443 tvDom->children[i]->newChild(std::move(tmp));
447 tvDom->children.pop_back();
448 h = true;
450 ++i;
454 std::unique_ptr<TVDom>
455 TVChildTarget::SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom)
457 if (p->isLeaf()) return p;
459 bool h = false;
460 sal_Int32 max = 0;
462 std::vector< std::unique_ptr<TVDom> >::iterator max_It, i;
463 max_It = tvDom->children.begin();
465 sal_Int32 c_int;
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
473 h = true;
474 c_int = (*i)->id.toInt32();
476 if (p_int==c_int)
478 (*(tvDom->children.insert(i+1, std::move(p))))->parent = tvDom;
479 return nullptr;
481 else if(c_int>max && c_int < p_int)
483 max = c_int;
484 max_It = i+1;
487 if (h)
489 (*(tvDom->children.insert(max_It, std::move(p))))->parent = tvDom;
490 return nullptr;
492 else
494 for (auto& child : tvDom->children)
496 p = SearchAndInsert(std::move(p), child.get());
497 if (p == nullptr)
498 break;
500 return p;
504 Any SAL_CALL
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 );
524 return seq;
527 sal_Bool SAL_CALL
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 ) )
533 return false;
535 return true;
538 // XHierarchicalNameAccess
540 Any SAL_CALL
541 TVChildTarget::getByHierarchicalName( const OUString& aName )
543 sal_Int32 idx;
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 ) );
555 else
556 return getByName( aName );
559 sal_Bool SAL_CALL
560 TVChildTarget::hasByHierarchicalName( const OUString& aName )
562 sal_Int32 idx;
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 ) )
569 return false;
571 return Elements[pref]->hasByHierarchicalName( aName.copy( 1 + idx ) );
573 else
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);
593 subst( instPath );
595 /**********************************************************************/
596 /* reading setup */
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
609 OUString url;
610 osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url );
611 if( errFile != osl::FileBase::E_None ) return configData;
612 if( !url.endsWith("/") )
613 url += "/";
614 OUString ret;
615 sal_Int32 idx;
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 ),
622 aDirItem ) )
623 ret = configData.locale.copy( 0,idx );
624 else
626 configData.locale= "en-US";
627 ret = "en";
629 url += ret;
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);
635 OUString aTreeFile;
636 sal_Int32 nFileSize;
637 for (;;)
639 aTreeFile = aTreeIt.nextTreeFile( nFileSize );
640 if( aTreeFile.isEmpty() )
641 break;
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( '.' );
660 if( idx_ == -1 )
661 continue;
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" )
673 continue;
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
679 sal_uInt64 nSize;
680 aFile.getSize( nSize );
681 configData.vFileLen.push_back( nSize );
682 configData.vFileURL.push_back( aFileUrl );
683 aFile.close();
687 aDirectory.close();
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 =
697 "?Language=" +
698 configData.locale +
699 "&System=" +
700 configData.system +
701 "&UseDB=no";
703 return configData;
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;
730 m_iUserPackage = 0;
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;
741 if( !xPackage.is() )
742 return 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 )
752 bRegistered = true;
754 if( !bRegistered )
755 return xHelpPackage;
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;
773 else
775 const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
776 OUString aMediaType = xPackageTypeInfo->getMediaType();
777 if( aMediaType == aHelpMediaType )
778 xHelpPackage = xPackage;
781 return xHelpPackage;
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
803 else
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 );
811 return xHelpPackage;
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;
833 else
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 );
841 return xHelpPackage;
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;
863 else
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 );
871 return xHelpPackage;
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 )
882 rv.clear();
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] )) );
901 if( bIsLanguage )
902 rv.push_back( aPureEntry );
909 OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize )
911 OUString aRetFile;
913 while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached )
915 switch( m_eState )
917 case IteratorState::UserExtensions:
919 Reference< deployment::XPackage > xParentPackageBundle;
920 Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
921 if( !xHelpPackage.is() )
922 break;
924 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
925 break;
928 case IteratorState::SharedExtensions:
930 Reference< deployment::XPackage > xParentPackageBundle;
931 Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
932 if( !xHelpPackage.is() )
933 break;
935 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
936 break;
938 case IteratorState::BundledExtensions:
940 Reference< deployment::XPackage > xParentPackageBundle;
941 Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
942 if( !xHelpPackage.is() )
943 break;
945 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
946 break;
949 case IteratorState::EndReached:
950 OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" );
951 break;
955 return aRetFile;
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;
974 for (;;)
976 uriRef = xFac->parse( aRetURL );
977 if ( uriRef.is() )
979 Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
980 if( !sxUri.is() )
981 break;
983 aRetURL = sxUri->expand( xMacroExpander );
986 return aRetURL;
989 OUString TreeFileIterator::implGetTreeFileFromPackage
990 ( sal_Int32& rnFileSize, const Reference< deployment::XPackage >& xPackage )
992 OUString aRetFile;
993 OUString aLanguage = m_aLanguage;
994 for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
996 aRetFile = expandURL( xPackage->getURL() + "/" + aLanguage + "/help.tree" );
997 if( iPass == 0 )
999 if( m_xSFA->exists( aRetFile ) )
1000 break;
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;
1010 rnFileSize = 0;
1011 if( m_xSFA->exists( aRetFile ) )
1012 rnFileSize = m_xSFA->getSize( aRetFile );
1013 else
1014 aRetFile.clear();
1016 return aRetFile;
1019 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */