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 /**************************************************************************
23 **************************************************************************
25 *************************************************************************/
26 #include <osl/diagnose.h>
27 #include <sal/log.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/interfacecontainer2.hxx>
31 #include <comphelper/propertysequence.hxx>
32 #include <com/sun/star/lang/IllegalArgumentException.hpp>
33 #include <com/sun/star/ucb/DuplicateProviderException.hpp>
34 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
35 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
36 #include <com/sun/star/ucb/XCommandInfo.hpp>
37 #include <com/sun/star/ucb/XContentProviderSupplier.hpp>
38 #include <com/sun/star/configuration/theDefaultProvider.hpp>
39 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/uno/Any.hxx>
42 #include <ucbhelper/cancelcommandexecution.hxx>
43 #include <ucbhelper/getcomponentcontext.hxx>
44 #include <ucbhelper/macros.hxx>
45 #include "identify.hxx"
46 #include "ucbcmds.hxx"
50 using namespace comphelper
;
51 using namespace com::sun::star::uno
;
52 using namespace com::sun::star::lang
;
53 using namespace com::sun::star::ucb
;
54 using namespace ucb_impl
;
55 using namespace com::sun::star
;
56 using namespace ucbhelper
;
61 bool fillPlaceholders(OUString
const & rInput
,
62 uno::Sequence
< uno::Any
> const & rReplacements
,
65 sal_Unicode
const * p
= rInput
.getStr();
66 sal_Unicode
const * pEnd
= p
+ rInput
.getLength();
67 sal_Unicode
const * pCopy
= p
;
68 OUStringBuffer aBuffer
;
74 && p
[0] == 'a' && p
[1] == 'm' && p
[2] == 'p'
77 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
82 else if (pEnd
- p
>= 3
83 && p
[0] == 'l' && p
[1] == 't' && p
[2] == ';')
85 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
90 else if (pEnd
- p
>= 3
91 && p
[0] == 'g' && p
[1] == 't' && p
[2] == ';')
93 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
101 sal_Unicode
const * q
= p
;
102 while (q
!= pEnd
&& *q
!= '>')
106 OUString
aKey(p
, q
- p
);
109 for (sal_Int32 i
= 2; i
+ 1 < rReplacements
.getLength();
112 OUString aReplaceKey
;
113 if ((rReplacements
[i
] >>= aReplaceKey
)
114 && aReplaceKey
== aKey
115 && (rReplacements
[i
+ 1] >>= aValue
))
123 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
124 aBuffer
.append(aValue
);
129 aBuffer
.append(pCopy
, pEnd
- pCopy
);
130 *pOutput
= aBuffer
.makeStringAndClear();
134 void makeAndAppendXMLName(
135 OUStringBuffer
& rBuffer
, const OUString
& rIn
)
137 sal_Int32 nCount
= rIn
.getLength();
138 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
140 const sal_Unicode c
= rIn
[ n
];
144 rBuffer
.append( "&" );
148 rBuffer
.append( """ );
152 rBuffer
.append( "'" );
156 rBuffer
.append( "<" );
160 rBuffer
.append( ">" );
170 bool createContentProviderData(
171 const OUString
& rProvider
,
172 const uno::Reference
< container::XHierarchicalNameAccess
>& rxHierNameAccess
,
173 ContentProviderData
& rInfo
)
175 // Obtain service name.
180 if ( !( rxHierNameAccess
->getByHierarchicalName(
181 rProvider
+ "/ServiceName" ) >>= aValue
) )
183 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
184 "Error getting item value!" );
187 catch (const container::NoSuchElementException
&)
192 rInfo
.ServiceName
= aValue
;
194 // Obtain URL Template.
196 if ( !( rxHierNameAccess
->getByHierarchicalName(
197 rProvider
+ "/URLTemplate" ) >>= aValue
) )
199 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
200 "Error getting item value!" );
203 rInfo
.URLTemplate
= aValue
;
207 if ( !( rxHierNameAccess
->getByHierarchicalName(
208 rProvider
+ "/Arguments" ) >>= aValue
) )
210 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
211 "Error getting item value!" );
214 rInfo
.Arguments
= aValue
;
221 // UniversalContentBroker Implementation.
224 UniversalContentBroker::UniversalContentBroker(
225 const Reference
< css::uno::XComponentContext
>& xContext
)
226 : m_xContext( xContext
),
229 OSL_ENSURE( m_xContext
.is(),
230 "UniversalContentBroker ctor: No service manager" );
235 UniversalContentBroker::~UniversalContentBroker()
240 // XComponent methods.
244 void SAL_CALL
UniversalContentBroker::dispose()
246 if ( m_pDisposeEventListeners
&& m_pDisposeEventListeners
->getLength() )
249 aEvt
.Source
= static_cast< XComponent
* >(this);
250 m_pDisposeEventListeners
->disposeAndClear( aEvt
);
253 if ( m_xNotifier
.is() )
254 m_xNotifier
->removeChangesListener( this );
259 void SAL_CALL
UniversalContentBroker::addEventListener(
260 const Reference
< XEventListener
>& Listener
)
262 if ( !m_pDisposeEventListeners
)
263 m_pDisposeEventListeners
.reset( new OInterfaceContainerHelper2( m_aMutex
) );
265 m_pDisposeEventListeners
->addInterface( Listener
);
270 void SAL_CALL
UniversalContentBroker::removeEventListener(
271 const Reference
< XEventListener
>& Listener
)
273 if ( m_pDisposeEventListeners
)
274 m_pDisposeEventListeners
->removeInterface( Listener
);
276 // Note: Don't want to delete empty container here -> performance.
280 // XServiceInfo methods.
282 XSERVICEINFO_COMMOM_IMPL( UniversalContentBroker
,
283 "com.sun.star.comp.ucb.UniversalContentBroker" )
284 /// @throws css::uno::Exception
285 static css::uno::Reference
< css::uno::XInterface
>
286 UniversalContentBroker_CreateInstance( const css::uno::Reference
< css::lang::XMultiServiceFactory
> & rSMgr
)
288 css::lang::XServiceInfo
* pX
= new UniversalContentBroker( ucbhelper::getComponentContext(rSMgr
) );
289 return css::uno::Reference
< css::uno::XInterface
>::query( pX
);
292 css::uno::Sequence
< OUString
>
293 UniversalContentBroker::getSupportedServiceNames_Static()
295 css::uno::Sequence
< OUString
> aSNS
{ UCB_SERVICE_NAME
};
299 // Service factory implementation.
302 ONE_INSTANCE_SERVICE_FACTORY_IMPL( UniversalContentBroker
);
305 // XInitialization methods.
309 void SAL_CALL
UniversalContentBroker::initialize( const css::uno::Sequence
< Any
>& aArguments
)
312 osl::MutexGuard
aGuard(m_aMutex
);
313 if (m_aArguments
.hasElements())
315 if (aArguments
.hasElements()
316 && !(m_aArguments
.getLength() == 2
317 && aArguments
.getLength() == 2
318 && m_aArguments
[0] == aArguments
[0]
319 && m_aArguments
[1] == aArguments
[1]))
321 throw IllegalArgumentException(
322 "UCB reinitialized with different arguments",
323 static_cast< cppu::OWeakObject
* >(this), 0);
327 if (!aArguments
.hasElements())
329 m_aArguments
.realloc(2);
330 m_aArguments
[0] <<= OUString("Local");
331 m_aArguments
[1] <<= OUString("Office");
335 m_aArguments
= aArguments
;
342 // XContentProviderManager methods.
346 Reference
< XContentProvider
> SAL_CALL
347 UniversalContentBroker::registerContentProvider(
348 const Reference
< XContentProvider
>& Provider
,
349 const OUString
& Scheme
,
350 sal_Bool ReplaceExisting
)
352 osl::MutexGuard
aGuard(m_aMutex
);
354 ProviderMap_Impl::iterator aIt
;
357 aIt
= m_aProviders
.find(Scheme
);
359 catch (const IllegalArgumentException
&)
361 return nullptr; //@@@
364 Reference
< XContentProvider
> xPrevious
;
365 if (aIt
== m_aProviders
.end())
367 ProviderList_Impl aList
;
368 aList
.push_front( ProviderListEntry_Impl(Provider
) );
371 m_aProviders
.add(Scheme
, aList
);
373 catch (const IllegalArgumentException
&)
375 return nullptr; //@@@
380 if (!ReplaceExisting
)
381 throw DuplicateProviderException();
383 ProviderList_Impl
& rList
= aIt
->getValue();
384 xPrevious
= rList
.front().getProvider();
385 rList
.push_front( ProviderListEntry_Impl(Provider
) );
393 void SAL_CALL
UniversalContentBroker::deregisterContentProvider(
394 const Reference
< XContentProvider
>& Provider
,
395 const OUString
& Scheme
)
397 osl::MutexGuard
aGuard(m_aMutex
);
399 ProviderMap_Impl::iterator aMapIt
;
402 aMapIt
= m_aProviders
.find(Scheme
);
404 catch (const IllegalArgumentException
&)
409 if (aMapIt
!= m_aProviders
.end())
411 ProviderList_Impl
& rList
= aMapIt
->getValue();
413 auto aListIt
= std::find_if(rList
.begin(), rList
.end(),
414 [&Provider
](const ProviderListEntry_Impl
& rEntry
) { return rEntry
.getProvider() == Provider
; });
415 if (aListIt
!= rList
.end())
416 rList
.erase(aListIt
);
419 m_aProviders
.erase(aMapIt
);
425 css::uno::Sequence
< ContentProviderInfo
> SAL_CALL
426 UniversalContentBroker::queryContentProviders()
428 // Return a list with information about active(!) content providers.
430 osl::MutexGuard
aGuard(m_aMutex
);
432 css::uno::Sequence
< ContentProviderInfo
> aSeq( m_aProviders
.size() );
433 ContentProviderInfo
* pInfo
= aSeq
.getArray();
435 ProviderMap_Impl::const_iterator end
= m_aProviders
.end();
436 for (ProviderMap_Impl::const_iterator
it(m_aProviders
.begin()); it
!= end
;
439 // Note: Active provider is always the first list element.
440 pInfo
->ContentProvider
= it
->getValue().front().getProvider();
441 pInfo
->Scheme
= it
->getRegexp();
450 Reference
< XContentProvider
> SAL_CALL
451 UniversalContentBroker::queryContentProvider( const OUString
&
454 return queryContentProvider( Identifier
, false );
458 // XContentProvider methods.
462 Reference
< XContent
> SAL_CALL
UniversalContentBroker::queryContent(
463 const Reference
< XContentIdentifier
>& Identifier
)
466 // Let the content provider for the scheme given with the content
467 // identifier create the XContent instance.
470 if ( !Identifier
.is() )
471 return Reference
< XContent
>();
473 Reference
< XContentProvider
> xProv
=
474 queryContentProvider( Identifier
->getContentIdentifier(), true );
476 return xProv
->queryContent( Identifier
);
478 return Reference
< XContent
>();
483 sal_Int32 SAL_CALL
UniversalContentBroker::compareContentIds(
484 const Reference
< XContentIdentifier
>& Id1
,
485 const Reference
< XContentIdentifier
>& Id2
)
487 OUString
aURI1( Id1
->getContentIdentifier() );
488 OUString
aURI2( Id2
->getContentIdentifier() );
490 Reference
< XContentProvider
> xProv1
491 = queryContentProvider( aURI1
, true );
492 Reference
< XContentProvider
> xProv2
493 = queryContentProvider( aURI2
, true );
495 // When both identifiers belong to the same provider, let that provider
496 // compare them; otherwise, simply compare the URI strings (which must
498 if ( xProv1
.is() && ( xProv1
== xProv2
) )
499 return xProv1
->compareContentIds( Id1
, Id2
);
501 return aURI1
.compareTo( aURI2
);
505 // XContentIdentifierFactory methods.
509 Reference
< XContentIdentifier
> SAL_CALL
510 UniversalContentBroker::createContentIdentifier(
511 const OUString
& ContentId
)
514 // Let the content provider for the scheme given with content
515 // identifier create the XContentIdentifier instance, if he supports
516 // the XContentIdentifierFactory interface. Otherwise create standard
517 // implementation object for XContentIdentifier.
520 Reference
< XContentIdentifier
> xIdentifier
;
522 Reference
< XContentProvider
> xProv
523 = queryContentProvider( ContentId
, true );
526 Reference
< XContentIdentifierFactory
> xFac( xProv
, UNO_QUERY
);
528 xIdentifier
= xFac
->createContentIdentifier( ContentId
);
531 if ( !xIdentifier
.is() )
532 xIdentifier
= new ContentIdentifier( ContentId
);
538 // XCommandProcessor methods.
542 sal_Int32 SAL_CALL
UniversalContentBroker::createCommandIdentifier()
544 osl::MutexGuard
aGuard( m_aMutex
);
546 // Just increase counter on every call to generate an identifier.
547 return ++m_nCommandId
;
552 Any SAL_CALL
UniversalContentBroker::execute(
553 const Command
& aCommand
,
555 const Reference
< XCommandEnvironment
>& Environment
)
560 // Note: Don't forget to adapt ucb_commands::CommandProcessorInfo
561 // ctor in ucbcmds.cxx when adding new commands!
564 if ( ( aCommand
.Handle
== GETCOMMANDINFO_HANDLE
) || aCommand
.Name
== GETCOMMANDINFO_NAME
)
570 aRet
<<= getCommandInfo();
572 else if ( ( aCommand
.Handle
== GLOBALTRANSFER_HANDLE
) || aCommand
.Name
== GLOBALTRANSFER_NAME
)
578 GlobalTransferCommandArgument2 aTransferArg
;
579 if ( !( aCommand
.Argument
>>= aTransferArg
) )
581 GlobalTransferCommandArgument aArg
;
582 if ( !( aCommand
.Argument
>>= aArg
) )
584 ucbhelper::cancelCommandExecution(
585 makeAny( IllegalArgumentException(
586 "Wrong argument type!",
587 static_cast< cppu::OWeakObject
* >( this ),
593 // Copy infos into the new structure
594 aTransferArg
.Operation
= aArg
.Operation
;
595 aTransferArg
.SourceURL
= aArg
.SourceURL
;
596 aTransferArg
.TargetURL
= aArg
.TargetURL
;
597 aTransferArg
.NewTitle
= aArg
.NewTitle
;
598 aTransferArg
.NameClash
= aArg
.NameClash
;
601 globalTransfer( aTransferArg
, Environment
);
603 else if ( ( aCommand
.Handle
== CHECKIN_HANDLE
) || aCommand
.Name
== CHECKIN_NAME
)
605 ucb::CheckinArgument aCheckinArg
;
606 if ( !( aCommand
.Argument
>>= aCheckinArg
) )
608 ucbhelper::cancelCommandExecution(
609 makeAny( IllegalArgumentException(
610 "Wrong argument type!",
611 static_cast< cppu::OWeakObject
* >( this ),
616 aRet
= checkIn( aCheckinArg
, Environment
);
624 ucbhelper::cancelCommandExecution(
625 makeAny( UnsupportedCommandException(
627 static_cast< cppu::OWeakObject
* >( this ) ) ),
636 // XCommandProcessor2 methods.
640 void SAL_CALL
UniversalContentBroker::releaseCommandIdentifier(sal_Int32
/*aCommandId*/)
642 // @@@ Not implemented ( yet).
647 void SAL_CALL
UniversalContentBroker::abort( sal_Int32
)
649 // @@@ Not implemented ( yet).
653 // XChangesListener methods
657 void SAL_CALL
UniversalContentBroker::changesOccurred( const util::ChangesEvent
& Event
)
659 if ( Event
.Changes
.hasElements() )
661 uno::Reference
< container::XHierarchicalNameAccess
> xHierNameAccess
;
662 Event
.Base
>>= xHierNameAccess
;
664 OSL_ASSERT( xHierNameAccess
.is() );
666 ContentProviderDataList aData
;
667 for ( const util::ElementChange
& rElem
: Event
.Changes
)
670 rElem
.Accessor
>>= aKey
;
672 ContentProviderData aInfo
;
674 // Removal of UCPs from the configuration leads to changesOccurred
675 // notifications, too, but it is hard to tell for a given
676 // ElementChange whether it is an addition or a removal, so as a
677 // heuristic consider as removals those that cause a
678 // NoSuchElementException in createContentProviderData.
680 // For now, removal of UCPs from the configuration is simply ignored
681 // (and not reflected in the UCB's data structures):
682 if (createContentProviderData(aKey
, xHierNameAccess
, aInfo
))
684 aData
.push_back(aInfo
);
688 prepareAndRegister(aData
);
693 // XEventListener methods
697 void SAL_CALL
UniversalContentBroker::disposing(const lang::EventObject
&)
699 if ( m_xNotifier
.is() )
701 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
703 if ( m_xNotifier
.is() )
709 // Non-interface methods
712 Reference
< XContentProvider
> UniversalContentBroker::queryContentProvider(
713 const OUString
& Identifier
,
716 osl::MutexGuard
aGuard( m_aMutex
);
718 ProviderList_Impl
const * pList
= m_aProviders
.map( Identifier
);
719 return pList
? bResolved
? pList
->front().getResolvedProvider()
720 : pList
->front().getProvider()
721 : Reference
< XContentProvider
>();
724 void UniversalContentBroker::configureUcb()
728 if (m_aArguments
.getLength() < 2
729 || !(m_aArguments
[0] >>= aKey1
) || !(m_aArguments
[1] >>= aKey2
))
731 OSL_FAIL("UniversalContentBroker::configureUcb(): Bad arguments");
735 ContentProviderDataList aData
;
736 if (!getContentProviderData(aKey1
, aKey2
, aData
))
738 SAL_WARN( "ucb", "No configuration");
742 prepareAndRegister(aData
);
745 void UniversalContentBroker::prepareAndRegister(
746 const ContentProviderDataList
& rData
)
748 for (const auto& rContentProviderData
: rData
)
750 OUString aProviderArguments
;
751 if (fillPlaceholders(rContentProviderData
.Arguments
,
753 &aProviderArguments
))
757 rContentProviderData
.ServiceName
,
759 rContentProviderData
.URLTemplate
);
763 OSL_FAIL("UniversalContentBroker::prepareAndRegister(): Bad argument placeholders");
768 bool UniversalContentBroker::getContentProviderData(
769 const OUString
& rKey1
,
770 const OUString
& rKey2
,
771 ContentProviderDataList
& rListToFill
)
773 if ( !m_xContext
.is() || rKey1
.isEmpty() || rKey2
.isEmpty() )
775 OSL_FAIL( "UniversalContentBroker::getContentProviderData - Invalid argument!" );
781 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
782 configuration::theDefaultProvider::get( m_xContext
);
784 OUStringBuffer
aFullPath(128);
786 "/org.openoffice.ucb.Configuration/ContentProviders"
788 makeAndAppendXMLName( aFullPath
, rKey1
);
789 aFullPath
.append( "']/SecondaryKeys/['" );
790 makeAndAppendXMLName( aFullPath
, rKey2
);
791 aFullPath
.append( "']/ProviderData" );
793 uno::Sequence
<uno::Any
> aArguments(comphelper::InitAnyPropertySequence(
795 {"nodepath", uno::Any(aFullPath
.makeStringAndClear())}
798 uno::Reference
< uno::XInterface
> xInterface(
799 xConfigProv
->createInstanceWithArguments(
800 "com.sun.star.configuration.ConfigurationAccess",
803 if ( !m_xNotifier
.is() )
805 m_xNotifier
.set( xInterface
, uno::UNO_QUERY_THROW
);
807 m_xNotifier
->addChangesListener( this );
810 uno::Reference
< container::XNameAccess
> xNameAccess(
811 xInterface
, uno::UNO_QUERY_THROW
);
813 const uno::Sequence
< OUString
> aElems
= xNameAccess
->getElementNames();
815 if ( aElems
.hasElements() )
817 uno::Reference
< container::XHierarchicalNameAccess
>
818 xHierNameAccess( xInterface
, uno::UNO_QUERY_THROW
);
820 // Iterate over children.
821 for ( const auto& rElem
: aElems
)
827 ContentProviderData aInfo
;
829 OUStringBuffer aElemBuffer
;
830 aElemBuffer
.append( "['" );
831 makeAndAppendXMLName( aElemBuffer
, rElem
);
832 aElemBuffer
.append( "']" );
835 createContentProviderData(
836 aElemBuffer
.makeStringAndClear(), xHierNameAccess
,
839 rListToFill
.push_back( aInfo
);
841 catch (const container::NoSuchElementException
&)
843 // getByHierarchicalName
844 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
845 "caught NoSuchElementException!" );
850 catch (const uno::RuntimeException
&)
852 SAL_WARN( "ucb", "caught RuntimeException!" );
855 catch (const uno::Exception
&)
857 // createInstance, createInstanceWithArguments
859 SAL_WARN( "ucb", "caught Exception!" );
867 // ProviderListEntry_Impl implementation.
870 Reference
< XContentProvider
> const & ProviderListEntry_Impl::resolveProvider() const
872 if ( !m_xResolvedProvider
.is() )
874 Reference
< XContentProviderSupplier
> xSupplier(
875 m_xProvider
, UNO_QUERY
);
876 if ( xSupplier
.is() )
877 m_xResolvedProvider
= xSupplier
->getContentProvider();
879 if ( !m_xResolvedProvider
.is() )
880 m_xResolvedProvider
= m_xProvider
;
883 return m_xResolvedProvider
;
886 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */