xref: /core/ucbhelper/source/client/content.cxx (revision 63dfd069)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 
24 #include <osl/diagnose.h>
25 #include <osl/mutex.hxx>
26 #include <sal/log.hxx>
27 #include <salhelper/simplereferenceobject.hxx>
28 #include <cppuhelper/weak.hxx>
29 #include <cppuhelper/queryinterface.hxx>
30 
31 #include <cppuhelper/implbase.hxx>
32 #include <com/sun/star/ucb/CheckinArgument.hpp>
33 #include <com/sun/star/ucb/ContentCreationError.hpp>
34 #include <com/sun/star/ucb/ContentCreationException.hpp>
35 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
36 #include <com/sun/star/ucb/XCommandInfo.hpp>
37 #include <com/sun/star/ucb/XCommandProcessor.hpp>
38 #include <com/sun/star/ucb/Command.hpp>
39 #include <com/sun/star/ucb/ContentAction.hpp>
40 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
41 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
42 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
43 #include <com/sun/star/ucb/OpenMode.hpp>
44 #include <com/sun/star/ucb/XContentCreator.hpp>
45 #include <com/sun/star/ucb/XContentEventListener.hpp>
46 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
47 #include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp>
48 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
49 #include <com/sun/star/ucb/XUniversalContentBroker.hpp>
50 #include <com/sun/star/beans/XPropertySetInfo.hpp>
51 #include <com/sun/star/beans/Property.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/sdbc/XRow.hpp>
54 #include <com/sun/star/lang/IllegalArgumentException.hpp>
55 #include <com/sun/star/beans/UnknownPropertyException.hpp>
56 #include <ucbhelper/content.hxx>
57 #include <ucbhelper/activedatasink.hxx>
58 #include <ucbhelper/activedatastreamer.hxx>
59 #include <ucbhelper/cancelcommandexecution.hxx>
60 
61 namespace com::sun::star::ucb { class XCommandEnvironment; }
62 namespace com::sun::star::ucb { class XContentProvider; }
63 namespace com::sun::star::sdbc { class XResultSet; }
64 
65 using namespace com::sun::star::container;
66 using namespace com::sun::star::beans;
67 using namespace com::sun::star::io;
68 using namespace com::sun::star::lang;
69 using namespace com::sun::star::sdbc;
70 using namespace com::sun::star::task;
71 using namespace com::sun::star::ucb;
72 using namespace com::sun::star::uno;
73 
74 namespace ucbhelper
75 {
76 
77 class EmptyInputStream : public ::cppu::WeakImplHelper< XInputStream >
78 {
79 public:
80     virtual sal_Int32 SAL_CALL readBytes(
81         Sequence< sal_Int8 > & data, sal_Int32 nBytesToRead ) override;
82     virtual sal_Int32 SAL_CALL readSomeBytes(
83         Sequence< sal_Int8 > & data, sal_Int32 nMaxBytesToRead ) override;
84     virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
85     virtual sal_Int32 SAL_CALL available() override;
86     virtual void SAL_CALL closeInput() override;
87 };
88 
89 sal_Int32 EmptyInputStream::readBytes(
90     Sequence< sal_Int8 > & data, sal_Int32 )
91 {
92     data.realloc( 0 );
93     return 0;
94 }
95 
96 sal_Int32 EmptyInputStream::readSomeBytes(
97     Sequence< sal_Int8 > & data, sal_Int32 )
98 {
99     data.realloc( 0 );
100     return 0;
101 }
102 
103 void EmptyInputStream::skipBytes( sal_Int32 )
104 {
105 }
106 
107 sal_Int32 EmptyInputStream::available()
108 {
109     return 0;
110 }
111 
112 void EmptyInputStream::closeInput()
113 {
114 }
115 
116 
117 // class ContentEventListener_Impl.
118 
119 
120 class ContentEventListener_Impl : public cppu::OWeakObject,
121                                       public XContentEventListener
122 {
123     Content_Impl& m_rContent;
124 
125 public:
126     explicit ContentEventListener_Impl( Content_Impl& rContent )
127         : m_rContent( rContent ) {}
128 
129     // XInterface
130     virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
131     virtual void SAL_CALL acquire()
132         throw() override;
133     virtual void SAL_CALL release()
134         throw() override;
135 
136     // XContentEventListener
137     virtual void SAL_CALL contentEvent( const ContentEvent& evt ) override;
138 
139     // XEventListener ( base of XContentEventListener )
140     virtual void SAL_CALL disposing( const EventObject& Source ) override;
141 };
142 
143 
144 // class Content_Impl.
145 
146 
147 class Content_Impl : public salhelper::SimpleReferenceObject
148 {
149 friend class ContentEventListener_Impl;
150 
151     mutable OUString               m_aURL;
152     Reference< XComponentContext >      m_xCtx;
153     Reference< XContent >               m_xContent;
154     Reference< XCommandProcessor >          m_xCommandProcessor;
155     Reference< XCommandEnvironment >    m_xEnv;
156     Reference< XContentEventListener >  m_xContentEventListener;
157     mutable osl::Mutex                  m_aMutex;
158 
159 private:
160     void reinit( const Reference< XContent >& xContent );
161     void disposing(const EventObject& Source);
162 
163 public:
164     Content_Impl() {};
165     Content_Impl( const Reference< XComponentContext >& rCtx,
166                   const Reference< XContent >& rContent,
167                   const Reference< XCommandEnvironment >& rEnv );
168 
169     virtual ~Content_Impl() override;
170 
171     const OUString&           getURL() const;
172     Reference< XContent >          getContent();
173     Reference< XCommandProcessor > getCommandProcessor();
174     Reference< XComponentContext > const & getComponentContext() const
175     { assert(m_xCtx.is()); return m_xCtx; }
176 
177     Any  executeCommand( const Command& rCommand );
178 
179     inline const Reference< XCommandEnvironment >& getEnvironment() const;
180     inline void setEnvironment(
181                         const Reference< XCommandEnvironment >& xNewEnv );
182 
183     void inserted();
184 };
185 
186 
187 // Helpers.
188 
189 /// @throws ContentCreationException
190 /// @throws RuntimeException
191 static void ensureContentProviderForURL( const Reference< XUniversalContentBroker >& rBroker,
192                                          const OUString & rURL )
193 {
194     Reference< XContentProvider > xProv
195         = rBroker->queryContentProvider( rURL );
196     if ( !xProv.is() )
197     {
198         throw ContentCreationException(
199             "No Content Provider available for URL: " + rURL,
200             Reference< XInterface >(),
201             ContentCreationError_NO_CONTENT_PROVIDER );
202     }
203 }
204 
205 /// @throws ContentCreationException
206 /// @throws RuntimeException
207 static Reference< XContentIdentifier > getContentIdentifierThrow(
208                                     const Reference< XUniversalContentBroker > & rBroker,
209                                     const OUString & rURL)
210 {
211     Reference< XContentIdentifier > xId
212         = rBroker->createContentIdentifier( rURL );
213 
214     if (!xId.is())
215     {
216         ensureContentProviderForURL( rBroker, rURL );
217 
218         throw ContentCreationException(
219             "Unable to create Content Identifier!",
220             Reference< XInterface >(),
221             ContentCreationError_IDENTIFIER_CREATION_FAILED );
222     }
223 
224     return xId;
225 }
226 
227 /// @throws RuntimeException
228 static Reference< XContentIdentifier > getContentIdentifierNoThrow(
229                                     const Reference< XUniversalContentBroker > & rBroker,
230                                     const OUString & rURL)
231 {
232     return rBroker->createContentIdentifier(rURL);
233 }
234 
235 /// @throws ContentCreationException
236 /// @throws RuntimeException
237 static Reference< XContent > getContentThrow(
238                                     const Reference< XUniversalContentBroker > & rBroker,
239                                     const Reference< XContentIdentifier > & xId)
240 {
241     Reference< XContent > xContent;
242     OUString msg;
243     try
244     {
245         xContent = rBroker->queryContent( xId );
246     }
247     catch ( IllegalIdentifierException const & e )
248     {
249         msg = e.Message;
250         // handled below.
251     }
252 
253     if ( !xContent.is() )
254     {
255         ensureContentProviderForURL( rBroker, xId->getContentIdentifier() );
256 
257         throw ContentCreationException(
258             "Unable to create Content for <" + xId->getContentIdentifier() + ">: " + msg,
259             Reference< XInterface >(),
260             ContentCreationError_CONTENT_CREATION_FAILED );
261     }
262 
263     return xContent;
264 }
265 
266 /// @throws RuntimeException
267 static Reference< XContent > getContentNoThrow(
268                                     const Reference< XUniversalContentBroker > & rBroker,
269                                     const Reference< XContentIdentifier > & xId)
270 {
271     Reference< XContent > xContent;
272     try
273     {
274         xContent = rBroker->queryContent( xId );
275     }
276     catch ( IllegalIdentifierException const & e )
277     {
278         SAL_WARN("ucbhelper", "getContentNoThrow: " << e);
279     }
280 
281     return xContent;
282 }
283 
284 
285 // Content Implementation.
286 
287 
288 Content::Content()
289 : m_xImpl( new Content_Impl )
290 {
291 }
292 
293 
294 Content::Content( const OUString& rURL,
295                   const Reference< XCommandEnvironment >& rEnv,
296                   const Reference< XComponentContext >& rCtx )
297 {
298     Reference< XUniversalContentBroker > pBroker(
299         UniversalContentBroker::create( rCtx ) );
300 
301     Reference< XContentIdentifier > xId
302         = getContentIdentifierThrow(pBroker, rURL);
303 
304     Reference< XContent > xContent = getContentThrow(pBroker, xId);
305 
306     m_xImpl = new Content_Impl( rCtx, xContent, rEnv );
307 }
308 
309 
310 Content::Content( const Reference< XContent >& rContent,
311                   const Reference< XCommandEnvironment >& rEnv,
312                   const Reference< XComponentContext >& rCtx )
313 {
314     m_xImpl = new Content_Impl( rCtx, rContent, rEnv );
315 }
316 
317 
318 Content::Content( const Content& rOther )
319 {
320     m_xImpl = rOther.m_xImpl;
321 }
322 
323 Content::Content( Content&& rOther ) noexcept
324 {
325     m_xImpl = std::move(rOther.m_xImpl);
326 }
327 
328 // static
329 bool Content::create( const OUString& rURL,
330                           const Reference< XCommandEnvironment >& rEnv,
331                           const Reference< XComponentContext >& rCtx,
332                           Content& rContent )
333 {
334     Reference< XUniversalContentBroker > pBroker(
335         UniversalContentBroker::create( rCtx ) );
336 
337     Reference< XContentIdentifier > xId
338         = getContentIdentifierNoThrow(pBroker, rURL);
339     if ( !xId.is() )
340         return false;
341 
342     Reference< XContent > xContent = getContentNoThrow(pBroker, xId);
343     if ( !xContent.is() )
344         return false;
345 
346     rContent.m_xImpl
347         = new Content_Impl( rCtx, xContent, rEnv );
348 
349     return true;
350 }
351 
352 
353 Content::~Content()
354 {
355 }
356 
357 
358 Content& Content::operator=( const Content& rOther )
359 {
360     m_xImpl = rOther.m_xImpl;
361     return *this;
362 }
363 
364 Content& Content::operator=( Content&& rOther ) noexcept
365 {
366     m_xImpl = std::move(rOther.m_xImpl);
367     return *this;
368 }
369 
370 Reference< XContent > Content::get() const
371 {
372     return m_xImpl->getContent();
373 }
374 
375 
376 const OUString& Content::getURL() const
377 {
378     return m_xImpl->getURL();
379 }
380 
381 
382 const Reference< XCommandEnvironment >& Content::getCommandEnvironment() const
383 {
384     return m_xImpl->getEnvironment();
385 }
386 
387 
388 void Content::setCommandEnvironment(
389                         const Reference< XCommandEnvironment >& xNewEnv )
390 {
391     m_xImpl->setEnvironment( xNewEnv );
392 }
393 
394 
395 Reference< XCommandInfo > Content::getCommands()
396 {
397     Command aCommand;
398     aCommand.Name     = "getCommandInfo";
399     aCommand.Handle   = -1; // n/a
400     aCommand.Argument = Any();
401 
402     Any aResult = m_xImpl->executeCommand( aCommand );
403 
404     Reference< XCommandInfo > xInfo;
405     aResult >>= xInfo;
406     return xInfo;
407 }
408 
409 
410 Reference< XPropertySetInfo > Content::getProperties()
411 {
412     Command aCommand;
413     aCommand.Name     = "getPropertySetInfo";
414     aCommand.Handle   = -1; // n/a
415     aCommand.Argument = Any();
416 
417     Any aResult = m_xImpl->executeCommand( aCommand );
418 
419     Reference< XPropertySetInfo > xInfo;
420     aResult >>= xInfo;
421     return xInfo;
422 }
423 
424 
425 Any Content::getPropertyValue( const OUString& rPropertyName )
426 {
427     Sequence<OUString> aNames { rPropertyName };
428 
429     Sequence< Any > aRet = getPropertyValues( aNames );
430     return aRet.getConstArray()[ 0 ];
431 }
432 
433 
434 Any Content::setPropertyValue( const OUString& rName,
435                                 const Any& rValue )
436 {
437     Sequence<OUString> aNames { rName };
438 
439     Sequence< Any > aValues( 1 );
440     aValues.getArray()[ 0 ] = rValue;
441 
442     Sequence< Any > aErrors = setPropertyValues( aNames, aValues );
443     return aErrors.getConstArray()[ 0 ];
444 }
445 
446 
447 Sequence< Any > Content::getPropertyValues(
448                             const Sequence< OUString >& rPropertyNames )
449 {
450     Reference< XRow > xRow = getPropertyValuesInterface( rPropertyNames );
451 
452     sal_Int32 nCount = rPropertyNames.getLength();
453     Sequence< Any > aValues( nCount );
454 
455     if ( xRow.is() )
456     {
457         Any* pValues = aValues.getArray();
458 
459         for ( sal_Int32 n = 0; n < nCount; ++n )
460             pValues[ n ] = xRow->getObject( n + 1, Reference< XNameAccess >() );
461     }
462 
463     return aValues;
464 }
465 
466 
467 Reference< XRow > Content::getPropertyValuesInterface(
468                             const Sequence< OUString >& rPropertyNames )
469 {
470     sal_Int32 nCount = rPropertyNames.getLength();
471     Sequence< Property > aProps( nCount );
472     Property* pProps = aProps.getArray();
473 
474     const OUString* pNames  = rPropertyNames.getConstArray();
475 
476     for ( sal_Int32 n = 0; n< nCount; ++n )
477     {
478         Property& rProp = pProps[ n ];
479 
480         rProp.Name       = pNames[ n ];
481         rProp.Handle     = -1; // n/a
482 //        rProp.Type       =
483 //        rProp.Attributes = ;
484     }
485 
486     Command aCommand;
487     aCommand.Name     = "getPropertyValues";
488     aCommand.Handle   = -1; // n/a
489     aCommand.Argument <<= aProps;
490 
491     Any aResult = m_xImpl->executeCommand( aCommand );
492 
493     Reference< XRow > xRow;
494     aResult >>= xRow;
495     return xRow;
496 }
497 
498 
499 Sequence< Any > Content::setPropertyValues(
500                             const Sequence< OUString >& rPropertyNames,
501                                 const Sequence< Any >& rValues )
502 {
503     if ( rPropertyNames.getLength() != rValues.getLength() )
504     {
505         ucbhelper::cancelCommandExecution(
506             makeAny( IllegalArgumentException(
507                         "Length of property names sequence and value "
508                         "sequence are unequal!",
509                         get(),
510                         -1 ) ),
511             m_xImpl->getEnvironment() );
512         // Unreachable
513     }
514 
515     sal_Int32 nCount = rValues.getLength();
516     Sequence< PropertyValue > aProps( nCount );
517     PropertyValue* pProps = aProps.getArray();
518 
519     const OUString* pNames  = rPropertyNames.getConstArray();
520     const Any* pValues = rValues.getConstArray();
521 
522     for ( sal_Int32 n = 0; n< nCount; ++n )
523     {
524         PropertyValue& rProp = pProps[ n ];
525 
526         rProp.Name   = pNames[ n ];
527         rProp.Handle = -1; // n/a
528         rProp.Value  = pValues[ n ];
529 //        rProp.State  = ;
530     }
531 
532     Command aCommand;
533     aCommand.Name     = "setPropertyValues";
534     aCommand.Handle   = -1; // n/a
535     aCommand.Argument <<= aProps;
536 
537     Any aResult = m_xImpl->executeCommand( aCommand );
538 
539     Sequence< Any > aErrors;
540     aResult >>= aErrors;
541     return aErrors;
542 }
543 
544 
545 Any Content::executeCommand( const OUString& rCommandName,
546                              const Any& rCommandArgument )
547 {
548     Command aCommand;
549     aCommand.Name     = rCommandName;
550     aCommand.Handle   = -1; // n/a
551     aCommand.Argument = rCommandArgument;
552 
553     return m_xImpl->executeCommand( aCommand );
554 }
555 
556 
557 Any Content::createCursorAny( const Sequence< OUString >& rPropertyNames,
558                               ResultSetInclude eMode )
559 {
560     sal_Int32 nCount = rPropertyNames.getLength();
561     Sequence< Property > aProps( nCount );
562     Property* pProps = aProps.getArray();
563     const OUString* pNames = rPropertyNames.getConstArray();
564     for ( sal_Int32 n = 0; n < nCount; ++n )
565     {
566         Property& rProp = pProps[ n ];
567         rProp.Name   = pNames[ n ];
568         rProp.Handle = -1; // n/a
569     }
570 
571     OpenCommandArgument2 aArg;
572     aArg.Mode       = ( eMode == INCLUDE_FOLDERS_ONLY )
573                         ? OpenMode::FOLDERS
574                         : ( eMode == INCLUDE_DOCUMENTS_ONLY )
575                             ? OpenMode::DOCUMENTS : OpenMode::ALL;
576     aArg.Priority   = 0; // unused
577     aArg.Sink.clear(); // unused
578     aArg.Properties = aProps;
579 
580     Command aCommand;
581     aCommand.Name     = "open";
582     aCommand.Handle   = -1; // n/a
583     aCommand.Argument <<= aArg;
584 
585     return m_xImpl->executeCommand( aCommand );
586 }
587 
588 
589 Reference< XResultSet > Content::createCursor(
590                             const Sequence< OUString >& rPropertyNames,
591                             ResultSetInclude eMode )
592 {
593     Any aCursorAny = createCursorAny( rPropertyNames, eMode );
594 
595     Reference< XDynamicResultSet > xDynSet;
596     Reference< XResultSet > aResult;
597 
598     aCursorAny >>= xDynSet;
599     if ( xDynSet.is() )
600         aResult = xDynSet->getStaticResultSet();
601 
602     OSL_ENSURE( aResult.is(), "Content::createCursor - no cursor!" );
603 
604     if ( !aResult.is() )
605     {
606         // Former, the open command directly returned a XResultSet.
607         aCursorAny >>= aResult;
608 
609         OSL_ENSURE( !aResult.is(),
610                     "Content::createCursor - open-Command must "
611                     "return a Reference< XDynnamicResultSet >!" );
612     }
613 
614     return aResult;
615 }
616 
617 
618 Reference< XDynamicResultSet > Content::createDynamicCursor(
619                             const Sequence< OUString >& rPropertyNames,
620                             ResultSetInclude eMode )
621 {
622     Reference< XDynamicResultSet > aResult;
623     createCursorAny( rPropertyNames, eMode ) >>= aResult;
624 
625     OSL_ENSURE( aResult.is(), "Content::createDynamicCursor - no cursor!" );
626 
627     return aResult;
628 }
629 
630 
631 Reference< XResultSet > Content::createSortedCursor(
632                             const Sequence< OUString >& rPropertyNames,
633                             const Sequence< NumberedSortingInfo >& rSortInfo,
634                             const Reference< XAnyCompareFactory >& rAnyCompareFactory,
635                             ResultSetInclude eMode )
636 {
637     Reference< XResultSet > aResult;
638     Reference< XDynamicResultSet > aDynSet;
639 
640     Any aCursorAny = createCursorAny( rPropertyNames, eMode );
641 
642     aCursorAny >>= aDynSet;
643 
644     if( aDynSet.is() )
645     {
646         Reference< XDynamicResultSet > aDynResult;
647 
648         if( m_xImpl->getComponentContext().is() )
649         {
650             Reference< XSortedDynamicResultSetFactory > aSortFactory =
651                                 SortedDynamicResultSetFactory::create( m_xImpl->getComponentContext());
652 
653             aDynResult = aSortFactory->createSortedDynamicResultSet( aDynSet,
654                                                               rSortInfo,
655                                                               rAnyCompareFactory );
656         }
657 
658         OSL_ENSURE( aDynResult.is(), "Content::createSortedCursor - no sorted cursor!" );
659 
660         if( aDynResult.is() )
661             aResult = aDynResult->getStaticResultSet();
662         else
663             aResult = aDynSet->getStaticResultSet();
664     }
665 
666     OSL_ENSURE( aResult.is(), "Content::createSortedCursor - no cursor!" );
667 
668     if ( !aResult.is() )
669     {
670         // Former, the open command directly returned a XResultSet.
671         aCursorAny >>= aResult;
672 
673         OSL_ENSURE( !aResult.is(),
674                     "Content::createCursor - open-Command must "
675                     "return a Reference< XDynnamicResultSet >!" );
676     }
677 
678     return aResult;
679 }
680 
681 
682 Reference< XInputStream > Content::openStream()
683 {
684     if ( !isDocument() )
685         return Reference< XInputStream >();
686 
687     Reference< XActiveDataSink > xSink = new ActiveDataSink;
688 
689     OpenCommandArgument2 aArg;
690     aArg.Mode       = OpenMode::DOCUMENT;
691     aArg.Priority   = 0; // unused
692     aArg.Sink       = xSink;
693     aArg.Properties = Sequence< Property >( 0 ); // unused
694 
695     Command aCommand;
696     aCommand.Name     = "open";
697     aCommand.Handle   = -1; // n/a
698     aCommand.Argument <<= aArg;
699 
700     m_xImpl->executeCommand( aCommand );
701 
702     return xSink->getInputStream();
703 }
704 
705 
706 Reference< XInputStream > Content::openStreamNoLock()
707 {
708     if ( !isDocument() )
709         return Reference< XInputStream >();
710 
711     Reference< XActiveDataSink > xSink = new ActiveDataSink;
712 
713     OpenCommandArgument2 aArg;
714     aArg.Mode       = OpenMode::DOCUMENT_SHARE_DENY_NONE;
715     aArg.Priority   = 0; // unused
716     aArg.Sink       = xSink;
717     aArg.Properties = Sequence< Property >( 0 ); // unused
718 
719     Command aCommand;
720     aCommand.Name     = "open";
721     aCommand.Handle   = -1; // n/a
722     aCommand.Argument <<= aArg;
723 
724     m_xImpl->executeCommand( aCommand );
725 
726     return xSink->getInputStream();
727 }
728 
729 
730 Reference< XStream > Content::openWriteableStream()
731 {
732     if ( !isDocument() )
733         return Reference< XStream >();
734 
735     Reference< XActiveDataStreamer > xStreamer = new ActiveDataStreamer;
736 
737     OpenCommandArgument2 aArg;
738     aArg.Mode       = OpenMode::DOCUMENT;
739     aArg.Priority   = 0; // unused
740     aArg.Sink       = xStreamer;
741     aArg.Properties = Sequence< Property >( 0 ); // unused
742 
743     Command aCommand;
744     aCommand.Name     = "open";
745     aCommand.Handle   = -1; // n/a
746     aCommand.Argument <<= aArg;
747 
748     m_xImpl->executeCommand( aCommand );
749 
750     return xStreamer->getStream();
751 }
752 
753 
754 Reference< XStream > Content::openWriteableStreamNoLock()
755 {
756     if ( !isDocument() )
757         return Reference< XStream >();
758 
759     Reference< XActiveDataStreamer > xStreamer = new ActiveDataStreamer;
760 
761     OpenCommandArgument2 aArg;
762     aArg.Mode       = OpenMode::DOCUMENT_SHARE_DENY_NONE;
763     aArg.Priority   = 0; // unused
764     aArg.Sink       = xStreamer;
765     aArg.Properties = Sequence< Property >( 0 ); // unused
766 
767     Command aCommand;
768     aCommand.Name     = "open";
769     aCommand.Handle   = -1; // n/a
770     aCommand.Argument <<= aArg;
771 
772     m_xImpl->executeCommand( aCommand );
773 
774     return xStreamer->getStream();
775 }
776 
777 
778 bool Content::openStream( const Reference< XActiveDataSink >& rSink )
779 {
780     if ( !isDocument() )
781         return false;
782 
783     OpenCommandArgument2 aArg;
784     aArg.Mode       = OpenMode::DOCUMENT;
785     aArg.Priority   = 0; // unused
786     aArg.Sink       = rSink;
787     aArg.Properties = Sequence< Property >( 0 ); // unused
788 
789     Command aCommand;
790     aCommand.Name     = "open";
791     aCommand.Handle   = -1; // n/a
792     aCommand.Argument <<= aArg;
793 
794     m_xImpl->executeCommand( aCommand );
795 
796     return true;
797 }
798 
799 
800 bool Content::openStream( const Reference< XOutputStream >& rStream )
801 {
802     if ( !isDocument() )
803         return false;
804 
805     OpenCommandArgument2 aArg;
806     aArg.Mode       = OpenMode::DOCUMENT;
807     aArg.Priority   = 0; // unused
808     aArg.Sink       = rStream;
809     aArg.Properties = Sequence< Property >( 0 ); // unused
810 
811     Command aCommand;
812     aCommand.Name     = "open";
813     aCommand.Handle   = -1; // n/a
814     aCommand.Argument <<= aArg;
815 
816     m_xImpl->executeCommand( aCommand );
817 
818     return true;
819 }
820 
821 
822 void Content::writeStream( const Reference< XInputStream >& rStream,
823                            bool bReplaceExisting )
824 {
825     InsertCommandArgument aArg;
826     aArg.Data            = rStream.is() ? rStream : new EmptyInputStream;
827     aArg.ReplaceExisting = bReplaceExisting;
828 
829     Command aCommand;
830     aCommand.Name     = "insert";
831     aCommand.Handle   = -1; // n/a
832     aCommand.Argument <<= aArg;
833 
834     m_xImpl->executeCommand( aCommand );
835 
836     m_xImpl->inserted();
837 }
838 
839 
840 Sequence< ContentInfo > Content::queryCreatableContentsInfo()
841 {
842     // First, try it using "CreatableContentsInfo" property -> the "new" way.
843     Sequence< ContentInfo > aInfo;
844     if ( getPropertyValue(
845              "CreatableContentsInfo" )
846          >>= aInfo )
847         return aInfo;
848 
849     // Second, try it using XContentCreator interface -> the "old" way (not
850     // providing the chance to supply an XCommandEnvironment.
851     Reference< XContentCreator > xCreator( m_xImpl->getContent(), UNO_QUERY );
852     if ( xCreator.is() )
853         aInfo = xCreator->queryCreatableContentsInfo();
854 
855     return aInfo;
856 }
857 
858 
859 bool Content::insertNewContent( const OUString& rContentType,
860                                     const Sequence< OUString >&
861                                         rPropertyNames,
862                                     const Sequence< Any >& rPropertyValues,
863                                     Content& rNewContent )
864 {
865     return insertNewContent( rContentType,
866                              rPropertyNames,
867                              rPropertyValues,
868                              new EmptyInputStream,
869                              rNewContent );
870 }
871 
872 
873 bool Content::insertNewContent( const OUString& rContentType,
874                                     const Sequence< OUString >&
875                                         rPropertyNames,
876                                     const Sequence< Any >& rPropertyValues,
877                                     const Reference< XInputStream >& rData,
878                                     Content& rNewContent )
879 {
880     if ( rContentType.isEmpty() )
881         return false;
882 
883     // First, try it using "createNewContent" command -> the "new" way.
884     ContentInfo aInfo;
885     aInfo.Type = rContentType;
886     aInfo.Attributes = 0;
887 
888     Command aCommand;
889     aCommand.Name     = "createNewContent";
890     aCommand.Handle   = -1; // n/a
891     aCommand.Argument <<= aInfo;
892 
893     Reference< XContent > xNew;
894     try
895     {
896         m_xImpl->executeCommand( aCommand ) >>= xNew;
897     }
898     catch ( RuntimeException const & )
899     {
900         throw;
901     }
902     catch ( Exception const & )
903     {
904     }
905 
906     if ( !xNew.is() )
907     {
908         // Second, try it using XContentCreator interface -> the "old"
909         // way (not providing the chance to supply an XCommandEnvironment.
910         Reference< XContentCreator > xCreator( m_xImpl->getContent(), UNO_QUERY );
911 
912         if ( !xCreator.is() )
913             return false;
914 
915         xNew = xCreator->createNewContent( aInfo );
916 
917         if ( !xNew.is() )
918             return false;
919     }
920 
921     Content aNewContent(
922         xNew, m_xImpl->getEnvironment(), m_xImpl->getComponentContext() );
923     aNewContent.setPropertyValues( rPropertyNames, rPropertyValues );
924     aNewContent.executeCommand( "insert",
925                                 makeAny(
926                                     InsertCommandArgument(
927                                         rData.is() ? rData : new EmptyInputStream,
928                                         false /* ReplaceExisting */ ) ) );
929     aNewContent.m_xImpl->inserted();
930 
931     rNewContent = aNewContent;
932     return true;
933 }
934 
935 
936 void Content::transferContent( const Content& rSourceContent,
937                                    InsertOperation eOperation,
938                                    const OUString & rTitle,
939                                    const sal_Int32 nNameClashAction,
940                                    const OUString & rMimeType,
941                                    bool bMajorVersion,
942                                    const OUString & rVersionComment,
943                                    OUString* pResultURL,
944                                    const OUString & rDocumentId ) const
945 {
946     Reference< XUniversalContentBroker > pBroker(
947         UniversalContentBroker::create( m_xImpl->getComponentContext() ) );
948 
949     // Execute command "globalTransfer" at UCB.
950 
951     TransferCommandOperation eTransOp = TransferCommandOperation();
952     OUString sCommand( "globalTransfer" );
953     bool bCheckIn = false;
954     switch ( eOperation )
955     {
956         case InsertOperation::Copy:
957             eTransOp = TransferCommandOperation_COPY;
958             break;
959 
960         case InsertOperation::Move:
961             eTransOp = TransferCommandOperation_MOVE;
962             break;
963 
964         case InsertOperation::Checkin:
965             eTransOp = TransferCommandOperation_COPY;
966             sCommand = "checkin";
967             bCheckIn = true;
968             break;
969     }
970     Command aCommand;
971     aCommand.Name     = sCommand;
972     aCommand.Handle   = -1; // n/a
973 
974     if ( !bCheckIn )
975     {
976         GlobalTransferCommandArgument2 aTransferArg(
977                                             eTransOp,
978                                             rSourceContent.getURL(), // SourceURL
979                                             getURL(),   // TargetFolderURL,
980                                             rTitle,
981                                             nNameClashAction,
982                                             rMimeType,
983                                             rDocumentId );
984         aCommand.Argument <<= aTransferArg;
985     }
986     else
987     {
988         CheckinArgument aCheckinArg( bMajorVersion, rVersionComment,
989                 rSourceContent.getURL(), getURL(), rTitle, rMimeType );
990         aCommand.Argument <<= aCheckinArg;
991     }
992 
993     Any aRet = pBroker->execute( aCommand, 0, m_xImpl->getEnvironment() );
994     if ( pResultURL != nullptr )
995         aRet >>= *pResultURL;
996 }
997 
998 
999 bool Content::isFolder()
1000 {
1001     bool bFolder = false;
1002     if ( getPropertyValue("IsFolder")
1003         >>= bFolder )
1004         return bFolder;
1005 
1006     ucbhelper::cancelCommandExecution(
1007          makeAny( UnknownPropertyException(
1008                     "Unable to retrieve value of property 'IsFolder'!",
1009                     get() ) ),
1010          m_xImpl->getEnvironment() );
1011 
1012 #if !(defined(_MSC_VER) && defined(ENABLE_LTO))
1013     // Unreachable - cancelCommandExecution always throws an exception.
1014     // But some compilers complain...
1015     return false;
1016 #endif
1017 }
1018 
1019 
1020 SAL_WNOUNREACHABLE_CODE_PUSH
1021 
1022 bool Content::isDocument()
1023 {
1024     bool bDoc = false;
1025     if ( getPropertyValue("IsDocument")
1026         >>= bDoc )
1027         return bDoc;
1028 
1029     ucbhelper::cancelCommandExecution(
1030          makeAny( UnknownPropertyException(
1031                     "Unable to retrieve value of property 'IsDocument'!",
1032                     get() ) ),
1033          m_xImpl->getEnvironment() );
1034 
1035     // Unreachable - cancelCommandExecution always throws an exception,
1036     // But some compilers complain...
1037     return false;
1038 }
1039 
1040 SAL_WNOUNREACHABLE_CODE_POP
1041 
1042 void Content::lock()
1043 {
1044     Command aCommand;
1045     aCommand.Name     = "lock";
1046     aCommand.Handle   = -1; // n/a
1047 
1048     m_xImpl->executeCommand( aCommand );
1049 
1050 }
1051 
1052 void Content::unlock()
1053 {
1054 
1055     Command aCommand;
1056     aCommand.Name     = "unlock";
1057     aCommand.Handle   = -1; // n/a
1058 
1059     m_xImpl->executeCommand( aCommand );
1060 
1061 }
1062 
1063 
1064 // Content_Impl Implementation.
1065 
1066 
1067 Content_Impl::Content_Impl( const Reference< XComponentContext >& rCtx,
1068                             const Reference< XContent >& rContent,
1069                             const Reference< XCommandEnvironment >& rEnv )
1070 : m_xCtx( rCtx ),
1071   m_xContent( rContent ),
1072   m_xEnv( rEnv )
1073 {
1074     assert(rCtx.is());
1075     if ( m_xContent.is() )
1076     {
1077         m_xContentEventListener = new ContentEventListener_Impl( *this );
1078         m_xContent->addContentEventListener( m_xContentEventListener );
1079 
1080 #if OSL_DEBUG_LEVEL > 0
1081         // Only done on demand in product version for performance reasons,
1082         // but a nice debug helper.
1083         getURL();
1084 #endif
1085     }
1086 }
1087 
1088 
1089 void Content_Impl::reinit( const Reference< XContent >& xContent )
1090 {
1091     osl::MutexGuard aGuard( m_aMutex );
1092 
1093     m_xCommandProcessor = nullptr;
1094 
1095     // #92581# - Don't reset m_aURL!!!
1096 
1097     if ( m_xContent.is() )
1098     {
1099         try
1100         {
1101             m_xContent->removeContentEventListener( m_xContentEventListener );
1102         }
1103         catch ( RuntimeException const & )
1104         {
1105         }
1106     }
1107 
1108     if ( xContent.is() )
1109     {
1110         m_xContent = xContent;
1111         m_xContent->addContentEventListener( m_xContentEventListener );
1112 
1113 #if OSL_DEBUG_LEVEL > 0
1114         // Only done on demand in product version for performance reasons,
1115         // but a nice debug helper.
1116         getURL();
1117 #endif
1118     }
1119     else
1120     {
1121         // We need m_xContent's URL in order to be able to create the
1122         // content object again if demanded ( --> Content_Impl::getContent() )
1123         getURL();
1124 
1125         m_xContent = nullptr;
1126     }
1127 }
1128 
1129 
1130 // virtual
1131 Content_Impl::~Content_Impl()
1132 {
1133     if ( m_xContent.is() )
1134     {
1135         try
1136         {
1137             m_xContent->removeContentEventListener( m_xContentEventListener );
1138         }
1139         catch ( RuntimeException const & )
1140         {
1141         }
1142     }
1143 }
1144 
1145 
1146 void Content_Impl::disposing( const EventObject& Source )
1147 {
1148     Reference<XContent> xContent;
1149 
1150     {
1151         osl::MutexGuard aGuard( m_aMutex );
1152         if(Source.Source != m_xContent)
1153             return;
1154 
1155         xContent = m_xContent;
1156 
1157         m_aURL.clear();
1158         m_xCommandProcessor = nullptr;
1159         m_xContent = nullptr;
1160     }
1161 
1162     if ( xContent.is() )
1163     {
1164         try
1165         {
1166             xContent->removeContentEventListener( m_xContentEventListener );
1167         }
1168         catch ( RuntimeException const & )
1169         {
1170         }
1171     }
1172 }
1173 
1174 
1175 const OUString& Content_Impl::getURL() const
1176 {
1177     if ( m_aURL.isEmpty() && m_xContent.is() )
1178     {
1179         osl::MutexGuard aGuard( m_aMutex );
1180 
1181         if ( m_aURL.isEmpty() && m_xContent.is() )
1182         {
1183             Reference< XContentIdentifier > xId = m_xContent->getIdentifier();
1184             if ( xId.is() )
1185                 m_aURL = xId->getContentIdentifier();
1186         }
1187     }
1188 
1189     return m_aURL;
1190 }
1191 
1192 
1193 Reference< XContent > Content_Impl::getContent()
1194 {
1195     if ( !m_xContent.is() && !m_aURL.isEmpty() )
1196     {
1197         osl::MutexGuard aGuard( m_aMutex );
1198 
1199         if ( !m_xContent.is() && !m_aURL.isEmpty() )
1200         {
1201             Reference< XUniversalContentBroker > pBroker(
1202                 UniversalContentBroker::create( getComponentContext() ) );
1203 
1204             OSL_ENSURE( pBroker->queryContentProviders().hasElements(),
1205                         "Content Broker not configured (no providers)!" );
1206 
1207             Reference< XContentIdentifier > xId
1208                 = pBroker->createContentIdentifier( m_aURL );
1209 
1210             OSL_ENSURE( xId.is(), "No Content Identifier!" );
1211 
1212             if ( xId.is() )
1213             {
1214                 try
1215                 {
1216                     m_xContent = pBroker->queryContent( xId );
1217                 }
1218                 catch ( IllegalIdentifierException const & )
1219                 {
1220                 }
1221 
1222                 if ( m_xContent.is() )
1223                     m_xContent->addContentEventListener(
1224                         m_xContentEventListener );
1225             }
1226         }
1227     }
1228 
1229     return m_xContent;
1230 }
1231 
1232 
1233 Reference< XCommandProcessor > Content_Impl::getCommandProcessor()
1234 {
1235     if ( !m_xCommandProcessor.is() )
1236     {
1237         osl::MutexGuard aGuard( m_aMutex );
1238 
1239         if ( !m_xCommandProcessor.is() )
1240             m_xCommandProcessor.set( getContent(), UNO_QUERY );
1241     }
1242 
1243     return m_xCommandProcessor;
1244 }
1245 
1246 
1247 Any Content_Impl::executeCommand( const Command& rCommand )
1248 {
1249     Reference< XCommandProcessor > xProc = getCommandProcessor();
1250     if ( !xProc.is() )
1251         return Any();
1252 
1253     // Execute command
1254     return xProc->execute( rCommand, 0, m_xEnv );
1255 }
1256 
1257 
1258 inline const Reference< XCommandEnvironment >&
1259                                         Content_Impl::getEnvironment() const
1260 {
1261     return m_xEnv;
1262 }
1263 
1264 
1265 inline void Content_Impl::setEnvironment(
1266                         const Reference< XCommandEnvironment >& xNewEnv )
1267 {
1268     osl::MutexGuard aGuard( m_aMutex );
1269     m_xEnv = xNewEnv;
1270 }
1271 
1272 
1273 void Content_Impl::inserted()
1274 {
1275     // URL might have changed during 'insert' => recalculate in next getURL()
1276     osl::MutexGuard aGuard( m_aMutex );
1277     m_aURL.clear();
1278 }
1279 
1280 
1281 // ContentEventListener_Impl Implementation.
1282 
1283 
1284 // XInterface methods.
1285 
1286 void SAL_CALL ContentEventListener_Impl::acquire()
1287     throw()
1288 {
1289     OWeakObject::acquire();
1290 }
1291 
1292 void SAL_CALL ContentEventListener_Impl::release()
1293     throw()
1294 {
1295     OWeakObject::release();
1296 }
1297 
1298 css::uno::Any SAL_CALL ContentEventListener_Impl::queryInterface( const css::uno::Type & rType )
1299 {
1300     css::uno::Any aRet = cppu::queryInterface( rType,
1301                                                static_cast< XContentEventListener* >(this),
1302                                                static_cast< XEventListener* >(this)
1303                                                );
1304     return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
1305 }
1306 
1307 // XContentEventListener methods.
1308 
1309 
1310 // virtual
1311 void SAL_CALL ContentEventListener_Impl::contentEvent( const ContentEvent& evt )
1312 {
1313     if ( evt.Source == m_rContent.m_xContent )
1314     {
1315         switch ( evt.Action )
1316         {
1317             case ContentAction::DELETED:
1318                 m_rContent.reinit( Reference< XContent >() );
1319                 break;
1320 
1321             case ContentAction::EXCHANGED:
1322                 m_rContent.reinit( evt.Content );
1323                 break;
1324 
1325             default:
1326                 break;
1327         }
1328     }
1329 }
1330 
1331 
1332 // XEventListenr methods.
1333 
1334 
1335 // virtual
1336 void SAL_CALL ContentEventListener_Impl::disposing( const EventObject& Source )
1337 {
1338     m_rContent.disposing(Source);
1339 }
1340 
1341 } /* namespace ucbhelper */
1342 
1343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1344