nss: upgrade to release 3.73
[LibreOffice.git] / xmlhelp / source / treeview / tvread.cxx
blob83505b36608be2cb71101dcddaa76c69d9505813
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 <osl/diagnose.h>
26 #include <tvread.hxx>
27 #include <expat.h>
28 #include <osl/file.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <com/sun/star/configuration/theDefaultProvider.hpp>
31 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/propertysequence.hxx>
35 #include <com/sun/star/deployment/thePackageManagerFactory.hpp>
36 #include <com/sun/star/util/theMacroExpander.hpp>
37 #include <com/sun/star/uri/UriReferenceFactory.hpp>
38 #include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
39 #include <i18nlangtag/languagetag.hxx>
40 #include <unotools/pathoptions.hxx>
41 #include <memory>
43 namespace treeview {
45 class TVDom
47 friend class TVChildTarget;
48 friend class TVRead;
50 public:
52 explicit TVDom( TVDom* arent = nullptr )
53 : kind( Kind::other ),
54 parent( arent ),
55 children( 0 )
59 TVDom* newChild()
61 children.emplace_back( new TVDom( this ) );
62 return children.back().get();
65 void newChild(std::unique_ptr<TVDom> p)
67 children.emplace_back(std::move(p));
68 children.back()->parent = this;
71 TVDom* getParent() const
73 if( parent )
74 return parent;
75 else
76 return const_cast<TVDom*>(this); // I am my own parent, if I am the root
79 enum class Kind {
80 tree_node,
81 tree_leaf,
82 other
85 bool isLeaf() const { return kind == TVDom::Kind::tree_leaf; }
86 void setKind( Kind ind ) { kind = ind; }
88 void setApplication( const char* appl )
90 application = OUString( appl,
91 strlen( appl ),
92 RTL_TEXTENCODING_UTF8 );
95 void setTitle( const char* itle )
97 title += OUString( itle,
98 strlen( itle ),
99 RTL_TEXTENCODING_UTF8 );
102 void setTitle( const XML_Char* itle,int len )
104 title += OUString( itle,
105 len,
106 RTL_TEXTENCODING_UTF8 );
109 void setId( const char* d )
111 id = OUString( d,
112 strlen( d ),
113 RTL_TEXTENCODING_UTF8 );
116 void setAnchor( const char* nchor )
118 anchor = OUString( nchor,
119 strlen( nchor ),
120 RTL_TEXTENCODING_UTF8 );
123 OUString const & getTargetURL()
125 if( targetURL.isEmpty() )
127 targetURL = "vnd.sun.star.help://" + id;
130 return targetURL;
133 private:
135 Kind kind;
136 OUString application;
137 OUString title;
138 OUString id;
139 OUString anchor;
140 OUString targetURL;
142 TVDom *parent;
143 std::vector< std::unique_ptr<TVDom> > children;
148 using namespace treeview;
149 using namespace com::sun::star;
150 using namespace com::sun::star::uno;
151 using namespace com::sun::star::beans;
152 using namespace com::sun::star::configuration;
153 using namespace com::sun::star::lang;
154 using namespace com::sun::star::util;
155 using namespace com::sun::star::container;
156 using namespace com::sun::star::deployment;
158 const char prodName[] = "%PRODUCTNAME";
159 const char vendName[] = "%VENDORNAME";
160 const char vendVersion[] = "%VENDORVERSION";
161 const char vendShort[] = "%VENDORSHORT";
162 const char prodVersion[] = "%PRODUCTVERSION";
164 ConfigData::ConfigData()
168 void ConfigData::replaceName( OUString& oustring ) const
170 sal_Int32 idx = -1,k = 0,off;
171 bool cap = false;
172 OUStringBuffer aStrBuf( 0 );
174 while( ( idx = oustring.indexOf( '%', ++idx ) ) != -1 )
176 if( oustring.indexOf( prodName,idx ) == idx )
177 off = PRODUCTNAME;
178 else if( oustring.indexOf( prodVersion,idx ) == idx )
179 off = PRODUCTVERSION;
180 else if( oustring.indexOf( vendName,idx ) == idx )
181 off = VENDORNAME;
182 else if( oustring.indexOf( vendVersion,idx ) == idx )
183 off = VENDORVERSION;
184 else if( oustring.indexOf( vendShort,idx ) == idx )
185 off = VENDORSHORT;
186 else
187 off = -1;
189 if( off != -1 )
191 if( ! cap )
193 cap = true;
194 aStrBuf.ensureCapacity( 256 );
197 aStrBuf.append( &oustring.getStr()[k],idx - k );
198 aStrBuf.append( m_vReplacement[off] );
199 k = idx + m_vAdd[off];
203 if( cap )
205 if( k < oustring.getLength() )
206 aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
207 oustring = aStrBuf.makeStringAndClear();
211 // TVRead
213 TVRead::TVRead( const ConfigData& configData,TVDom* tvDom )
215 if( ! tvDom )
216 return;
218 Title = tvDom->title;
219 configData.replaceName( Title );
220 if( tvDom->isLeaf() )
222 TargetURL = tvDom->getTargetURL() + configData.appendix;
223 if( !tvDom->anchor.isEmpty() )
224 TargetURL += "#" + tvDom->anchor;
226 else
227 Children = new TVChildTarget( configData,tvDom );
230 TVRead::~TVRead()
234 // XNameAccess
236 Any SAL_CALL
237 TVRead::getByName( const OUString& aName )
239 bool found( true );
240 Any aAny;
241 if( aName == "Title" )
242 aAny <<= Title;
243 else if( aName == "TargetURL" )
244 aAny <<= TargetURL;
245 else if( aName == "Children" )
247 cppu::OWeakObject* p = Children.get();
248 aAny <<= Reference< XInterface >( p );
250 else
251 found = false;
253 if( found )
254 return aAny;
256 throw NoSuchElementException();
259 Sequence< OUString > SAL_CALL
260 TVRead::getElementNames( )
262 Sequence< OUString > seq( 3 );
264 seq[0] = "Title";
265 seq[1] = "TargetURL";
266 seq[2] = "Children";
268 return seq;
271 sal_Bool SAL_CALL
272 TVRead::hasByName( const OUString& aName )
274 if( aName == "Title" ||
275 aName == "TargetURL" ||
276 aName == "Children" )
277 return true;
279 return false;
282 // XHierarchicalNameAccess
284 Any SAL_CALL
285 TVRead::getByHierarchicalName( const OUString& aName )
287 OUString aRest;
288 if( aName.startsWith("Children/", &aRest) )
289 return Children->getByHierarchicalName( aRest );
291 return getByName( aName );
294 sal_Bool SAL_CALL
295 TVRead::hasByHierarchicalName( const OUString& aName )
297 OUString aRest;
298 if( aName.startsWith("Children/", &aRest) )
299 return Children->hasByHierarchicalName( aRest );
301 return hasByName( aName );
304 /**************************************************************************/
305 /* */
306 /* TVChildTarget */
307 /* */
308 /**************************************************************************/
310 extern "C" {
312 static void start_handler(void *userData,
313 const XML_Char *name,
314 const XML_Char **atts)
316 TVDom::Kind kind;
318 if( strcmp( name,"help_section" ) == 0 ||
319 strcmp( name,"node" ) == 0 )
320 kind = TVDom::Kind::tree_node;
321 else if( strcmp( name,"topic" ) == 0 )
322 kind = TVDom::Kind::tree_leaf;
323 else
324 return;
326 TVDom **tvDom = static_cast< TVDom** >( userData );
327 TVDom *p;
328 p = *tvDom;
330 *tvDom = p->newChild();
331 p = *tvDom;
333 p->setKind( kind );
334 while( *atts )
336 if( strcmp( *atts,"application" ) == 0 )
337 p->setApplication( *(atts+1) );
338 else if( strcmp( *atts,"title" ) == 0 )
339 p->setTitle( *(atts+1) );
340 else if( strcmp( *atts,"id" ) == 0 )
341 p->setId( *(atts+1) );
342 else if( strcmp( *atts,"anchor" ) == 0 )
343 p->setAnchor( *(atts+1) );
345 atts+=2;
349 static void end_handler(void *userData,
350 SAL_UNUSED_PARAMETER const XML_Char * )
352 TVDom **tvDom = static_cast< TVDom** >( userData );
353 *tvDom = (*tvDom)->getParent();
356 static void data_handler( void *userData,
357 const XML_Char *s,
358 int len)
360 TVDom **tvDom = static_cast< TVDom** >( userData );
361 if( (*tvDom)->isLeaf() )
362 (*tvDom)->setTitle( s,len );
367 TVChildTarget::TVChildTarget( const ConfigData& configData,TVDom* tvDom )
369 Elements.resize( tvDom->children.size() );
370 for( size_t i = 0; i < Elements.size(); ++i )
371 Elements[i] = new TVRead( configData,tvDom->children[i].get() );
374 TVChildTarget::TVChildTarget( const Reference< XComponentContext >& xContext )
376 ConfigData configData = init( xContext );
378 if( configData.locale.isEmpty() || configData.system.isEmpty() )
379 return;
381 sal_uInt64 ret,len = 0;
382 int j = configData.vFileURL.size();
384 TVDom tvDom;
385 TVDom* pTVDom = &tvDom;
387 while( j )
389 len = configData.vFileLen[--j];
390 std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files
391 osl::File aFile( configData.vFileURL[j] );
392 (void)aFile.open( osl_File_OpenFlag_Read );
393 aFile.read( s.get(),len,ret );
394 aFile.close();
396 XML_Parser parser = XML_ParserCreate( nullptr );
397 XML_SetElementHandler( parser,
398 start_handler,
399 end_handler );
400 XML_SetCharacterDataHandler( parser,
401 data_handler);
402 XML_SetUserData( parser,&pTVDom ); // does not return this
404 XML_Status const parsed = XML_Parse(parser, s.get(), int(len), j==0);
405 SAL_WARN_IF(XML_STATUS_ERROR == parsed, "xmlhelp",
406 "TVChildTarget::TVChildTarget(): Tree file parsing failed");
408 XML_ParserFree( parser );
410 Check(pTVDom);
412 // now TVDom holds the relevant information
414 Elements.resize( tvDom.children.size() );
415 for( size_t i = 0; i < Elements.size(); ++i )
416 Elements[i] = new TVRead( configData,tvDom.children[i].get() );
419 TVChildTarget::~TVChildTarget()
423 void TVChildTarget::Check(TVDom* tvDom)
425 if (tvDom->children.empty())
427 return;
430 unsigned i = 0;
431 bool h = false;
433 while((i<tvDom->children.size()-1) && (!h))
435 if (((tvDom->children[i])->application == (tvDom->children[tvDom->children.size()-1])->application) &&
436 ((tvDom->children[i])->id == (tvDom->children[tvDom->children.size()-1])->id))
438 TVDom* p = tvDom->children.back().get();
440 for(auto & k : p->children)
442 std::unique_ptr<TVDom> tmp(SearchAndInsert(std::move(k), tvDom->children[i].get()));
443 if (tmp)
445 tvDom->children[i]->newChild(std::move(tmp));
449 tvDom->children.pop_back();
450 h = true;
452 ++i;
456 std::unique_ptr<TVDom>
457 TVChildTarget::SearchAndInsert(std::unique_ptr<TVDom> p, TVDom* tvDom)
459 if (p->isLeaf()) return p;
461 bool h = false;
462 sal_Int32 max = 0;
464 std::vector< std::unique_ptr<TVDom> >::iterator max_It, i;
465 max_It = tvDom->children.begin();
467 sal_Int32 c_int;
468 sal_Int32 p_int = p->id.toInt32();
470 for(i = tvDom->children.begin(); i!=tvDom->children.end(); ++i)
471 if (!((*i)->isLeaf()) &&
472 ((*i)->id.getLength() == p->id.getLength()) &&
473 (p->id.replaceAt((*i)->parent->id.getLength(), p->id.getLength()-(*i)->parent->id.getLength(), "") == (*i)->parent->id)) //prefix check
475 h = true;
476 c_int = (*i)->id.toInt32();
478 if (p_int==c_int)
480 (*(tvDom->children.insert(i+1, std::move(p))))->parent = tvDom;
481 return nullptr;
483 else if(c_int>max && c_int < p_int)
485 max = c_int;
486 max_It = i+1;
489 if (h)
491 (*(tvDom->children.insert(max_It, std::move(p))))->parent = tvDom;
492 return nullptr;
494 else
496 for (auto& child : tvDom->children)
498 p = SearchAndInsert(std::move(p), child.get());
499 if (p == nullptr)
500 break;
502 return p;
506 Any SAL_CALL
507 TVChildTarget::getByName( const OUString& aName )
509 OUString num( aName.copy( 2, aName.getLength()-4 ) );
510 sal_Int32 idx = num.toInt32() - 1;
511 if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
512 throw NoSuchElementException();
514 cppu::OWeakObject* p = Elements[idx].get();
515 return Any( Reference< XInterface >( p ) );
518 Sequence< OUString > SAL_CALL
519 TVChildTarget::getElementNames( )
521 Sequence< OUString > seq( Elements.size() );
522 for( size_t i = 0; i < Elements.size(); ++i )
523 seq[i] = OUString::number( 1+i );
525 return seq;
528 sal_Bool SAL_CALL
529 TVChildTarget::hasByName( const OUString& aName )
531 OUString num( aName.copy( 2, aName.getLength()-4 ) );
532 sal_Int32 idx = num.toInt32() - 1;
533 if( idx < 0 || Elements.size() <= o3tl::make_unsigned( idx ) )
534 return false;
536 return true;
539 // XHierarchicalNameAccess
541 Any SAL_CALL
542 TVChildTarget::getByHierarchicalName( const OUString& aName )
544 sal_Int32 idx;
546 if( ( idx = aName.indexOf( '/' ) ) != -1 )
548 OUString num( aName.copy( 2, idx-4 ) );
549 sal_Int32 pref = num.toInt32() - 1;
551 if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
552 throw NoSuchElementException();
554 return Elements[pref]->getByHierarchicalName( aName.copy( 1 + idx ) );
556 else
557 return getByName( aName );
560 sal_Bool SAL_CALL
561 TVChildTarget::hasByHierarchicalName( const OUString& aName )
563 sal_Int32 idx;
565 if( ( idx = aName.indexOf( '/' ) ) != -1 )
567 OUString num( aName.copy( 2, idx-4 ) );
568 sal_Int32 pref = num.toInt32() - 1;
569 if( pref < 0 || Elements.size() <= o3tl::make_unsigned( pref ) )
570 return false;
572 return Elements[pref]->hasByHierarchicalName( aName.copy( 1 + idx ) );
574 else
575 return hasByName( aName );
578 ConfigData TVChildTarget::init( const Reference< XComponentContext >& xContext )
580 ConfigData configData;
581 Reference< XMultiServiceFactory > sProvider( getConfiguration(xContext) );
583 /**********************************************************************/
584 /* reading Office.Common */
585 /**********************************************************************/
587 Reference< XHierarchicalNameAccess > xHierAccess( getHierAccess( sProvider,
588 "org.openoffice.Office.Common" ) );
589 OUString system( getKey( xHierAccess,"Help/System" ) );
590 bool showBasic( getBooleanKey(xHierAccess,"Help/ShowBasic") );
591 OUString instPath( getKey( xHierAccess,"Path/Current/Help" ) );
592 if( instPath.isEmpty() )
593 // try to determine path from default
594 instPath = "$(instpath)/help";
596 // replace anything like $(instpath);
597 subst( instPath );
599 /**********************************************************************/
600 /* reading setup */
601 /**********************************************************************/
603 xHierAccess = getHierAccess( sProvider,
604 "org.openoffice.Setup" );
606 OUString setupversion( getKey( xHierAccess,"Product/ooSetupVersion" ) );
607 OUString setupextension;
611 Reference< lang::XMultiServiceFactory > xConfigProvider = theDefaultProvider::get( xContext );
613 uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence(
615 {"nodepath", uno::Any(OUString("/org.openoffice.Setup/Product"))}
616 }));
618 // open it
619 uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
620 "com.sun.star.configuration.ConfigurationAccess",
621 lParams) );
623 uno::Reference< container::XNameAccess > xDirectAccess(xCFG, uno::UNO_QUERY);
624 uno::Any aRet = xDirectAccess->getByName("ooSetupExtension");
626 aRet >>= setupextension;
628 catch ( uno::Exception& )
632 OUString productVersion( setupversion + " " + setupextension );
633 OUString locale( getKey( xHierAccess,"L10N/ooLocale" ) );
635 // Determine fileurl from url and locale
636 OUString url;
637 osl::FileBase::RC errFile = osl::FileBase::getFileURLFromSystemPath( instPath,url );
638 if( errFile != osl::FileBase::E_None ) return configData;
639 if( !url.endsWith("/") )
640 url += "/";
641 OUString ret;
642 sal_Int32 idx;
643 osl::DirectoryItem aDirItem;
644 if( osl::FileBase::E_None == osl::DirectoryItem::get( url + locale,aDirItem ) )
645 ret = locale;
646 else if( ( ( idx = locale.indexOf( '-' ) ) != -1 ||
647 ( idx = locale.indexOf( '_' ) ) != -1 ) &&
648 osl::FileBase::E_None == osl::DirectoryItem::get( url + locale.subView( 0,idx ),
649 aDirItem ) )
650 ret = locale.copy( 0,idx );
651 else
653 locale = "en-US";
654 ret = "en";
656 url += ret;
658 // first of all, try do determine whether there are any *.tree files present
660 // Start with extensions to set them at the end of the list
661 TreeFileIterator aTreeIt( locale );
662 OUString aTreeFile;
663 sal_Int32 nFileSize;
664 for (;;)
666 aTreeFile = aTreeIt.nextTreeFile( nFileSize );
667 if( aTreeFile.isEmpty() )
668 break;
669 configData.vFileLen.push_back( nFileSize );
670 configData.vFileURL.push_back( aTreeFile );
673 osl::Directory aDirectory( url );
674 osl::FileStatus aFileStatus(
675 osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_FileURL );
676 if( osl::Directory::E_None == aDirectory.open() )
678 OUString aFileUrl, aFileName;
679 while( aDirectory.getNextItem( aDirItem ) == osl::FileBase::E_None &&
680 aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
681 aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) &&
682 aFileStatus.isValid( osl_FileStatus_Mask_FileName ) )
684 aFileUrl = aFileStatus.getFileURL();
685 aFileName = aFileStatus.getFileName();
686 int idx_ = aFileName.lastIndexOf( '.' );
687 if( idx_ == -1 )
688 continue;
690 const sal_Unicode* str = aFileName.getStr();
692 if( aFileName.getLength() == idx_ + 5 &&
693 ( str[idx_ + 1] == 't' || str[idx_ + 1] == 'T' ) &&
694 ( str[idx_ + 2] == 'r' || str[idx_ + 2] == 'R' ) &&
695 ( str[idx_ + 3] == 'e' || str[idx_ + 3] == 'E' ) &&
696 ( str[idx_ + 4] == 'e' || str[idx_ + 4] == 'E' ) )
698 OUString baseName = aFileName.copy(0,idx_).toAsciiLowerCase();
699 if(! showBasic && baseName == "sbasic" )
700 continue;
701 osl::File aFile( aFileUrl );
702 if( osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) )
704 // use the file size, not aFileStatus size, in case the
705 // tree file is a symlink
706 sal_uInt64 nSize;
707 aFile.getSize( nSize );
708 configData.vFileLen.push_back( nSize );
709 configData.vFileURL.push_back( aFileUrl );
710 aFile.close();
714 aDirectory.close();
717 configData.m_vAdd[0] = 12;
718 configData.m_vAdd[1] = 15;
719 configData.m_vAdd[2] = 11;
720 configData.m_vAdd[3] = 14;
721 configData.m_vAdd[4] = 12;
722 configData.m_vReplacement[0] = utl::ConfigManager::getProductName();
723 configData.m_vReplacement[1] = productVersion;
724 // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
726 configData.system = system;
727 configData.locale = locale;
728 configData.appendix =
729 "?Language=" +
730 configData.locale +
731 "&System=" +
732 configData.system +
733 "&UseDB=no";
735 return configData;
738 Reference< XMultiServiceFactory >
739 TVChildTarget::getConfiguration(const Reference< XComponentContext >& rxContext)
741 Reference< XMultiServiceFactory > xProvider;
742 if( rxContext.is() )
746 xProvider = theDefaultProvider::get( rxContext );
748 catch( const css::uno::Exception& )
750 OSL_ENSURE( xProvider.is(),"can not instantiate configuration" );
754 return xProvider;
757 Reference< XHierarchicalNameAccess >
758 TVChildTarget::getHierAccess( const Reference< XMultiServiceFactory >& sProvider,
759 const char* file )
761 Reference< XHierarchicalNameAccess > xHierAccess;
763 if( sProvider.is() )
765 Sequence< Any > seq(1);
766 seq[0] <<= OUString::createFromAscii( file );
770 xHierAccess =
771 Reference< XHierarchicalNameAccess >
772 ( sProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", seq ),
773 UNO_QUERY );
775 catch( const css::uno::Exception& )
780 return xHierAccess;
783 OUString
784 TVChildTarget::getKey( const Reference< XHierarchicalNameAccess >& xHierAccess,
785 const char* key )
787 OUString instPath;
788 if( xHierAccess.is() )
790 Any aAny;
793 aAny =
794 xHierAccess->getByHierarchicalName( OUString::createFromAscii( key ) );
796 catch( const css::container::NoSuchElementException& )
799 aAny >>= instPath;
801 return instPath;
804 bool
805 TVChildTarget::getBooleanKey(const Reference<
806 XHierarchicalNameAccess >& xHierAccess,
807 const char* key)
809 bool ret = false;
810 if( xHierAccess.is() )
812 Any aAny;
815 aAny =
816 xHierAccess->getByHierarchicalName(
817 OUString::createFromAscii(key));
819 catch( const css::container::NoSuchElementException& )
822 aAny >>= ret;
824 return ret;
827 void TVChildTarget::subst( OUString& instpath )
829 SvtPathOptions aOptions;
830 instpath = aOptions.SubstituteVariable( instpath );
834 const char aHelpMediaType[] = "application/vnd.sun.star.help";
836 TreeFileIterator::TreeFileIterator( const OUString& aLanguage )
837 : m_eState( IteratorState::UserExtensions )
838 , m_aLanguage( aLanguage )
840 m_xContext = ::comphelper::getProcessComponentContext();
841 if( !m_xContext.is() )
843 throw RuntimeException( "TreeFileIterator::TreeFileIterator(), no XComponentContext" );
846 m_xSFA = ucb::SimpleFileAccess::create(m_xContext);
848 m_bUserPackagesLoaded = false;
849 m_bSharedPackagesLoaded = false;
850 m_bBundledPackagesLoaded = false;
851 m_iUserPackage = 0;
852 m_iSharedPackage = 0;
853 m_iBundledPackage = 0;
856 Reference< deployment::XPackage > TreeFileIterator::implGetHelpPackageFromPackage
857 ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle )
859 o_xParentPackageBundle.clear();
861 Reference< deployment::XPackage > xHelpPackage;
862 if( !xPackage.is() )
863 return xHelpPackage;
865 // Check if parent package is registered
866 beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered
867 ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) );
868 bool bRegistered = false;
869 if( option.IsPresent )
871 beans::Ambiguous<sal_Bool> const & reg = option.Value;
872 if( !reg.IsAmbiguous && reg.Value )
873 bRegistered = true;
875 if( !bRegistered )
876 return xHelpPackage;
878 if( xPackage->isBundle() )
880 Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle
881 ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() );
882 auto pSubPkg = std::find_if(aPkgSeq.begin(), aPkgSeq.end(),
883 [](const Reference< deployment::XPackage >& xSubPkg) {
884 const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType();
885 OUString aMediaType = xPackageTypeInfo->getMediaType();
886 return aMediaType == aHelpMediaType;
888 if (pSubPkg != aPkgSeq.end())
890 xHelpPackage = *pSubPkg;
891 o_xParentPackageBundle = xPackage;
894 else
896 const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
897 OUString aMediaType = xPackageTypeInfo->getMediaType();
898 if( aMediaType == aHelpMediaType )
899 xHelpPackage = xPackage;
902 return xHelpPackage;
905 Reference< deployment::XPackage > TreeFileIterator::implGetNextUserHelpPackage
906 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
908 Reference< deployment::XPackage > xHelpPackage;
910 if( !m_bUserPackagesLoaded )
912 Reference< XPackageManager > xUserManager =
913 thePackageManagerFactory::get( m_xContext )->getPackageManager("user");
914 m_aUserPackagesSeq = xUserManager->getDeployedPackages
915 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
917 m_bUserPackagesLoaded = true;
920 if( m_iUserPackage == m_aUserPackagesSeq.getLength() )
922 m_eState = IteratorState::SharedExtensions; // Later: SHARED_MODULE
924 else
926 const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray();
927 Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ];
928 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextUserHelpPackage(): Invalid package" );
929 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
932 return xHelpPackage;
935 Reference< deployment::XPackage > TreeFileIterator::implGetNextSharedHelpPackage
936 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
938 Reference< deployment::XPackage > xHelpPackage;
940 if( !m_bSharedPackagesLoaded )
942 Reference< XPackageManager > xSharedManager =
943 thePackageManagerFactory::get( m_xContext )->getPackageManager("shared");
944 m_aSharedPackagesSeq = xSharedManager->getDeployedPackages
945 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
947 m_bSharedPackagesLoaded = true;
950 if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() )
952 m_eState = IteratorState::BundledExtensions;
954 else
956 const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray();
957 Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ];
958 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextSharedHelpPackage(): Invalid package" );
959 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
962 return xHelpPackage;
965 Reference< deployment::XPackage > TreeFileIterator::implGetNextBundledHelpPackage
966 ( Reference< deployment::XPackage >& o_xParentPackageBundle )
968 Reference< deployment::XPackage > xHelpPackage;
970 if( !m_bBundledPackagesLoaded )
972 Reference< XPackageManager > xBundledManager =
973 thePackageManagerFactory::get( m_xContext )->getPackageManager("bundled");
974 m_aBundledPackagesSeq = xBundledManager->getDeployedPackages
975 ( Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
977 m_bBundledPackagesLoaded = true;
980 if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() )
982 m_eState = IteratorState::EndReached;
984 else
986 const Reference< deployment::XPackage >* pBundledPackages = m_aBundledPackagesSeq.getConstArray();
987 Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ];
988 OSL_ENSURE( xPackage.is(), "TreeFileIterator::implGetNextBundledHelpPackage(): Invalid package" );
989 xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
992 return xHelpPackage;
995 static bool isLetter( sal_Unicode c )
997 return rtl::isAsciiAlpha(c);
1000 void TreeFileIterator::implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv,
1001 const css::uno::Reference< css::deployment::XPackage >& xPackage )
1003 rv.clear();
1004 OUString aExtensionPath = xPackage->getURL();
1005 const Sequence< OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true );
1007 for( const OUString& aEntry : aEntrySeq )
1009 if( m_xSFA->isFolder( aEntry ) )
1011 sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' );
1012 if( nLastSlash != -1 )
1014 OUString aPureEntry = aEntry.copy( nLastSlash + 1 );
1016 // Check language scheme
1017 int nLen = aPureEntry.getLength();
1018 const sal_Unicode* pc = aPureEntry.getStr();
1019 bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) );
1020 bool bIsLanguage = bStartCanBeLanguage &&
1021 ( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) );
1022 if( bIsLanguage )
1023 rv.push_back( aPureEntry );
1030 OUString TreeFileIterator::nextTreeFile( sal_Int32& rnFileSize )
1032 OUString aRetFile;
1034 while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached )
1036 switch( m_eState )
1038 case IteratorState::UserExtensions:
1040 Reference< deployment::XPackage > xParentPackageBundle;
1041 Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
1042 if( !xHelpPackage.is() )
1043 break;
1045 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1046 break;
1049 case IteratorState::SharedExtensions:
1051 Reference< deployment::XPackage > xParentPackageBundle;
1052 Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
1053 if( !xHelpPackage.is() )
1054 break;
1056 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1057 break;
1059 case IteratorState::BundledExtensions:
1061 Reference< deployment::XPackage > xParentPackageBundle;
1062 Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
1063 if( !xHelpPackage.is() )
1064 break;
1066 aRetFile = implGetTreeFileFromPackage( rnFileSize, xHelpPackage );
1067 break;
1070 case IteratorState::EndReached:
1071 OSL_FAIL( "DataBaseIterator::nextTreeFile(): Invalid case IteratorState::EndReached" );
1072 break;
1076 return aRetFile;
1079 OUString TreeFileIterator::expandURL( const OUString& aURL )
1081 static Reference< util::XMacroExpander > xMacroExpander;
1082 static Reference< uri::XUriReferenceFactory > xFac;
1084 osl::MutexGuard aGuard( m_aMutex );
1086 if( !xMacroExpander.is() || !xFac.is() )
1088 xFac = uri::UriReferenceFactory::create( m_xContext );
1090 xMacroExpander = util::theMacroExpander::get(m_xContext);
1093 OUString aRetURL = aURL;
1094 Reference< uri::XUriReference > uriRef;
1095 for (;;)
1097 uriRef = xFac->parse( aRetURL );
1098 if ( uriRef.is() )
1100 Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
1101 if( !sxUri.is() )
1102 break;
1104 aRetURL = sxUri->expand( xMacroExpander );
1107 return aRetURL;
1110 OUString TreeFileIterator::implGetTreeFileFromPackage
1111 ( sal_Int32& rnFileSize, const Reference< deployment::XPackage >& xPackage )
1113 OUString aRetFile;
1114 OUString aLanguage = m_aLanguage;
1115 for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
1117 aRetFile = expandURL( xPackage->getURL() + "/" + aLanguage + "/help.tree" );
1118 if( iPass == 0 )
1120 if( m_xSFA->exists( aRetFile ) )
1121 break;
1123 ::std::vector< OUString > av;
1124 implGetLanguageVectorFromPackage( av, xPackage );
1125 ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage );
1126 if( pFound != av.end() )
1127 aLanguage = *pFound;
1131 rnFileSize = 0;
1132 if( m_xSFA->exists( aRetFile ) )
1133 rnFileSize = m_xSFA->getSize( aRetFile );
1134 else
1135 aRetFile.clear();
1137 return aRetFile;
1140 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */