use insert function instead of for loop
[LibreOffice.git] / ucb / source / core / ucbcmds.cxx
blob7995e6746629170cd4911babb4f4636ce9add927
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 <osl/diagnose.h>
21 #include <comphelper/propertysequence.hxx>
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/exc_hlp.hxx>
24 #include <rtl/ustring.hxx>
25 #include <com/sun/star/uno/XInterface.hpp>
26 #include <com/sun/star/beans/PropertyState.hpp>
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <com/sun/star/beans/XPropertySetInfo.hpp>
29 #include <com/sun/star/io/IOException.hpp>
30 #include <com/sun/star/io/Pipe.hpp>
31 #include <com/sun/star/io/XActiveDataSink.hpp>
32 #include <com/sun/star/io/XOutputStream.hpp>
33 #include <com/sun/star/io/XSeekable.hpp>
34 #include <com/sun/star/lang/IllegalArgumentException.hpp>
35 #include <com/sun/star/sdbc/SQLException.hpp>
36 #include <com/sun/star/sdbc/XRow.hpp>
37 #include <com/sun/star/task/XInteractionHandler.hpp>
38 #include <com/sun/star/ucb/CommandEnvironment.hpp>
39 #include <com/sun/star/ucb/CommandFailedException.hpp>
40 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
41 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
42 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
43 #include <com/sun/star/ucb/InsertCommandArgument2.hpp>
44 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
45 #include <com/sun/star/ucb/NameClash.hpp>
46 #include <com/sun/star/ucb/NameClashException.hpp>
47 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
48 #include <com/sun/star/ucb/OpenMode.hpp>
49 #include <com/sun/star/ucb/TransferInfo2.hpp>
50 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
51 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
52 #include <com/sun/star/ucb/XCommandInfo.hpp>
53 #include <com/sun/star/ucb/XContentAccess.hpp>
54 #include <com/sun/star/ucb/XContentCreator.hpp>
55 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
56 #include <com/sun/star/ucb/XInteractionSupplyName.hpp>
57 #include <com/sun/star/uno/Any.hxx>
58 #include <com/sun/star/uno/Sequence.hxx>
59 #include <ucbhelper/cancelcommandexecution.hxx>
60 #include <ucbhelper/simplenameclashresolverequest.hxx>
61 #include <utility>
62 #include "ucbcmds.hxx"
63 #include "ucb.hxx"
65 using namespace com::sun::star;
67 namespace
69 // Helper to provide defaults for type and attributes (save some typing)
70 beans::Property makeProperty(const OUString& n, sal_Int32 h, uno::Type t = {}, sal_Int16 a = {})
72 return { n, h, t, a };
75 // struct TransferCommandContext.
78 struct TransferCommandContext
80 uno::Reference< uno::XComponentContext > m_xContext;
81 uno::Reference< ucb::XCommandProcessor > xProcessor;
82 uno::Reference< ucb::XCommandEnvironment > xEnv;
83 uno::Reference< ucb::XCommandEnvironment > xOrigEnv;
84 ucb::GlobalTransferCommandArgument2 aArg;
86 TransferCommandContext(
87 uno::Reference< uno::XComponentContext > xContext,
88 uno::Reference< ucb::XCommandProcessor > _xProcessor,
89 uno::Reference< ucb::XCommandEnvironment > _xEnv,
90 uno::Reference< ucb::XCommandEnvironment > _xOrigEnv,
91 ucb::GlobalTransferCommandArgument2 _aArg )
92 : m_xContext(std::move( xContext )), xProcessor(std::move( _xProcessor )), xEnv(std::move( _xEnv )),
93 xOrigEnv(std::move( _xOrigEnv )), aArg(std::move( _aArg )) {}
99 class InteractionHandlerProxy :
100 public cppu::WeakImplHelper< task::XInteractionHandler >
102 uno::Reference< task::XInteractionHandler > m_xOrig;
104 public:
105 explicit InteractionHandlerProxy(
106 uno::Reference< task::XInteractionHandler > xOrig )
107 : m_xOrig(std::move( xOrig )) {}
109 // XInteractionHandler methods.
110 virtual void SAL_CALL handle(
111 const uno::Reference< task::XInteractionRequest >& Request ) override;
115 // virtual
116 void SAL_CALL InteractionHandlerProxy::handle(
117 const uno::Reference< task::XInteractionRequest >& Request )
119 if ( !m_xOrig.is() )
120 return;
122 // Filter unwanted requests by just not handling them.
123 uno::Any aRequest = Request->getRequest();
125 // "transfer"
126 ucb::InteractiveBadTransferURLException aBadTransferURLEx;
127 if ( aRequest >>= aBadTransferURLEx )
129 return;
131 else
133 // "transfer"
134 ucb::UnsupportedNameClashException aUnsupportedNameClashEx;
135 if ( aRequest >>= aUnsupportedNameClashEx )
137 if ( aUnsupportedNameClashEx.NameClash
138 != ucb::NameClash::ERROR )
139 return;
141 else
143 // "insert"
144 ucb::NameClashException aNameClashEx;
145 if ( aRequest >>= aNameClashEx )
147 return;
149 else
151 // "transfer"
152 ucb::UnsupportedCommandException aUnsupportedCommandEx;
153 if ( aRequest >>= aUnsupportedCommandEx )
155 return;
161 // not filtered; let the original handler do the work.
162 m_xOrig->handle( Request );
168 class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink >
170 uno::Reference< io::XInputStream > m_xStream;
172 public:
173 // XActiveDataSink methods.
174 virtual void SAL_CALL setInputStream(
175 const uno::Reference< io::XInputStream >& aStream ) override;
176 virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override;
180 // virtual
181 void SAL_CALL ActiveDataSink::setInputStream(
182 const uno::Reference< io::XInputStream >& aStream )
184 m_xStream = aStream;
188 // virtual
189 uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream()
191 return m_xStream;
197 class CommandProcessorInfo :
198 public cppu::WeakImplHelper< ucb::XCommandInfo >
200 uno::Sequence< ucb::CommandInfo > m_xInfo;
202 public:
203 CommandProcessorInfo();
205 // XCommandInfo methods
206 virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override;
207 virtual ucb::CommandInfo SAL_CALL
208 getCommandInfoByName( const OUString& Name ) override;
209 virtual ucb::CommandInfo SAL_CALL
210 getCommandInfoByHandle( sal_Int32 Handle ) override;
211 virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override;
212 virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override;
216 CommandProcessorInfo::CommandProcessorInfo()
217 : m_xInfo{
218 ucb::CommandInfo(
219 GETCOMMANDINFO_NAME, // Name
220 GETCOMMANDINFO_HANDLE, // Handle
221 cppu::UnoType<void>::get() ), // ArgType
222 ucb::CommandInfo(
223 GLOBALTRANSFER_NAME, // Name
224 GLOBALTRANSFER_HANDLE, // Handle
225 cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ), // ArgType
226 ucb::CommandInfo(
227 CHECKIN_NAME, // Name
228 CHECKIN_HANDLE, // Handle
229 cppu::UnoType<ucb::CheckinArgument>::get() ) } // ArgType
234 // virtual
235 uno::Sequence< ucb::CommandInfo > SAL_CALL
236 CommandProcessorInfo::getCommands()
238 return m_xInfo;
242 // virtual
243 ucb::CommandInfo SAL_CALL
244 CommandProcessorInfo::getCommandInfoByName( const OUString& Name )
246 auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo),
247 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
248 if (pInfo != std::cend(m_xInfo))
249 return *pInfo;
251 throw ucb::UnsupportedCommandException();
255 // virtual
256 ucb::CommandInfo SAL_CALL
257 CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle )
259 auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo),
260 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
261 if (pInfo != std::cend(m_xInfo))
262 return *pInfo;
264 throw ucb::UnsupportedCommandException();
268 // virtual
269 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName(
270 const OUString& Name )
272 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
273 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
277 // virtual
278 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle )
280 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
281 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
285 OUString createDesiredName(
286 const OUString & rSourceURL, const OUString & rNewTitle )
288 OUString aName( rNewTitle );
289 if ( aName.isEmpty() )
291 // calculate name using source URL
293 // @@@ It's not guaranteed that slashes contained in the URL are
294 // actually path separators. This depends on the fact whether the
295 // URL is hierarchical. Only then the slashes are path separators.
296 // Therefore this algorithm is not guaranteed to work! But, ATM
297 // I don't know a better solution. It would have been better to
298 // have a member for the clashing name in
299 // UnsupportedNameClashException...
301 sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' );
302 bool bTrailingSlash = false;
303 if ( nLastSlash == rSourceURL.getLength() - 1 )
305 nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash );
306 bTrailingSlash = true;
309 if ( nLastSlash != -1 )
311 if ( bTrailingSlash )
312 aName = rSourceURL.copy(
313 nLastSlash + 1,
314 rSourceURL.getLength() - nLastSlash - 2 );
315 else
316 aName = rSourceURL.copy( nLastSlash + 1 );
318 else
320 aName = rSourceURL;
323 // query, fragment present?
324 sal_Int32 nPos = aName.indexOf( '?' );
325 if ( nPos == -1 )
326 nPos = aName.indexOf( '#' );
328 if ( nPos != -1 )
329 aName = aName.copy( 0, nPos );
331 return aName;
334 OUString createDesiredName(
335 const ucb::GlobalTransferCommandArgument & rArg )
337 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
340 OUString createDesiredName(
341 const ucb::TransferInfo & rArg )
343 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
347 enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN };
349 NameClashContinuation interactiveNameClashResolve(
350 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
351 const OUString & rTargetURL,
352 const OUString & rClashingName,
353 /* [out] */ uno::Any & rException,
354 /* [out] */ OUString & rNewName )
356 rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest(
357 new ucbhelper::SimpleNameClashResolveRequest(
358 rTargetURL, // target folder URL
359 rClashingName
360 ) );
362 rException = xRequest->getRequest();
363 if ( xEnv.is() )
365 uno::Reference< task::XInteractionHandler > xIH
366 = xEnv->getInteractionHandler();
367 if ( xIH.is() )
370 xIH->handle( xRequest );
372 rtl::Reference< ucbhelper::InteractionContinuation >
373 xSelection( xRequest->getSelection() );
375 if ( xSelection.is() )
377 // Handler handled the request.
378 uno::Reference< task::XInteractionAbort > xAbort(
379 xSelection->getXWeak(), uno::UNO_QUERY );
380 if ( xAbort.is() )
382 // Abort.
383 return ABORT;
385 else
387 uno::Reference<
388 ucb::XInteractionReplaceExistingData >
389 xReplace(
390 xSelection->getXWeak(), uno::UNO_QUERY );
391 if ( xReplace.is() )
393 // Try again: Replace existing data.
394 return OVERWRITE;
396 else
398 uno::Reference<
399 ucb::XInteractionSupplyName >
400 xSupplyName(
401 xSelection->getXWeak(), uno::UNO_QUERY );
402 if ( xSupplyName.is() )
404 // Try again: Use new name.
405 rNewName = xRequest->getNewName();
406 return NEW_NAME;
408 else
410 OSL_FAIL( "Unknown interaction continuation!" );
411 return UNKNOWN;
418 return NOT_HANDLED;
421 /// @throws uno::RuntimeException
422 bool setTitle(
423 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor,
424 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
425 const OUString & rNewTitle )
429 uno::Sequence< beans::PropertyValue > aPropValues{ { /* Name */ u"Title"_ustr,
430 /* Handle */ -1,
431 /* Value */ uno::Any(rNewTitle),
432 /* State */ {} } };
434 ucb::Command aSetPropsCommand(
435 u"setPropertyValues"_ustr,
437 uno::Any( aPropValues ) );
439 uno::Any aResult
440 = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv );
442 uno::Sequence< uno::Any > aErrors;
443 aResult >>= aErrors;
445 OSL_ENSURE( aErrors.getLength() == 1,
446 "getPropertyValues return value invalid!" );
448 if ( aErrors[ 0 ].hasValue() )
450 // error occurred.
451 OSL_FAIL( "error setting Title property!" );
452 return false;
455 catch ( uno::RuntimeException const & )
457 throw;
459 catch ( uno::Exception const & )
461 return false;
464 return true;
467 /// @throws uno::Exception
468 uno::Reference< ucb::XContent > createNew(
469 const TransferCommandContext & rContext,
470 const uno::Reference< ucb::XContent > & xTarget,
471 bool bSourceIsFolder,
472 bool bSourceIsDocument,
473 bool bSourceIsLink )
477 // (1) Obtain creatable types from target.
480 // First, try it using "CreatabeleContentsInfo" property and
481 // "createNewContent" command -> the "new" way.
483 uno::Reference< ucb::XCommandProcessor > xCommandProcessorT(
484 xTarget, uno::UNO_QUERY );
485 if ( !xCommandProcessorT.is() )
487 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
489 {"Folder", uno::Any(rContext.aArg.TargetURL)}
490 }));
491 ucbhelper::cancelCommandExecution(
492 ucb::IOErrorCode_CANT_CREATE,
493 aArgs,
494 rContext.xOrigEnv,
495 u"Target is no XCommandProcessor!"_ustr,
496 rContext.xProcessor );
497 // Unreachable
500 uno::Sequence< beans::Property > aPropsToObtain{ makeProperty(u"CreatableContentsInfo"_ustr, -1) };
502 ucb::Command aGetPropsCommand(
503 u"getPropertyValues"_ustr,
505 uno::Any( aPropsToObtain ) );
507 uno::Reference< sdbc::XRow > xRow;
508 xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
510 uno::Sequence< ucb::ContentInfo > aTypesInfo;
511 bool bGotTypesInfo = false;
513 if ( xRow.is() )
515 uno::Any aValue = xRow->getObject(
516 1, uno::Reference< container::XNameAccess >() );
517 if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) )
519 bGotTypesInfo = true;
523 uno::Reference< ucb::XContentCreator > xCreator;
525 if ( !bGotTypesInfo )
527 // Second, try it using XContentCreator interface -> the "old" way (not
528 // providing the chance to supply an XCommandEnvironment.
530 xCreator.set( xTarget, uno::UNO_QUERY );
532 if ( !xCreator.is() )
534 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
536 {"Folder", uno::Any(rContext.aArg.TargetURL)}
537 }));
538 ucbhelper::cancelCommandExecution(
539 ucb::IOErrorCode_CANT_CREATE,
540 aArgs,
541 rContext.xOrigEnv,
542 u"Target is no XContentCreator!"_ustr,
543 rContext.xProcessor );
544 // Unreachable
547 aTypesInfo = xCreator->queryCreatableContentsInfo();
550 if ( !aTypesInfo.hasElements() )
552 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
554 {"Folder", uno::Any(rContext.aArg.TargetURL)}
555 }));
556 ucbhelper::cancelCommandExecution(
557 ucb::IOErrorCode_CANT_CREATE,
558 aArgs,
559 rContext.xOrigEnv,
560 u"No types creatable!"_ustr,
561 rContext.xProcessor );
562 // Unreachable
565 // (2) Try to find a matching target type for the source object.
567 std::function<bool(const sal_Int32)> lCompare;
569 if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK )
571 // Create link
572 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
574 else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) ||
575 ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) )
577 // Copy / Move
578 // Is source a link? Create link in target folder then.
579 if ( bSourceIsLink )
581 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
583 else
585 // (not a and not b) or (a and b)
586 // not( a or b) or (a and b)
587 lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) {
588 return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) )
589 && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ;
593 else
595 ucbhelper::cancelCommandExecution(
596 uno::Any( lang::IllegalArgumentException(
597 u"Unknown transfer operation!"_ustr,
598 rContext.xProcessor,
599 -1 ) ),
600 rContext.xOrigEnv );
601 // Unreachable
604 uno::Reference< ucb::XContent > xNew;
605 auto pTypeInfo = std::find_if(std::cbegin(aTypesInfo), std::cend(aTypesInfo),
606 [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); });
607 if (pTypeInfo != std::cend(aTypesInfo))
609 // (3) Create a new, empty object of matched type.
611 if ( !xCreator.is() )
613 // First, try it using "CreatabeleContentsInfo" property and
614 // "createNewContent" command -> the "new" way.
615 ucb::Command aCreateNewCommand(
616 u"createNewContent"_ustr,
618 uno::Any( *pTypeInfo ) );
620 xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv )
621 >>= xNew;
623 else
625 // Second, try it using XContentCreator interface -> the "old"
626 // way (not providing the chance to supply an XCommandEnvironment.
628 xNew = xCreator->createNewContent( *pTypeInfo );
631 if ( !xNew.is() )
633 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
635 {"Folder", uno::Any(rContext.aArg.TargetURL)}
636 }));
637 ucbhelper::cancelCommandExecution(
638 ucb::IOErrorCode_CANT_CREATE,
639 aArgs,
640 rContext.xOrigEnv,
641 u"createNewContent failed!"_ustr,
642 rContext.xProcessor );
643 // Unreachable
647 return xNew;
650 /// @throws uno::Exception
651 void transferProperties(
652 const TransferCommandContext & rContext,
653 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS,
654 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN )
656 ucb::Command aGetPropertySetInfoCommand(
657 u"getPropertySetInfo"_ustr,
659 uno::Any() );
661 uno::Reference< beans::XPropertySetInfo > xInfo;
662 xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv )
663 >>= xInfo;
665 if ( !xInfo.is() )
667 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
669 {"Uri", uno::Any(rContext.aArg.SourceURL)}
670 }));
671 ucbhelper::cancelCommandExecution(
672 ucb::IOErrorCode_CANT_READ,
673 aArgs,
674 rContext.xOrigEnv,
675 u"Unable to get propertyset info from source object!"_ustr,
676 rContext.xProcessor );
677 // Unreachable
680 uno::Sequence< beans::Property > aAllProps = xInfo->getProperties();
682 ucb::Command aGetPropsCommand1(
683 u"getPropertyValues"_ustr,
685 uno::Any( aAllProps ) );
687 uno::Reference< sdbc::XRow > xRow1;
688 xCommandProcessorS->execute(
689 aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1;
691 if ( !xRow1.is() )
693 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
695 {"Uri", uno::Any(rContext.aArg.SourceURL)}
696 }));
697 ucbhelper::cancelCommandExecution(
698 ucb::IOErrorCode_CANT_READ,
699 aArgs,
700 rContext.xOrigEnv,
701 u"Unable to get properties from source object!"_ustr,
702 rContext.xProcessor );
703 // Unreachable
706 // Assemble data structure for setPropertyValues command.
708 // Note: Make room for additional Title and TargetURL too. -> + 2
709 uno::Sequence< beans::PropertyValue > aPropValues(
710 aAllProps.getLength() + 2 );
711 auto pPropValues = aPropValues.getArray();
713 bool bHasTitle = rContext.aArg.NewTitle.isEmpty();
714 bool bHasTargetURL = ( rContext.aArg.Operation
715 != ucb::TransferCommandOperation_LINK );
717 sal_Int32 nWritePos = 0;
718 for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m )
720 const beans::Property & rCurrProp = aAllProps[ m ];
721 beans::PropertyValue & rCurrValue = pPropValues[ nWritePos ];
723 uno::Any aValue;
725 if ( rCurrProp.Name == "Title" )
727 // Supply new title, if given.
728 if ( !bHasTitle )
730 bHasTitle = true;
731 aValue <<= rContext.aArg.NewTitle;
734 else if ( rCurrProp.Name == "TargetURL" )
736 // Supply source URL as link target for the new link to create.
737 if ( !bHasTargetURL )
739 bHasTargetURL = true;
740 aValue <<= rContext.aArg.SourceURL;
744 if ( !aValue.hasValue() )
748 aValue = xRow1->getObject(
749 m + 1, uno::Reference< container::XNameAccess >() );
751 catch ( sdbc::SQLException const & )
753 // Argh! But try to bring things to an end. Perhaps the
754 // mad property is not really important...
758 if ( aValue.hasValue() )
760 rCurrValue.Name = rCurrProp.Name;
761 rCurrValue.Handle = rCurrProp.Handle;
762 rCurrValue.Value = std::move(aValue);
764 nWritePos++;
768 // Title needed, but not set yet?
769 if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() )
771 pPropValues[ nWritePos ].Name = "Title";
772 pPropValues[ nWritePos ].Handle = -1;
773 pPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle;
775 nWritePos++;
778 // TargetURL needed, but not set yet?
779 if ( !bHasTargetURL && ( rContext.aArg.Operation
780 == ucb::TransferCommandOperation_LINK ) )
782 pPropValues[ nWritePos ].Name = "TargetURL";
783 pPropValues[ nWritePos ].Handle = -1;
784 pPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL;
786 nWritePos++;
789 aPropValues.realloc( nWritePos );
791 // Set properties at new object.
793 ucb::Command aSetPropsCommand(
794 u"setPropertyValues"_ustr,
796 uno::Any( aPropValues ) );
798 xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv );
800 // @@@ What to do with source props that are not supported by the
801 // new object? addProperty ???
804 /// @throws uno::Exception
805 uno::Reference< io::XInputStream > getInputStream(
806 const TransferCommandContext & rContext,
807 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
809 uno::Reference< io::XInputStream > xInputStream;
812 // (1) Try to get data as XInputStream via XActiveDataSink.
817 uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink;
819 ucb::OpenCommandArgument2 aArg;
820 aArg.Mode = ucb::OpenMode::DOCUMENT;
821 aArg.Priority = 0; // unused
822 aArg.Sink = xSink;
823 aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused
825 ucb::Command aOpenCommand(
826 u"open"_ustr,
828 uno::Any( aArg ) );
830 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
831 xInputStream = xSink->getInputStream();
833 catch ( uno::RuntimeException const & )
835 throw;
837 catch ( uno::Exception const & )
839 // will be handled below.
842 if ( !xInputStream.is() )
846 // (2) Try to get data via XOutputStream.
851 uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW );
853 ucb::OpenCommandArgument2 aArg;
854 aArg.Mode = ucb::OpenMode::DOCUMENT;
855 aArg.Priority = 0; // unused
856 aArg.Sink = xOutputStream;
857 aArg.Properties = uno::Sequence< beans::Property >( 0 );
859 ucb::Command aOpenCommand(
860 u"open"_ustr,
862 uno::Any( aArg ) );
864 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
866 xInputStream.set( xOutputStream, uno::UNO_QUERY );
868 catch ( uno::RuntimeException const & )
870 throw;
872 catch ( uno::Exception const & )
874 OSL_FAIL( "unable to get input stream from document!" );
878 return xInputStream;
881 /// @throws uno::Exception
882 uno::Reference< sdbc::XResultSet > getResultSet(
883 const TransferCommandContext & rContext,
884 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
886 uno::Reference< sdbc::XResultSet > xResultSet;
888 ucb::OpenCommandArgument2 aArg;
889 aArg.Mode = ucb::OpenMode::ALL;
890 aArg.Priority = 0; // unused
891 aArg.Sink = nullptr;
892 aArg.Properties = { makeProperty(u"IsFolder"_ustr, -1 /* unknown */),
893 makeProperty(u"IsDocument"_ustr, -1 /* unknown */),
894 makeProperty(u"TargetURL"_ustr, -1 /* unknown */) };
896 ucb::Command aOpenCommand( u"open"_ustr,
898 uno::Any( aArg ) );
901 uno::Reference< ucb::XDynamicResultSet > xSet;
902 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet;
904 if ( xSet.is() )
905 xResultSet = xSet->getStaticResultSet();
907 catch ( uno::RuntimeException const & )
909 throw;
911 catch ( uno::Exception const & )
913 OSL_FAIL( "unable to get result set from folder!" );
916 return xResultSet;
919 /// @throws uno::Exception
920 void handleNameClashRename(
921 const TransferCommandContext & rContext,
922 const uno::Reference< ucb::XContent > & xNew,
923 const uno::Reference<
924 ucb::XCommandProcessor > & xCommandProcessorN,
925 const uno::Reference<
926 ucb::XCommandProcessor > & xCommandProcessorS,
927 /* [inout] */ uno::Reference< io::XInputStream > & xInputStream )
929 sal_Int32 nTry = 0;
931 // Obtain old title.
932 uno::Sequence< beans::Property > aProps{ makeProperty(u"Title"_ustr, -1) };
934 ucb::Command aGetPropsCommand(
935 u"getPropertyValues"_ustr,
937 uno::Any( aProps ) );
939 uno::Reference< sdbc::XRow > xRow;
940 xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
942 if ( !xRow.is() )
944 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
946 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
947 }));
948 ucbhelper::cancelCommandExecution(
949 ucb::IOErrorCode_CANT_READ,
950 aArgs,
951 rContext.xOrigEnv,
952 u"Unable to get properties from new object!"_ustr,
953 rContext.xProcessor );
954 // Unreachable
957 OUString aOldTitle = xRow->getString( 1 );
958 if ( aOldTitle.isEmpty() )
960 ucbhelper::cancelCommandExecution(
961 uno::Any( beans::UnknownPropertyException(
962 u"Unable to get property 'Title' from new object!"_ustr,
963 rContext.xProcessor ) ),
964 rContext.xOrigEnv );
965 // Unreachable
968 // Some pseudo-intelligence for not destroying file extensions.
969 OUString aOldTitlePre;
970 OUString aOldTitlePost;
971 sal_Int32 nPos = aOldTitle.lastIndexOf( '.' );
972 if ( nPos != -1 )
974 aOldTitlePre = aOldTitle.copy( 0, nPos );
975 aOldTitlePost = aOldTitle.copy( nPos );
977 else
978 aOldTitlePre = aOldTitle;
980 if ( nPos > 0 )
981 aOldTitlePre += "_";
983 bool bContinue = true;
986 nTry++;
988 OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) +
989 aOldTitlePost;
991 // Set new title
992 setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle );
994 // Retry inserting the content.
997 // Previous try may have read from stream. Seek to begin (if
998 // optional interface XSeekable is supported) or get a new stream.
999 if ( xInputStream.is() )
1001 uno::Reference< io::XSeekable > xSeekable(
1002 xInputStream, uno::UNO_QUERY );
1003 if ( xSeekable.is() )
1007 xSeekable->seek( 0 );
1009 catch ( lang::IllegalArgumentException const & )
1011 xInputStream.clear();
1013 catch ( io::IOException const & )
1015 xInputStream.clear();
1018 else
1019 xInputStream.clear();
1021 if ( !xInputStream.is() )
1023 xInputStream
1024 = getInputStream( rContext, xCommandProcessorS );
1025 if ( !xInputStream.is() )
1027 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1029 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
1030 }));
1031 ucbhelper::cancelCommandExecution(
1032 ucb::IOErrorCode_CANT_READ,
1033 aArgs,
1034 rContext.xOrigEnv,
1035 u"Got no data stream from source!"_ustr,
1036 rContext.xProcessor );
1037 // Unreachable
1042 ucb::InsertCommandArgument2 aArg;
1043 aArg.Data = xInputStream;
1044 aArg.ReplaceExisting = false;
1046 ucb::Command aInsertCommand(
1047 u"insert"_ustr,
1049 uno::Any( aArg ) );
1051 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1053 // Success!
1054 bContinue = false;
1056 catch ( uno::RuntimeException const & )
1058 throw;
1060 catch ( uno::Exception const & )
1064 while ( bContinue && ( nTry < 50 ) );
1066 if ( nTry == 50 )
1068 ucbhelper::cancelCommandExecution(
1069 uno::Any(
1070 ucb::UnsupportedNameClashException(
1071 u"Unable to resolve name clash!"_ustr,
1072 rContext.xProcessor,
1073 ucb::NameClash::RENAME ) ),
1074 rContext.xOrigEnv );
1075 // Unreachable
1079 /// @throws uno::Exception
1080 void globalTransfer_(
1081 const TransferCommandContext & rContext,
1082 const uno::Reference< ucb::XContent > & xSource,
1083 const uno::Reference< ucb::XContent > & xTarget,
1084 const uno::Reference< sdbc::XRow > & xSourceProps )
1086 // IsFolder: property is required.
1087 bool bSourceIsFolder = xSourceProps->getBoolean( 1 );
1088 if ( !bSourceIsFolder && xSourceProps->wasNull() )
1090 ucbhelper::cancelCommandExecution(
1091 uno::Any( beans::UnknownPropertyException(
1092 u"Unable to get property 'IsFolder' from source object!"_ustr,
1093 rContext.xProcessor ) ),
1094 rContext.xOrigEnv );
1095 // Unreachable
1098 // IsDocument: property is required.
1099 bool bSourceIsDocument = xSourceProps->getBoolean( 2 );
1100 if ( !bSourceIsDocument && xSourceProps->wasNull() )
1102 ucbhelper::cancelCommandExecution(
1103 uno::Any( beans::UnknownPropertyException(
1104 u"Unable to get property 'IsDocument' from source object!"_ustr,
1105 rContext.xProcessor ) ),
1106 rContext.xOrigEnv );
1107 // Unreachable
1110 // TargetURL: property is optional.
1111 bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty();
1114 // (1) Try to find a matching target type for the source object and
1115 // create a new, empty object of that type.
1118 uno::Reference< ucb::XContent > xNew = createNew( rContext,
1119 xTarget,
1120 bSourceIsFolder,
1121 bSourceIsDocument,
1122 bSourceIsLink );
1123 if ( !xNew.is() )
1125 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1127 {"Folder", uno::Any(rContext.aArg.TargetURL)}
1128 }));
1129 ucbhelper::cancelCommandExecution(
1130 ucb::IOErrorCode_CANT_CREATE,
1131 aArgs,
1132 rContext.xOrigEnv,
1133 u"No matching content type at target!"_ustr,
1134 rContext.xProcessor );
1135 // Unreachable
1139 // (2) Transfer property values from source to new object.
1142 uno::Reference< ucb::XCommandProcessor > xCommandProcessorN(
1143 xNew, uno::UNO_QUERY );
1144 if ( !xCommandProcessorN.is() )
1146 uno::Any aProps(beans::PropertyValue(
1147 u"Uri"_ustr,
1149 uno::Any(
1150 xNew->getIdentifier()->
1151 getContentIdentifier()),
1152 beans::PropertyState_DIRECT_VALUE));
1153 ucbhelper::cancelCommandExecution(
1154 ucb::IOErrorCode_CANT_WRITE,
1155 uno::Sequence< uno::Any >(&aProps, 1),
1156 rContext.xOrigEnv,
1157 u"New content is not a XCommandProcessor!"_ustr,
1158 rContext.xProcessor );
1159 // Unreachable
1162 // Obtain all properties from source.
1164 uno::Reference< ucb::XCommandProcessor > xCommandProcessorS(
1165 xSource, uno::UNO_QUERY );
1166 if ( !xCommandProcessorS.is() )
1168 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1170 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1171 }));
1172 ucbhelper::cancelCommandExecution(
1173 ucb::IOErrorCode_CANT_READ,
1174 aArgs,
1175 rContext.xOrigEnv,
1176 u"Source content is not a XCommandProcessor!"_ustr,
1177 rContext.xProcessor );
1178 // Unreachable
1181 transferProperties( rContext, xCommandProcessorS, xCommandProcessorN );
1184 // (3) Try to obtain a data stream from source.
1187 uno::Reference< io::XInputStream > xInputStream;
1189 if ( bSourceIsDocument && ( rContext.aArg.Operation
1190 != ucb::TransferCommandOperation_LINK ) )
1191 xInputStream = getInputStream( rContext, xCommandProcessorS );
1194 // (4) Try to obtain a resultset (children) from source.
1197 uno::Reference< sdbc::XResultSet > xResultSet;
1199 if ( bSourceIsFolder && ( rContext.aArg.Operation
1200 != ucb::TransferCommandOperation_LINK ) )
1201 xResultSet = getResultSet( rContext, xCommandProcessorS );
1204 // (5) Insert (store) new content.
1207 ucb::InsertCommandArgument2 aArg;
1208 aArg.Data = xInputStream;
1209 aArg.MimeType = rContext.aArg.MimeType;
1210 aArg.DocumentId = rContext.aArg.DocumentId;
1212 switch ( rContext.aArg.NameClash )
1214 case ucb::NameClash::OVERWRITE:
1215 aArg.ReplaceExisting = true;
1216 break;
1218 case ucb::NameClash::ERROR:
1219 case ucb::NameClash::RENAME:
1220 case ucb::NameClash::KEEP: // deprecated
1221 case ucb::NameClash::ASK:
1222 aArg.ReplaceExisting = false;
1223 break;
1225 default:
1226 aArg.ReplaceExisting = false;
1227 OSL_FAIL( "Unknown nameclash directive!" );
1228 break;
1231 OUString aDesiredName = createDesiredName( rContext.aArg );
1233 bool bRetry;
1236 bRetry = false;
1240 ucb::Command aInsertCommand(
1241 u"insert"_ustr,
1243 uno::Any( aArg ) );
1245 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1247 catch ( ucb::UnsupportedNameClashException const & exc )
1249 OSL_ENSURE( !aArg.ReplaceExisting,
1250 "BUG: UnsupportedNameClashException not allowed here!" );
1252 if (exc.NameClash != ucb::NameClash::ERROR) {
1253 OSL_FAIL( "BUG: NameClash::ERROR expected!" );
1256 // No chance to solve name clashes, because I'm not able to detect
1257 // whether there is one.
1258 throw ucb::UnsupportedNameClashException(
1259 u"Unable to resolve name clashes, no chance to detect "
1260 "that there is one!"_ustr,
1261 rContext.xProcessor,
1262 rContext.aArg.NameClash );
1264 catch ( ucb::NameClashException const & )
1266 // The 'insert' command throws a NameClashException if the parameter
1267 // ReplaceExisting of the command's argument was set to false and
1268 // there exists a resource with a clashing name in the target folder
1269 // of the operation.
1271 // 'insert' command has no direct support for name clashes other
1272 // than ERROR ( ReplaceExisting == false ) and OVERWRITE
1273 // ( ReplaceExisting == true ). So we have to implement the
1274 // other name clash handling directives on top of the content.
1276 // @@@ 'insert' command should be extended that it accepts a
1277 // name clash handling directive, exactly like 'transfer' command.
1279 switch ( rContext.aArg.NameClash )
1281 case ucb::NameClash::OVERWRITE:
1283 ucbhelper::cancelCommandExecution(
1284 uno::Any(
1285 ucb::UnsupportedNameClashException(
1286 u"BUG: insert + replace == true MUST NOT "
1287 "throw NameClashException."_ustr,
1288 rContext.xProcessor,
1289 rContext.aArg.NameClash ) ),
1290 rContext.xOrigEnv );
1291 [[fallthrough]]; // Unreachable
1294 case ucb::NameClash::ERROR:
1295 throw;
1297 case ucb::NameClash::RENAME:
1299 // "invent" a new valid title.
1300 handleNameClashRename( rContext,
1301 xNew,
1302 xCommandProcessorN,
1303 xCommandProcessorS,
1304 xInputStream );
1305 break;
1308 case ucb::NameClash::ASK:
1310 uno::Any aExc;
1311 OUString aNewTitle;
1312 NameClashContinuation eCont
1313 = interactiveNameClashResolve(
1314 rContext.xOrigEnv, // always use original environment!
1315 rContext.aArg.TargetURL, // target folder URL
1316 aDesiredName,
1317 aExc,
1318 aNewTitle );
1320 switch ( eCont )
1322 case NOT_HANDLED:
1323 // Not handled.
1324 cppu::throwException( aExc );
1325 [[fallthrough]]; // break;
1327 case UNKNOWN:
1328 // Handled, but not clear, how...
1329 // fall through intended.
1331 case ABORT:
1332 throw ucb::CommandFailedException(
1333 u"abort requested via interaction "
1334 "handler"_ustr,
1335 uno::Reference< uno::XInterface >(),
1336 aExc );
1337 // break;
1339 case OVERWRITE:
1340 OSL_ENSURE( !aArg.ReplaceExisting,
1341 "Hu? ReplaceExisting already true?"
1343 aArg.ReplaceExisting = true;
1344 bRetry = true;
1345 break;
1347 case NEW_NAME:
1349 // set new name -> set "Title" property...
1350 if ( setTitle( xCommandProcessorN,
1351 rContext.xEnv,
1352 aNewTitle ) )
1354 // remember suggested title...
1355 aDesiredName = aNewTitle;
1357 // ... and try again.
1358 bRetry = true;
1360 else
1362 // error setting title. Abort.
1363 throw ucb::CommandFailedException(
1364 u"error setting Title property!"_ustr,
1365 uno::Reference< uno::XInterface >(),
1366 aExc );
1368 break;
1372 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1374 break;
1376 case ucb::NameClash::KEEP: // deprecated
1377 default:
1379 ucbhelper::cancelCommandExecution(
1380 uno::Any(
1381 ucb::UnsupportedNameClashException(
1382 u"default action, don't know how to "
1383 "handle name clash"_ustr,
1384 rContext.xProcessor,
1385 rContext.aArg.NameClash ) ),
1386 rContext.xOrigEnv );
1387 // Unreachable
1392 while ( bRetry );
1395 // (6) Process children of source.
1398 if ( xResultSet.is() )
1402 // Iterate over children...
1404 uno::Reference< sdbc::XRow > xChildRow(
1405 xResultSet, uno::UNO_QUERY );
1407 if ( !xChildRow.is() )
1409 uno::Any aProps(
1410 beans::PropertyValue(
1411 u"Uri"_ustr,
1413 uno::Any(rContext.aArg.SourceURL),
1414 beans::PropertyState_DIRECT_VALUE));
1415 ucbhelper::cancelCommandExecution(
1416 ucb::IOErrorCode_CANT_READ,
1417 uno::Sequence< uno::Any >(&aProps, 1),
1418 rContext.xOrigEnv,
1419 u"Unable to get properties from children of source!"_ustr,
1420 rContext.xProcessor );
1421 // Unreachable
1424 uno::Reference< ucb::XContentAccess > xChildAccess(
1425 xResultSet, uno::UNO_QUERY );
1427 if ( !xChildAccess.is() )
1429 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1431 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1432 }));
1433 ucbhelper::cancelCommandExecution(
1434 ucb::IOErrorCode_CANT_READ,
1435 aArgs,
1436 rContext.xOrigEnv,
1437 u"Unable to get children of source!"_ustr,
1438 rContext.xProcessor );
1439 // Unreachable
1442 if ( xResultSet->first() )
1444 ucb::GlobalTransferCommandArgument2 aTransArg(
1445 rContext.aArg.Operation,
1446 OUString(), // SourceURL; filled later
1447 xNew->getIdentifier()
1448 ->getContentIdentifier(), // TargetURL
1449 OUString(), // NewTitle;
1450 rContext.aArg.NameClash,
1451 rContext.aArg.MimeType,
1452 rContext.aArg.DocumentId);
1454 TransferCommandContext aSubCtx(
1455 rContext.m_xContext,
1456 rContext.xProcessor,
1457 rContext.xEnv,
1458 rContext.xOrigEnv,
1459 std::move(aTransArg) );
1462 uno::Reference< ucb::XContent > xChild
1463 = xChildAccess->queryContent();
1464 if ( xChild.is() )
1466 // Recursion!
1468 aSubCtx.aArg.SourceURL
1469 = xChild->getIdentifier()->getContentIdentifier();
1471 globalTransfer_( aSubCtx,
1472 xChild,
1473 xNew,
1474 xChildRow );
1477 while ( xResultSet->next() );
1480 catch ( sdbc::SQLException const & )
1485 try {
1486 uno::Reference< ucb::XCommandProcessor > xcp(
1487 xTarget, uno::UNO_QUERY );
1489 uno::Any aAny;
1490 uno::Reference< ucb::XCommandInfo > xci;
1491 if(xcp.is())
1492 aAny =
1493 xcp->execute(
1494 ucb::Command(
1495 u"getCommandInfo"_ustr,
1497 uno::Any()),
1499 rContext.xEnv );
1501 static constexpr OUString cmdName(u"flush"_ustr);
1502 if((aAny >>= xci) && xci->hasCommandByName(cmdName))
1503 xcp->execute(
1504 ucb::Command(
1505 cmdName,
1507 uno::Any()) ,
1509 rContext.xEnv );
1511 catch( uno::Exception const & )
1516 } /* namespace */
1519 // UniversalContentBroker implementation ( XCommandProcessor commands ).
1522 uno::Reference< ucb::XCommandInfo >
1523 UniversalContentBroker::getCommandInfo()
1525 return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1529 void UniversalContentBroker::globalTransfer(
1530 const ucb::GlobalTransferCommandArgument2 & rArg,
1531 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1533 // Use own command environment with own interaction handler intercepting
1534 // some interaction requests that shall not be handled by the user-supplied
1535 // interaction handler.
1536 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1537 if (xEnv.is())
1539 xLocalEnv.set( ucb::CommandEnvironment::create(
1540 m_xContext,
1541 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1542 xEnv->getProgressHandler() ) );
1546 // (1) Try to transfer the content using 'transfer' command.
1549 uno::Reference< ucb::XContent > xTarget;
1550 uno::Reference< ucb::XContentIdentifier > xId
1551 = createContentIdentifier( rArg.TargetURL );
1552 if ( xId.is() )
1556 xTarget = queryContent( xId );
1558 catch ( ucb::IllegalIdentifierException const & )
1563 if ( !xTarget.is() )
1565 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1567 {"Uri", uno::Any(rArg.TargetURL)}
1568 }));
1569 ucbhelper::cancelCommandExecution(
1570 ucb::IOErrorCode_CANT_READ,
1571 aArgs,
1572 xEnv,
1573 u"Can't instantiate target object!"_ustr,
1574 this );
1575 // Unreachable
1578 if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) ||
1579 ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) )
1581 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1582 xTarget, uno::UNO_QUERY );
1583 if ( !xCommandProcessor.is() )
1585 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1587 {"Uri", uno::Any(rArg.TargetURL)}
1588 }));
1589 ucbhelper::cancelCommandExecution(
1590 ucb::IOErrorCode_CANT_READ,
1591 aArgs,
1592 xEnv,
1593 u"Target content is not a XCommandProcessor!"_ustr,
1594 this );
1595 // Unreachable
1598 ucb::TransferInfo2 aTransferArg(
1599 ( rArg.Operation
1600 == ucb::TransferCommandOperation_MOVE ), // MoveData
1601 rArg.SourceURL,
1602 rArg.NewTitle,
1603 rArg.NameClash,
1604 rArg.MimeType );
1606 bool bRetry;
1609 bRetry = false;
1613 ucb::Command aCommand(
1614 u"transfer"_ustr, // Name
1615 -1, // Handle
1616 uno::Any( aTransferArg ) ); // Argument
1618 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1620 // Command succeeded. We're done.
1621 return;
1623 catch ( ucb::InteractiveBadTransferURLException const & )
1625 // Source URL is not supported by target. Try to transfer
1626 // the content "manually".
1628 catch ( ucb::UnsupportedCommandException const & )
1630 // 'transfer' command is not supported by commandprocessor.
1631 // Try to transfer manually.
1633 catch ( ucb::UnsupportedNameClashException const & exc )
1635 OSL_ENSURE( aTransferArg.NameClash == exc.NameClash,
1636 "nameclash mismatch!" );
1637 if ( exc.NameClash == ucb::NameClash::ASK )
1639 // Try to detect a name clash by invoking "transfer" with
1640 // NameClash::ERROR.
1643 ucb::TransferInfo2 aTransferArg1(
1644 aTransferArg.MoveData,
1645 aTransferArg.SourceURL,
1646 aTransferArg.NewTitle,
1647 ucb::NameClash::ERROR,
1648 aTransferArg.MimeType );
1650 ucb::Command aCommand1(
1651 u"transfer"_ustr,
1653 uno::Any( aTransferArg1 ) );
1655 xCommandProcessor->execute( aCommand1, 0, xLocalEnv );
1657 // Command succeeded. We're done.
1658 return;
1660 catch ( ucb::UnsupportedNameClashException const & )
1662 // No chance to solve name clashes, because I'm not
1663 // able to detect whether there is one.
1664 throw exc; // Not just 'throw;'!
1666 catch ( ucb::NameClashException const & )
1668 // There's a clash. Use interaction handler to solve it.
1670 uno::Any aExc;
1671 OUString aNewTitle;
1672 NameClashContinuation eCont
1673 = interactiveNameClashResolve(
1674 xEnv, // always use original environment!
1675 rArg.TargetURL, // target folder URL
1676 createDesiredName(
1677 aTransferArg ), // clashing name
1678 aExc,
1679 aNewTitle );
1681 switch ( eCont )
1683 case NOT_HANDLED:
1684 // Not handled.
1685 cppu::throwException( aExc );
1686 [[fallthrough]]; // break;
1688 case UNKNOWN:
1689 // Handled, but not clear, how...
1690 // fall through intended.
1692 case ABORT:
1693 throw ucb::CommandFailedException(
1694 u"abort requested via interaction "
1695 "handler"_ustr,
1696 uno::Reference< uno::XInterface >(),
1697 aExc );
1698 // break;
1700 case OVERWRITE:
1701 aTransferArg.NameClash
1702 = ucb::NameClash::OVERWRITE;
1703 bRetry = true;
1704 break;
1706 case NEW_NAME:
1707 aTransferArg.NewTitle = aNewTitle;
1708 bRetry = true;
1709 break;
1712 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1715 else
1717 throw;
1721 while ( bRetry );
1725 // (2) Try to transfer the content "manually".
1728 uno::Reference< ucb::XContent > xSource;
1731 uno::Reference< ucb::XContentIdentifier > xId2
1732 = createContentIdentifier( rArg.SourceURL );
1733 if ( xId2.is() )
1734 xSource = queryContent( xId2 );
1736 catch ( ucb::IllegalIdentifierException const & )
1738 // Error handling via "if ( !xSource.is() )" below.
1741 if ( !xSource.is() )
1743 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1745 {"Uri", uno::Any(rArg.SourceURL)}
1746 }));
1747 ucbhelper::cancelCommandExecution(
1748 ucb::IOErrorCode_CANT_READ,
1749 aArgs,
1750 xEnv,
1751 u"Can't instantiate source object!"_ustr,
1752 this );
1753 // Unreachable
1756 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1757 xSource, uno::UNO_QUERY );
1758 if ( !xCommandProcessor.is() )
1760 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1762 {"Uri", uno::Any(rArg.SourceURL)}
1763 }));
1764 ucbhelper::cancelCommandExecution(
1765 ucb::IOErrorCode_CANT_READ,
1766 aArgs,
1767 xEnv,
1768 u"Source content is not a XCommandProcessor!"_ustr,
1769 this );
1770 // Unreachable
1773 // Obtain interesting property values from source...
1775 uno::Sequence< beans::Property > aProps{ makeProperty(u"IsFolder"_ustr, -1 /* unknown */),
1776 makeProperty(u"IsDocument"_ustr, -1 /* unknown */),
1777 makeProperty(u"TargetURL"_ustr, -1 /* unknown */),
1778 makeProperty(u"BaseURI"_ustr, -1 /* unknown */) };
1780 ucb::Command aGetPropsCommand(
1781 u"getPropertyValues"_ustr,
1783 uno::Any( aProps ) );
1785 uno::Reference< sdbc::XRow > xRow;
1786 xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow;
1788 if ( !xRow.is() )
1790 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1792 {"Uri", uno::Any(rArg.SourceURL)}
1793 }));
1794 ucbhelper::cancelCommandExecution(
1795 ucb::IOErrorCode_CANT_READ,
1796 aArgs,
1797 xEnv,
1798 u"Unable to get properties from source object!"_ustr,
1799 this );
1800 // Unreachable
1803 TransferCommandContext aTransferCtx(
1804 m_xContext, this, xLocalEnv, xEnv, rArg );
1806 if ( rArg.NewTitle.isEmpty() )
1808 // BaseURI: property is optional.
1809 OUString aBaseURI( xRow->getString( 4 ) );
1810 if ( !aBaseURI.isEmpty() )
1812 aTransferCtx.aArg.NewTitle
1813 = createDesiredName( aBaseURI, OUString() );
1817 // Do it!
1818 globalTransfer_( aTransferCtx, xSource, xTarget, xRow );
1821 // (3) Delete source, if operation is MOVE.
1824 if ( rArg.Operation != ucb::TransferCommandOperation_MOVE )
1825 return;
1829 ucb::Command aCommand(
1830 u"delete"_ustr, // Name
1831 -1, // Handle
1832 uno::Any( true ) ); // Argument
1834 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1836 catch ( uno::Exception const & )
1838 OSL_FAIL( "Cannot delete source object!" );
1839 throw;
1843 uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg,
1844 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1846 uno::Any aRet;
1847 // Use own command environment with own interaction handler intercepting
1848 // some interaction requests that shall not be handled by the user-supplied
1849 // interaction handler.
1850 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1851 if (xEnv.is())
1853 xLocalEnv.set( ucb::CommandEnvironment::create(
1854 m_xContext,
1855 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1856 xEnv->getProgressHandler() ) );
1859 uno::Reference< ucb::XContent > xTarget;
1860 uno::Reference< ucb::XContentIdentifier > xId
1861 = createContentIdentifier( rArg.TargetURL );
1862 if ( xId.is() )
1866 xTarget = queryContent( xId );
1868 catch ( ucb::IllegalIdentifierException const & )
1873 if ( !xTarget.is() )
1875 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1877 {"Uri", uno::Any(rArg.TargetURL)}
1878 }));
1879 ucbhelper::cancelCommandExecution(
1880 ucb::IOErrorCode_CANT_READ,
1881 aArgs,
1882 xEnv,
1883 u"Can't instantiate target object!"_ustr,
1884 this );
1885 // Unreachable
1888 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1889 xTarget, uno::UNO_QUERY );
1890 if ( !xCommandProcessor.is() )
1892 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1894 {"Uri", uno::Any(rArg.TargetURL)}
1895 }));
1896 ucbhelper::cancelCommandExecution(
1897 ucb::IOErrorCode_CANT_READ,
1898 aArgs,
1899 xEnv,
1900 u"Target content is not a XCommandProcessor!"_ustr,
1901 this );
1902 // Unreachable
1907 ucb::Command aCommand(
1908 u"checkin"_ustr, -1,
1909 uno::Any( rArg ) );
1911 aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1913 catch ( ucb::UnsupportedCommandException const & )
1915 // 'checkin' command is not supported by commandprocessor:
1916 // ignore.
1918 return aRet;
1921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */