1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "dp_registry.hrc"
23 #include "dp_resource.h"
24 #include "dp_interact.h"
26 #include "osl/diagnose.h"
27 #include "rtl/ustrbuf.hxx"
28 #include "rtl/uri.hxx"
29 #include "cppuhelper/compbase2.hxx"
30 #include "cppuhelper/exc_hlp.hxx"
31 #include "comphelper/sequence.hxx"
32 #include "ucbhelper/content.hxx"
33 #include "com/sun/star/uno/DeploymentException.hpp"
34 #include "com/sun/star/lang/DisposedException.hpp"
35 #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp"
36 #include "com/sun/star/lang/XServiceInfo.hpp"
37 #include "com/sun/star/lang/XSingleComponentFactory.hpp"
38 #include "com/sun/star/lang/XSingleServiceFactory.hpp"
39 #include "com/sun/star/util/XUpdatable.hpp"
40 #include "com/sun/star/container/XContentEnumerationAccess.hpp"
41 #include "com/sun/star/deployment/PackageRegistryBackend.hpp"
42 #include <boost/unordered_map.hpp>
44 #include <boost/unordered_set.hpp>
47 using namespace ::dp_misc
;
48 using namespace ::com::sun::star
;
49 using namespace ::com::sun::star::uno
;
50 using namespace ::com::sun::star::ucb
;
53 namespace dp_registry
{
57 Reference
<deployment::XPackageRegistry
> create(
58 Reference
<deployment::XPackageRegistry
> const & xRootRegistry
,
59 OUString
const & context
, OUString
const & cachePath
, bool readOnly
,
60 Reference
<XComponentContext
> const & xComponentContext
);
66 typedef ::cppu::WeakComponentImplHelper2
<
67 deployment::XPackageRegistry
, util::XUpdatable
> t_helper
;
69 //==============================================================================
70 class PackageRegistryImpl
: private MutexHolder
, public t_helper
72 struct ci_string_hash
{
73 ::std::size_t operator () ( OUString
const & str
) const {
74 return str
.toAsciiLowerCase().hashCode();
77 struct ci_string_equals
{
78 bool operator () ( OUString
const & str1
, OUString
const & str2
) const{
79 return str1
.equalsIgnoreAsciiCase( str2
);
82 typedef ::boost::unordered_map
<
83 OUString
, Reference
<deployment::XPackageRegistry
>,
84 ci_string_hash
, ci_string_equals
> t_string2registry
;
85 typedef ::boost::unordered_map
<
87 ci_string_hash
, ci_string_equals
> t_string2string
;
89 Reference
<deployment::XPackageRegistry
> > t_registryset
;
91 t_string2registry m_mediaType2backend
;
92 t_string2string m_filter2mediaType
;
93 t_registryset m_ambiguousBackends
;
94 t_registryset m_allBackends
;
95 ::std::vector
< Reference
<deployment::XPackageTypeInfo
> > m_typesInfos
;
98 Reference
<deployment::XPackageRegistry
> const & xBackend
);
102 virtual void SAL_CALL
disposing();
104 virtual ~PackageRegistryImpl();
105 PackageRegistryImpl() : t_helper( getMutex() ) {}
109 static Reference
<deployment::XPackageRegistry
> create(
110 OUString
const & context
,
111 OUString
const & cachePath
, bool readOnly
,
112 Reference
<XComponentContext
> const & xComponentContext
);
115 virtual void SAL_CALL
update() throw (RuntimeException
);
118 virtual Reference
<deployment::XPackage
> SAL_CALL
bindPackage(
119 OUString
const & url
, OUString
const & mediaType
, sal_Bool bRemoved
,
120 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
121 throw (deployment::DeploymentException
,
122 deployment::InvalidRemovedParameterException
,
123 CommandFailedException
,
124 lang::IllegalArgumentException
, RuntimeException
);
125 virtual Sequence
< Reference
<deployment::XPackageTypeInfo
> > SAL_CALL
126 getSupportedPackageTypes() throw (RuntimeException
);
127 virtual void SAL_CALL
packageRemoved(OUString
const & url
, OUString
const & mediaType
)
128 throw (deployment::DeploymentException
,
133 //______________________________________________________________________________
134 inline void PackageRegistryImpl::check()
136 ::osl::MutexGuard
guard( getMutex() );
137 if (rBHelper
.bInDispose
|| rBHelper
.bDisposed
) {
138 throw lang::DisposedException(
139 "PackageRegistry instance has already been disposed!",
140 static_cast<OWeakObject
*>(this) );
144 //______________________________________________________________________________
145 void PackageRegistryImpl::disposing()
147 // dispose all backends:
148 t_registryset::const_iterator
iPos( m_allBackends
.begin() );
149 t_registryset::const_iterator
const iEnd( m_allBackends
.end() );
150 for ( ; iPos
!= iEnd
; ++iPos
) {
151 try_dispose( *iPos
);
153 m_mediaType2backend
= t_string2registry();
154 m_ambiguousBackends
= t_registryset();
155 m_allBackends
= t_registryset();
157 t_helper::disposing();
160 //______________________________________________________________________________
161 PackageRegistryImpl::~PackageRegistryImpl()
165 //______________________________________________________________________________
166 OUString
normalizeMediaType( OUString
const & mediaType
)
171 buf
.append( mediaType
.getToken( 0, '/', index
).trim() );
174 buf
.append( static_cast< sal_Unicode
>('/') );
176 return buf
.makeStringAndClear();
179 //______________________________________________________________________________
181 void PackageRegistryImpl::packageRemoved(
182 OUString
const & url
, OUString
const & mediaType
)
183 throw (css::deployment::DeploymentException
,
184 css::uno::RuntimeException
)
186 const t_string2registry::const_iterator i
=
187 m_mediaType2backend
.find(mediaType
);
189 if (i
!= m_mediaType2backend
.end())
191 i
->second
->packageRemoved(url
, mediaType
);
195 void PackageRegistryImpl::insertBackend(
196 Reference
<deployment::XPackageRegistry
> const & xBackend
)
198 m_allBackends
.insert( xBackend
);
199 typedef ::boost::unordered_set
<OUString
, OUStringHash
> t_stringset
;
200 t_stringset ambiguousFilters
;
202 const Sequence
< Reference
<deployment::XPackageTypeInfo
> > packageTypes(
203 xBackend
->getSupportedPackageTypes() );
204 for ( sal_Int32 pos
= 0; pos
< packageTypes
.getLength(); ++pos
)
206 Reference
<deployment::XPackageTypeInfo
> const & xPackageType
=
208 m_typesInfos
.push_back( xPackageType
);
210 const OUString
mediaType( normalizeMediaType(
211 xPackageType
->getMediaType() ) );
212 ::std::pair
<t_string2registry::iterator
, bool> mb_insertion(
213 m_mediaType2backend
.insert( t_string2registry::value_type(
214 mediaType
, xBackend
) ) );
215 if (mb_insertion
.second
)
217 // add parameterless media-type, too:
218 sal_Int32 semi
= mediaType
.indexOf( ';' );
220 m_mediaType2backend
.insert(
221 t_string2registry::value_type(
222 mediaType
.copy( 0, semi
), xBackend
) );
224 const OUString
fileFilter( xPackageType
->getFileFilter() );
225 //The package backend shall also be called to determine the mediatype
226 //(XPackageRegistry.bindPackage) when the URL points to a directory.
227 const bool bExtension
= (mediaType
== "application/vnd.sun.star.package-bundle");
228 if (fileFilter
.isEmpty() || fileFilter
== "*.*" || fileFilter
== "*" || bExtension
)
230 m_ambiguousBackends
.insert( xBackend
);
234 sal_Int32 nIndex
= 0;
236 OUString
token( fileFilter
.getToken( 0, ';', nIndex
) );
237 if (token
.match( "*." ))
238 token
= token
.copy( 1 );
241 // mark any further wildcards ambig:
242 bool ambig
= (token
.indexOf('*') >= 0 ||
243 token
.indexOf('?') >= 0);
245 ::std::pair
<t_string2string::iterator
, bool> ins(
246 m_filter2mediaType
.insert(
247 t_string2string::value_type(
248 token
, mediaType
) ) );
251 // filter has already been in: add previously
252 // added backend to ambig set
253 const t_string2registry::const_iterator
iFind(
254 m_mediaType2backend
.find(
255 /* media-type of pr. added backend */
256 ins
.first
->second
) );
258 iFind
!= m_mediaType2backend
.end() );
259 if (iFind
!= m_mediaType2backend
.end())
260 m_ambiguousBackends
.insert( iFind
->second
);
264 m_ambiguousBackends
.insert( xBackend
);
265 // mark filter to be removed later from filters map:
266 ambiguousFilters
.insert( token
);
272 #if OSL_DEBUG_LEVEL > 0
276 buf
.appendAscii( "more than one PackageRegistryBackend for media-type=\"" );
277 buf
.append( mediaType
);
278 buf
.appendAscii( "\" => " );
279 buf
.append( Reference
<lang::XServiceInfo
>(
280 xBackend
, UNO_QUERY_THROW
)->
281 getImplementationName() );
282 buf
.appendAscii( "\"!" );
283 OSL_FAIL( OUStringToOString(
284 buf
.makeStringAndClear(),
285 RTL_TEXTENCODING_UTF8
).getStr() );
290 // cut out ambiguous filters:
291 t_stringset::const_iterator
iPos( ambiguousFilters
.begin() );
292 const t_stringset::const_iterator
iEnd( ambiguousFilters
.end() );
293 for ( ; iPos
!= iEnd
; ++iPos
) {
294 m_filter2mediaType
.erase( *iPos
);
298 //______________________________________________________________________________
299 Reference
<deployment::XPackageRegistry
> PackageRegistryImpl::create(
300 OUString
const & context
,
301 OUString
const & cachePath
, bool readOnly
,
302 Reference
<XComponentContext
> const & xComponentContext
)
304 PackageRegistryImpl
* that
= new PackageRegistryImpl
;
305 Reference
<deployment::XPackageRegistry
> xRet(that
);
307 // auto-detect all registered package registries:
308 Reference
<container::XEnumeration
> xEnum(
309 Reference
<container::XContentEnumerationAccess
>(
310 xComponentContext
->getServiceManager(),
311 UNO_QUERY_THROW
)->createContentEnumeration(
312 "com.sun.star.deployment.PackageRegistryBackend" ) );
315 while (xEnum
->hasMoreElements())
317 Any
element( xEnum
->nextElement() );
318 Sequence
<Any
> registryArgs(cachePath
.isEmpty() ? 1 : 3 );
319 registryArgs
[ 0 ] <<= context
;
320 if (!cachePath
.isEmpty())
322 Reference
<lang::XServiceInfo
> xServiceInfo(
323 element
, UNO_QUERY_THROW
);
324 OUString
registryCachePath(
327 xServiceInfo
->getImplementationName(),
328 rtl_UriCharClassPchar
,
329 rtl_UriEncodeIgnoreEscapes
,
330 RTL_TEXTENCODING_UTF8
) ) );
331 registryArgs
[ 1 ] <<= registryCachePath
;
332 registryArgs
[ 2 ] <<= readOnly
;
334 create_folder( 0, registryCachePath
,
335 Reference
<XCommandEnvironment
>() );
338 Reference
<deployment::XPackageRegistry
> xBackend
;
339 Reference
<lang::XSingleComponentFactory
> xFac( element
, UNO_QUERY
);
342 xFac
->createInstanceWithArgumentsAndContext(
343 registryArgs
, xComponentContext
), UNO_QUERY
);
346 Reference
<lang::XSingleServiceFactory
> xSingleServiceFac(
347 element
, UNO_QUERY_THROW
);
349 xSingleServiceFac
->createInstanceWithArguments(
350 registryArgs
), UNO_QUERY
);
352 if (! xBackend
.is()) {
353 throw DeploymentException(
354 "cannot instantiate PackageRegistryBackend service: "
355 + Reference
<lang::XServiceInfo
>(
356 element
, UNO_QUERY_THROW
)->getImplementationName(),
357 static_cast<OWeakObject
*>(that
) );
360 that
->insertBackend( xBackend
);
364 // Insert bundle back-end.
365 // Always register as last, because we want to add extensions also as folders
366 // and as a default we accept every folder, which was not recognized by the other
368 Reference
<deployment::XPackageRegistry
> extensionBackend
=
369 ::dp_registry::backend::bundle::create(
370 that
, context
, cachePath
, readOnly
, xComponentContext
);
371 that
->insertBackend(extensionBackend
);
373 Reference
<lang::XServiceInfo
> xServiceInfo(
374 extensionBackend
, UNO_QUERY_THROW
);
376 OSL_ASSERT(xServiceInfo
.is());
377 OUString
registryCachePath(
380 xServiceInfo
->getImplementationName(),
381 rtl_UriCharClassPchar
,
382 rtl_UriEncodeIgnoreEscapes
,
383 RTL_TEXTENCODING_UTF8
) ) );
384 create_folder( 0, registryCachePath
, Reference
<XCommandEnvironment
>());
387 #if OSL_DEBUG_LEVEL > 1
390 t_registryset allBackends
;
391 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
392 for ( t_string2string::const_iterator
iPos(
393 that
->m_filter2mediaType
.begin() );
394 iPos
!= that
->m_filter2mediaType
.end(); ++iPos
)
397 buf
.appendAscii( "extension \"" );
398 buf
.append( iPos
->first
);
399 buf
.appendAscii( "\" maps to media-type \"" );
400 buf
.append( iPos
->second
);
401 buf
.appendAscii( "\" maps to backend " );
402 const Reference
<deployment::XPackageRegistry
> xBackend(
403 that
->m_mediaType2backend
.find( iPos
->second
)->second
);
404 allBackends
.insert( xBackend
);
405 buf
.append( Reference
<lang::XServiceInfo
>(
406 xBackend
, UNO_QUERY_THROW
)
407 ->getImplementationName() );
408 dp_misc::writeConsole( buf
.makeStringAndClear() + "\n");
410 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
411 for ( t_registryset::const_iterator
iPos(
412 that
->m_ambiguousBackends
.begin() );
413 iPos
!= that
->m_ambiguousBackends
.end(); ++iPos
)
417 Reference
<lang::XServiceInfo
>(
418 *iPos
, UNO_QUERY_THROW
)->getImplementationName() );
419 buf
.appendAscii( ": " );
420 const Sequence
< Reference
<deployment::XPackageTypeInfo
> > types(
421 (*iPos
)->getSupportedPackageTypes() );
422 for ( sal_Int32 pos
= 0; pos
< types
.getLength(); ++pos
) {
423 Reference
<deployment::XPackageTypeInfo
> const & xInfo
=
425 buf
.append( xInfo
->getMediaType() );
426 const OUString
filter( xInfo
->getFileFilter() );
427 if (!filter
.isEmpty()) {
428 buf
.appendAscii( " (" );
429 buf
.append( filter
);
430 buf
.appendAscii( ")" );
432 if (pos
< (types
.getLength() - 1))
433 buf
.appendAscii( ", " );
435 dp_misc::TRACE(buf
.makeStringAndClear() + "\n\n");
437 allBackends
.insert( that
->m_ambiguousBackends
.begin(),
438 that
->m_ambiguousBackends
.end() );
439 OSL_ASSERT( allBackends
== that
->m_allBackends
);
446 // XUpdatable: broadcast to backends
447 //______________________________________________________________________________
448 void PackageRegistryImpl::update() throw (RuntimeException
)
451 t_registryset::const_iterator
iPos( m_allBackends
.begin() );
452 const t_registryset::const_iterator
iEnd( m_allBackends
.end() );
453 for ( ; iPos
!= iEnd
; ++iPos
) {
454 const Reference
<util::XUpdatable
> xUpdatable( *iPos
, UNO_QUERY
);
456 xUpdatable
->update();
461 //______________________________________________________________________________
462 Reference
<deployment::XPackage
> PackageRegistryImpl::bindPackage(
463 OUString
const & url
, OUString
const & mediaType_
, sal_Bool bRemoved
,
464 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
465 throw (deployment::DeploymentException
, deployment::InvalidRemovedParameterException
,
466 CommandFailedException
,
467 lang::IllegalArgumentException
, RuntimeException
)
470 OUString
mediaType(mediaType_
);
471 if (mediaType
.isEmpty())
473 ::ucbhelper::Content ucbContent
;
474 if (create_ucb_content(
475 &ucbContent
, url
, xCmdEnv
, false /* no throw */ )
476 && !ucbContent
.isFolder())
478 OUString
title( StrTitle::getTitle( ucbContent
) );
481 const t_string2string::const_iterator
iFind(
482 m_filter2mediaType
.find(title
) );
483 if (iFind
!= m_filter2mediaType
.end()) {
484 mediaType
= iFind
->second
;
487 sal_Int32 point
= title
.indexOf( '.', 1 /* consume . */ );
490 title
= title
.copy(point
);
494 if (mediaType
.isEmpty())
496 // try ambiguous backends:
497 t_registryset::const_iterator
iPos( m_ambiguousBackends
.begin() );
498 const t_registryset::const_iterator
iEnd( m_ambiguousBackends
.end() );
499 for ( ; iPos
!= iEnd
; ++iPos
)
502 return (*iPos
)->bindPackage( url
, mediaType
, bRemoved
,
503 identifier
, xCmdEnv
);
505 catch (const lang::IllegalArgumentException
&) {
508 throw lang::IllegalArgumentException(
509 getResourceString(RID_STR_CANNOT_DETECT_MEDIA_TYPE
) + url
,
510 static_cast<OWeakObject
*>(this), static_cast<sal_Int16
>(-1) );
514 // get backend by media-type:
515 t_string2registry::const_iterator
iFind(
516 m_mediaType2backend
.find( normalizeMediaType(mediaType
) ) );
517 if (iFind
== m_mediaType2backend
.end()) {
518 // xxx todo: more sophisticated media-type argument parsing...
519 sal_Int32 q
= mediaType
.indexOf( ';' );
521 iFind
= m_mediaType2backend
.find(
524 mediaType
.copy( 0, q
) ) );
527 if (iFind
== m_mediaType2backend
.end()) {
528 throw lang::IllegalArgumentException(
529 getResourceString(RID_STR_UNSUPPORTED_MEDIA_TYPE
) + mediaType
,
530 static_cast<OWeakObject
*>(this), static_cast<sal_Int16
>(-1) );
532 return iFind
->second
->bindPackage( url
, mediaType
, bRemoved
,
533 identifier
, xCmdEnv
);
537 //______________________________________________________________________________
538 Sequence
< Reference
<deployment::XPackageTypeInfo
> >
539 PackageRegistryImpl::getSupportedPackageTypes() throw (RuntimeException
)
541 return comphelper::containerToSequence(m_typesInfos
);
545 //==============================================================================
546 Reference
<deployment::XPackageRegistry
> SAL_CALL
create(
547 OUString
const & context
,
548 OUString
const & cachePath
, bool readOnly
,
549 Reference
<XComponentContext
> const & xComponentContext
)
551 return PackageRegistryImpl::create(
552 context
, cachePath
, readOnly
, xComponentContext
);
555 } // namespace dp_registry
557 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */