tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / desktop / source / deployment / registry / dp_registry.cxx
blob6e9a4e9f1c2c9f4d57760b1bff53da0513e56567
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 <sal/config.h>
21 #include <sal/log.hxx>
23 #include <dp_shared.hxx>
24 #include <dp_package.hxx>
25 #include <strings.hrc>
26 #include <dp_registry.hxx>
27 #include <dp_misc.h>
28 #include <dp_ucb.h>
29 #include <osl/diagnose.h>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/uri.hxx>
32 #include <cppuhelper/basemutex.hxx>
33 #include <cppuhelper/compbase.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <ucbhelper/content.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <com/sun/star/ucb/ContentCreationException.hpp>
38 #include <com/sun/star/uno/DeploymentException.hpp>
39 #include <com/sun/star/lang/DisposedException.hpp>
40 #include <com/sun/star/lang/IllegalArgumentException.hpp>
41 #include <com/sun/star/lang/XServiceInfo.hpp>
42 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
43 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
44 #include <com/sun/star/util/XUpdatable.hpp>
45 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
46 #include <com/sun/star/deployment/XPackageTypeInfo.hpp>
47 #include <com/sun/star/deployment/XPackageRegistry.hpp>
48 #include <set>
49 #include <string_view>
50 #include <unordered_map>
51 #include <unordered_set>
53 using namespace ::dp_misc;
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::ucb;
59 namespace dp_registry {
61 namespace {
63 typedef ::cppu::WeakComponentImplHelper<
64 deployment::XPackageRegistry, util::XUpdatable > t_helper;
67 class PackageRegistryImpl : private cppu::BaseMutex, public t_helper
69 struct ci_string_hash {
70 std::size_t operator () ( OUString const & str ) const {
71 return str.toAsciiLowerCase().hashCode();
74 struct ci_string_equals {
75 bool operator () ( std::u16string_view str1, std::u16string_view str2 ) const{
76 return o3tl::equalsIgnoreAsciiCase( str1, str2 );
79 typedef std::unordered_map<
80 OUString, Reference<deployment::XPackageRegistry>,
81 ci_string_hash, ci_string_equals > t_string2registry;
82 typedef std::unordered_map<
83 OUString, OUString,
84 ci_string_hash, ci_string_equals > t_string2string;
85 typedef std::set<
86 Reference<deployment::XPackageRegistry> > t_registryset;
88 t_string2registry m_mediaType2backend;
89 t_string2string m_filter2mediaType;
90 t_registryset m_ambiguousBackends;
91 t_registryset m_allBackends;
92 std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
94 void insertBackend(
95 Reference<deployment::XPackageRegistry> const & xBackend );
97 protected:
98 void check();
99 virtual void SAL_CALL disposing() override;
101 virtual ~PackageRegistryImpl() override;
102 PackageRegistryImpl() : t_helper( m_aMutex ) {}
105 public:
106 static Reference<deployment::XPackageRegistry> create(
107 OUString const & context,
108 OUString const & cachePath,
109 Reference<XComponentContext> const & xComponentContext );
111 // XUpdatable
112 virtual void SAL_CALL update() override;
114 // XPackageRegistry
115 virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
116 OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
117 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
118 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
119 getSupportedPackageTypes() override;
120 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
125 void PackageRegistryImpl::check()
127 ::osl::MutexGuard guard( m_aMutex );
128 if (rBHelper.bInDispose || rBHelper.bDisposed) {
129 throw lang::DisposedException(
130 u"PackageRegistry instance has already been disposed!"_ustr,
131 static_cast<OWeakObject *>(this) );
136 void PackageRegistryImpl::disposing()
138 // dispose all backends:
139 for (auto const& backend : m_allBackends)
141 try_dispose(backend);
143 m_mediaType2backend = t_string2registry();
144 m_ambiguousBackends = t_registryset();
145 m_allBackends = t_registryset();
147 t_helper::disposing();
151 PackageRegistryImpl::~PackageRegistryImpl()
156 OUString normalizeMediaType( std::u16string_view mediaType )
158 OUStringBuffer buf;
159 sal_Int32 index = 0;
160 for (;;) {
161 buf.append( o3tl::trim(o3tl::getToken(mediaType, 0, '/', index )) );
162 if (index < 0)
163 break;
164 buf.append( '/' );
166 return buf.makeStringAndClear();
170 void PackageRegistryImpl::packageRemoved(
171 OUString const & url, OUString const & mediaType)
173 const t_string2registry::const_iterator i =
174 m_mediaType2backend.find(mediaType);
176 if (i != m_mediaType2backend.end())
178 i->second->packageRemoved(url, mediaType);
182 void PackageRegistryImpl::insertBackend(
183 Reference<deployment::XPackageRegistry> const & xBackend )
185 m_allBackends.insert( xBackend );
186 std::unordered_set<OUString> ambiguousFilters;
188 const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
189 xBackend->getSupportedPackageTypes() );
190 for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
192 m_typesInfos.push_back( xPackageType );
194 const OUString mediaType( normalizeMediaType(
195 xPackageType->getMediaType() ) );
196 std::pair<t_string2registry::iterator, bool> a_insertion(
197 m_mediaType2backend.emplace( mediaType, xBackend ) );
198 if (a_insertion.second)
200 // add parameterless media-type, too:
201 sal_Int32 semi = mediaType.indexOf( ';' );
202 if (semi >= 0) {
203 m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
205 const OUString fileFilter( xPackageType->getFileFilter() );
206 //The package backend shall also be called to determine the mediatype
207 //(XPackageRegistry.bindPackage) when the URL points to a directory.
208 const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
209 if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
211 m_ambiguousBackends.insert( xBackend );
213 else
215 sal_Int32 nIndex = 0;
216 do {
217 OUString token( fileFilter.getToken( 0, ';', nIndex ) );
218 if (token.match( "*." ))
219 token = token.copy( 1 );
220 if (token.isEmpty())
221 continue;
222 // mark any further wildcards ambig:
223 bool ambig = (token.indexOf('*') >= 0 ||
224 token.indexOf('?') >= 0);
225 if (! ambig) {
226 std::pair<t_string2string::iterator, bool> ins(
227 m_filter2mediaType.emplace(
228 token, mediaType ) );
229 ambig = !ins.second;
230 if (ambig) {
231 // filter has already been in: add previously
232 // added backend to ambig set
233 const t_string2registry::const_iterator iFind(
234 m_mediaType2backend.find(
235 /* media-type of pr. added backend */
236 ins.first->second ) );
237 OSL_ASSERT(
238 iFind != m_mediaType2backend.end() );
239 if (iFind != m_mediaType2backend.end())
240 m_ambiguousBackends.insert( iFind->second );
243 if (ambig) {
244 m_ambiguousBackends.insert( xBackend );
245 // mark filter to be removed later from filters map:
246 ambiguousFilters.insert( token );
249 while (nIndex >= 0);
252 #if OSL_DEBUG_LEVEL > 0
253 else
255 SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
256 << mediaType
257 << "\" => "
258 << Reference<lang::XServiceInfo>(
259 xBackend, UNO_QUERY_THROW )->getImplementationName()
260 << "\"!" );
262 #endif
265 // cut out ambiguous filters:
266 for (auto const& ambiguousFilter : ambiguousFilters)
268 m_filter2mediaType.erase(ambiguousFilter);
273 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
274 OUString const & context,
275 OUString const & cachePath,
276 Reference<XComponentContext> const & xComponentContext )
278 rtl::Reference<PackageRegistryImpl> that = new PackageRegistryImpl;
280 // auto-detect all registered package registries:
281 Reference<container::XEnumeration> xEnum(
282 Reference<container::XContentEnumerationAccess>(
283 xComponentContext->getServiceManager(),
284 UNO_QUERY_THROW )->createContentEnumeration(
285 u"com.sun.star.deployment.PackageRegistryBackend"_ustr ) );
286 if (xEnum.is())
288 while (xEnum->hasMoreElements())
290 Any element( xEnum->nextElement() );
291 Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
292 auto pregistryArgs = registryArgs.getArray();
293 pregistryArgs[ 0 ] <<= context;
294 if (!cachePath.isEmpty())
296 Reference<lang::XServiceInfo> xServiceInfo(
297 element, UNO_QUERY_THROW );
298 OUString registryCachePath(
299 makeURL( cachePath,
300 ::rtl::Uri::encode(
301 xServiceInfo->getImplementationName(),
302 rtl_UriCharClassPchar,
303 rtl_UriEncodeIgnoreEscapes,
304 RTL_TEXTENCODING_UTF8 ) ) );
305 pregistryArgs[ 1 ] <<= registryCachePath;
306 pregistryArgs[ 2 ] <<= false; // readOnly;
307 create_folder( nullptr, registryCachePath,
308 Reference<XCommandEnvironment>() );
311 Reference<deployment::XPackageRegistry> xBackend;
312 Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
313 if (xFac.is()) {
314 xBackend.set(
315 xFac->createInstanceWithArgumentsAndContext(
316 registryArgs, xComponentContext ), UNO_QUERY );
318 else {
319 Reference<lang::XSingleServiceFactory> xSingleServiceFac(
320 element, UNO_QUERY_THROW );
321 xBackend.set(
322 xSingleServiceFac->createInstanceWithArguments(
323 registryArgs ), UNO_QUERY );
325 if (! xBackend.is()) {
326 throw DeploymentException(
327 "cannot instantiate PackageRegistryBackend service: "
328 + Reference<lang::XServiceInfo>(
329 element, UNO_QUERY_THROW )->getImplementationName(),
330 static_cast<OWeakObject *>(that.get()) );
333 that->insertBackend( xBackend );
337 // Insert bundle back-end.
338 // Always register as last, because we want to add extensions also as folders
339 // and as a default we accept every folder, which was not recognized by the other
340 // backends.
341 Reference<deployment::XPackageRegistry> extensionBackend =
342 ::dp_registry::backend::bundle::create(
343 that, context, cachePath, xComponentContext);
344 that->insertBackend(extensionBackend);
346 Reference<lang::XServiceInfo> xServiceInfo(
347 extensionBackend, UNO_QUERY_THROW );
349 OSL_ASSERT(xServiceInfo.is());
350 OUString registryCachePath(
351 makeURL( cachePath,
352 ::rtl::Uri::encode(
353 xServiceInfo->getImplementationName(),
354 rtl_UriCharClassPchar,
355 rtl_UriEncodeIgnoreEscapes,
356 RTL_TEXTENCODING_UTF8 ) ) );
357 create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
360 #if OSL_DEBUG_LEVEL > 0
361 // dump tables:
363 t_registryset allBackends;
364 dp_misc::TRACE(u"> [dp_registry.cxx] media-type detection:\n\n"_ustr );
365 for (auto const& elem : that->m_filter2mediaType)
367 const Reference<deployment::XPackageRegistry> xBackend(
368 that->m_mediaType2backend.find( elem.second )->second );
369 allBackends.insert( xBackend );
370 dp_misc::TRACE(
371 "extension \"" + elem.first + "\" maps to media-type \"" + elem.second
372 + "\" maps to backend "
373 + Reference<lang::XServiceInfo>(
374 xBackend, UNO_QUERY_THROW )
375 ->getImplementationName()
376 + "\n");
378 dp_misc::TRACE( u"> [dp_registry.cxx] ambiguous backends:\n\n"_ustr );
379 for (auto const& ambiguousBackend : that->m_ambiguousBackends)
381 OUStringBuffer buf;
382 buf.append(
383 Reference<lang::XServiceInfo>(
384 ambiguousBackend, UNO_QUERY_THROW )->getImplementationName()
385 + ": " );
386 const Sequence< Reference<deployment::XPackageTypeInfo> > types(
387 ambiguousBackend->getSupportedPackageTypes() );
388 for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
389 Reference<deployment::XPackageTypeInfo> const & xInfo =
390 types[ pos ];
391 buf.append( xInfo->getMediaType() );
392 const OUString filter( xInfo->getFileFilter() );
393 if (!filter.isEmpty()) {
394 buf.append( " (" + filter + ")" );
396 if (pos < (types.getLength() - 1))
397 buf.append( ", " );
399 dp_misc::TRACE(buf + "\n\n");
401 allBackends.insert( that->m_ambiguousBackends.begin(),
402 that->m_ambiguousBackends.end() );
403 OSL_ASSERT( allBackends == that->m_allBackends );
405 #endif
407 return that;
410 // XUpdatable: broadcast to backends
412 void PackageRegistryImpl::update()
414 check();
415 for (auto const& backend : m_allBackends)
417 const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
418 if (xUpdatable.is())
419 xUpdatable->update();
423 // XPackageRegistry
425 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
426 OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
427 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
429 check();
430 OUString mediaType(mediaType_);
431 if (mediaType.isEmpty())
433 ::ucbhelper::Content ucbContent;
434 bool bOk=true;
438 bOk = create_ucb_content(
439 &ucbContent, url, xCmdEnv, false /* no throw */ )
440 && !ucbContent.isFolder();
442 catch (const css::ucb::ContentCreationException&)
444 bOk = false;
447 if (bOk)
449 OUString title( StrTitle::getTitle( ucbContent ) );
450 for (;;)
452 const t_string2string::const_iterator iFind(
453 m_filter2mediaType.find(title) );
454 if (iFind != m_filter2mediaType.end()) {
455 mediaType = iFind->second;
456 break;
458 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
459 if (point < 0)
460 break;
461 title = title.copy(point);
465 if (mediaType.isEmpty())
467 // try ambiguous backends:
468 for (auto const& ambiguousBackend : m_ambiguousBackends)
470 try {
471 return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
472 identifier, xCmdEnv );
474 catch (const lang::IllegalArgumentException &) {
477 throw lang::IllegalArgumentException(
478 DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
479 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
481 else
483 // get backend by media-type:
484 t_string2registry::const_iterator iFind(
485 m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
486 if (iFind == m_mediaType2backend.end()) {
487 // xxx todo: more sophisticated media-type argument parsing...
488 sal_Int32 q = mediaType.indexOf( ';' );
489 if (q >= 0) {
490 iFind = m_mediaType2backend.find(
491 normalizeMediaType(
492 // cut parameters:
493 mediaType.subView( 0, q ) ) );
496 if (iFind == m_mediaType2backend.end()) {
497 throw lang::IllegalArgumentException(
498 DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
499 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
501 return iFind->second->bindPackage( url, mediaType, bRemoved,
502 identifier, xCmdEnv );
507 Sequence< Reference<deployment::XPackageTypeInfo> >
508 PackageRegistryImpl::getSupportedPackageTypes()
510 return comphelper::containerToSequence(m_typesInfos);
512 } // anon namespace
515 Reference<deployment::XPackageRegistry> create(
516 OUString const & context,
517 OUString const & cachePath,
518 Reference<XComponentContext> const & xComponentContext )
520 return PackageRegistryImpl::create(
521 context, cachePath, xComponentContext );
524 } // namespace dp_registry
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */