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 .
20 #include "submission.hxx"
23 #include "binding.hxx"
25 #include "evaluationcontext.hxx"
26 #include "submission/submission_put.hxx"
27 #include "submission/submission_post.hxx"
28 #include "submission/submission_get.hxx"
30 #include <rtl/ustring.hxx>
31 #include <com/sun/star/lang/NoSupportException.hpp>
32 #include <com/sun/star/uno/Sequence.hxx>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/xforms/XModel.hpp>
35 #include <com/sun/star/uno/RuntimeException.hpp>
36 #include <com/sun/star/xml/dom/XNodeList.hpp>
37 #include <com/sun/star/xml/dom/XDocument.hpp>
38 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
39 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
40 #include <com/sun/star/xml/dom/NodeType.hpp>
41 #include <com/sun/star/task/XInteractionHandler.hpp>
42 #include <com/sun/star/task/XInteractionRequest.hpp>
43 #include <com/sun/star/task/XInteractionContinuation.hpp>
44 #include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
45 #include <com/sun/star/frame/XFrame.hpp>
46 #include <cppuhelper/exc_hlp.hxx>
47 #include <cppuhelper/typeprovider.hxx>
48 #include <comphelper/interaction.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <comphelper/servicehelper.hxx>
53 using com::sun::star::util::VetoException
;
54 using com::sun::star::form::submission::XSubmissionVetoListener
;
55 using com::sun::star::lang::WrappedTargetException
;
56 using com::sun::star::lang::NoSupportException
;
57 using com::sun::star::task::XInteractionHandler
;
58 using com::sun::star::task::XInteractionRequest
;
59 using com::sun::star::task::XInteractionContinuation
;
60 using com::sun::star::xforms::XModel
;
61 using com::sun::star::xforms::InvalidDataOnSubmitException
;
62 using com::sun::star::xml::xpath::XXPathObject
;
63 using com::sun::star::frame::XFrame
;
64 using xforms::Submission
;
68 using namespace com::sun::star::uno
;
69 using namespace com::sun::star::lang
;
70 using namespace com::sun::star::xml::dom
;
72 Submission::Submission() :
82 mbOmitXmlDeclaration(),
84 msCDataSectionElement(),
87 msIncludeNamespacePrefixes()
89 initializePropertySet();
92 Submission::~Submission() throw()
97 void Submission::setModel( const Reference
<XModel
>& xModel
)
103 void Submission::setID( const OUString
& sID
)
109 void Submission::setBind( const OUString
& sBind
)
114 OUString
Submission::getRef() const
116 return maRef
.getExpression();
119 void Submission::setRef( const OUString
& sRef
)
121 maRef
.setExpression( sRef
);
125 void Submission::setAction( const OUString
& sAction
)
131 void Submission::setMethod( const OUString
& sMethod
)
137 void Submission::setVersion( const OUString
& sVersion
)
139 msVersion
= sVersion
;
143 void Submission::setIndent( bool bIndent
)
149 void Submission::setMediaType( const OUString
& sMediaType
)
151 msMediaType
= sMediaType
;
155 void Submission::setEncoding( const OUString
& sEncoding
)
157 msEncoding
= sEncoding
;
161 void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration
)
163 mbOmitXmlDeclaration
= bOmitXmlDeclaration
;
167 void Submission::setStandalone( bool bStandalone
)
169 mbStandalone
= bStandalone
;
173 void Submission::setCDataSectionElement( const OUString
& sCDataSectionElement
)
175 msCDataSectionElement
= sCDataSectionElement
;
179 void Submission::setReplace( const OUString
& sReplace
)
181 msReplace
= sReplace
;
185 void Submission::setSeparator( const OUString
& sSeparator
)
187 msSeparator
= sSeparator
;
191 void Submission::setIncludeNamespacePrefixes( const Sequence
< OUString
>& rIncludeNamespacePrefixes
)
193 msIncludeNamespacePrefixes
= rIncludeNamespacePrefixes
;
196 bool Submission::doSubmit( const Reference
< XInteractionHandler
>& xHandler
)
200 // construct XXPathObject for submission doc; use bind in preference of ref
201 EvaluationContext aEvalContext
;
202 ComputedExpression aExpression
;
203 if( !msBind
.isEmpty() )
205 Binding
* pBinding
= comphelper::getUnoTunnelImplementation
<Binding
>( mxModel
->getBinding(msBind
) );
206 if( pBinding
!= nullptr )
208 aExpression
.setExpression( pBinding
->getBindingExpression() );
209 aEvalContext
= pBinding
->getEvaluationContext();
211 // TODO: else: illegal binding name -> raise error
213 else if( !maRef
.getExpression().isEmpty() )
215 aExpression
.setExpression( maRef
.getExpression() );
216 aEvalContext
= comphelper::getUnoTunnelImplementation
<Model
>( mxModel
)->getEvaluationContext();
220 aExpression
.setExpression( "/" );
221 aEvalContext
= comphelper::getUnoTunnelImplementation
<Model
>( mxModel
)->getEvaluationContext();
223 aExpression
.evaluate( aEvalContext
);
224 Reference
<XXPathObject
> xResult
= aExpression
.getXPath();
225 OSL_ENSURE( xResult
.is(), "no result?" );
227 // early out if we have not obtained any result
232 // Reference< XNodeList > aList = xResult->getNodeList();
233 OUString aMethod
= getMethod();
235 // strip whitespace-only text node for get submission
236 Reference
< XDocumentFragment
> aFragment
= createSubmissionDocument(
237 xResult
, aMethod
.equalsIgnoreAsciiCase("get"));
239 // submit result; set encoding, etc.
240 std::unique_ptr
<CSubmission
> xSubmission
;
241 if (aMethod
.equalsIgnoreAsciiCase("PUT"))
242 xSubmission
.reset(new CSubmissionPut( getAction(), aFragment
));
243 else if (aMethod
.equalsIgnoreAsciiCase("post"))
244 xSubmission
.reset(new CSubmissionPost( getAction(), aFragment
));
245 else if (aMethod
.equalsIgnoreAsciiCase("get"))
246 xSubmission
.reset(new CSubmissionGet( getAction(), aFragment
));
249 OSL_FAIL("Unsupported xforms submission method");
253 if (!xSubmission
->IsWebProtocol())
256 CSubmission::SubmissionResult aResult
= xSubmission
->submit( xHandler
);
258 if (aResult
== CSubmission::SUCCESS
)
260 Reference
< XDocument
> aInstanceDoc
= getInstanceDocument(xResult
);
261 aResult
= xSubmission
->replace(getReplace(), aInstanceDoc
, Reference
< XFrame
>());
264 return ( aResult
== CSubmission::SUCCESS
);
267 Sequence
<sal_Int8
> Submission::getUnoTunnelId()
269 static cppu::OImplementationId aImplementationId
;
270 return aImplementationId
.getImplementationId();
274 void Submission::liveCheck()
276 bool bValid
= mxModel
.is();
279 throw RuntimeException();
282 Model
* Submission::getModelImpl() const
284 Model
* pModel
= nullptr;
286 pModel
= comphelper::getUnoTunnelImplementation
<Model
>( mxModel
);
291 // Property-Set implementation
295 #define HANDLE_Bind 1
297 #define HANDLE_Action 3
298 #define HANDLE_Method 4
299 #define HANDLE_Version 5
300 #define HANDLE_Indent 6
301 #define HANDLE_MediaType 7
302 #define HANDLE_Encoding 8
303 #define HANDLE_OmitXmlDeclaration 9
304 #define HANDLE_Standalone 10
305 #define HANDLE_CDataSectionElement 11
306 #define HANDLE_Replace 12
307 #define HANDLE_Separator 13
308 #define HANDLE_IncludeNamespacePrefixes 14
309 #define HANDLE_Model 15
311 #define REGISTER_PROPERTY( property, type ) \
312 registerProperty( PROPERTY( property, type ), \
313 new DirectPropertyAccessor< Submission, type >( this, &Submission::set##property, &Submission::get##property ) );
315 #define REGISTER_PROPERTY_BOOL( property ) \
316 registerProperty( PROPERTY( property, bool ), \
317 new BooleanPropertyAccessor< Submission >( this, &Submission::set##property, &Submission::get##property ) );
319 void Submission::initializePropertySet()
321 REGISTER_PROPERTY ( ID
, OUString
);
322 REGISTER_PROPERTY ( Bind
, OUString
);
323 REGISTER_PROPERTY ( Ref
, OUString
);
324 REGISTER_PROPERTY ( Action
, OUString
);
325 REGISTER_PROPERTY ( Method
, OUString
);
326 REGISTER_PROPERTY ( Version
, OUString
);
327 REGISTER_PROPERTY_BOOL( Indent
);
328 REGISTER_PROPERTY ( MediaType
, OUString
);
329 REGISTER_PROPERTY ( Encoding
, OUString
);
330 REGISTER_PROPERTY_BOOL( OmitXmlDeclaration
);
331 REGISTER_PROPERTY_BOOL( Standalone
);
332 REGISTER_PROPERTY ( CDataSectionElement
, OUString
);
333 REGISTER_PROPERTY ( Replace
, OUString
);
334 REGISTER_PROPERTY ( Separator
, OUString
);
335 REGISTER_PROPERTY ( IncludeNamespacePrefixes
, Sequence
< OUString
> );
336 REGISTER_PROPERTY ( Model
, Reference
<XModel
> );
338 initializePropertyValueCache( HANDLE_Indent
);
339 initializePropertyValueCache( HANDLE_OmitXmlDeclaration
);
340 initializePropertyValueCache( HANDLE_Standalone
);
343 sal_Bool SAL_CALL
Submission::convertFastPropertyValue(
344 Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
346 if ( nHandle
== HANDLE_IncludeNamespacePrefixes
)
348 // for convenience reasons (????), we accept a string which contains
349 // a comma-separated list of namespace prefixes
351 if ( rValue
>>= sTokenList
)
353 std::vector
< OUString
> aPrefixes
;
356 aPrefixes
.push_back( sTokenList
.getToken( 0, ',', p
) );
358 Sequence
< OUString
> aConvertedPrefixes( aPrefixes
.data(), aPrefixes
.size() );
359 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, makeAny( aConvertedPrefixes
) );
363 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, rValue
);
366 OUString SAL_CALL
Submission::getName()
371 void SAL_CALL
Submission::setName( const OUString
& sID
)
377 sal_Int64 SAL_CALL
Submission::getSomething(
378 const Sequence
<sal_Int8
>& aId
)
380 return ( aId
== getUnoTunnelId() ) ? reinterpret_cast<sal_Int64
>(this) : 0;
384 static OUString
lcl_message( const OUString
& rID
, const OUString
& rText
)
386 OUString aMessage
= "XForms submission '" + rID
+ "' failed" + rText
+ ".";
390 void SAL_CALL
Submission::submitWithInteraction(
391 const Reference
<XInteractionHandler
>& _rxHandler
)
393 // as long as this class is not really threadsafe, we need to copy
394 // the members we're interested in
395 Reference
< XModel
> xModel( mxModel
);
396 OUString
sID( msID
);
398 if ( !xModel
.is() || msID
.isEmpty() )
399 throw RuntimeException(
400 "This is not a valid submission object.",
404 Model
* pModel
= comphelper::getUnoTunnelImplementation
<Model
>( xModel
);
405 OSL_ENSURE( pModel
!= nullptr, "illegal model?" );
407 // #i36765# #i47248# warning on submission of illegal data
408 // check for validity (and query user if invalid)
409 bool bValid
= pModel
->isValid();
412 InvalidDataOnSubmitException
aInvalidDataException(
413 lcl_message(sID
, " due to invalid data" ), *this );
415 if( _rxHandler
.is() )
417 // laboriously create interaction request
418 comphelper::OInteractionRequest
* pRequest
419 = new comphelper::OInteractionRequest(
420 makeAny( aInvalidDataException
) );
421 Reference
<XInteractionRequest
> xRequest
= pRequest
;
423 comphelper::OInteractionApprove
* pContinue
424 = new comphelper::OInteractionApprove();
425 Reference
<XInteractionContinuation
> xContinue
= pContinue
;
426 pRequest
->addContinuation( xContinue
);
428 comphelper::OInteractionDisapprove
* pCancel
429 = new comphelper::OInteractionDisapprove();
430 Reference
<XInteractionContinuation
> xCancel
= pCancel
;
431 pRequest
->addContinuation( xCancel
);
433 // ask the handler...
434 _rxHandler
->handle( xRequest
);
435 OSL_ENSURE( pContinue
->wasSelected() || pCancel
->wasSelected(),
436 "handler didn't select" );
438 // and continue, if user chose 'continue'
439 if( pContinue
->wasSelected() )
443 // abort if invalid (and user didn't tell us to continue)
445 throw aInvalidDataException
;
448 // attempt submission
449 bool bResult
= false;
452 bResult
= doSubmit( _rxHandler
);
454 catch( const VetoException
& )
456 OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
460 catch( const Exception
& )
462 css::uno::Any anyEx
= cppu::getCaughtException();
463 // exception caught: re-throw as wrapped target exception
464 throw WrappedTargetException(
465 lcl_message( sID
, " due to exception being thrown" ),
471 // other failure: throw wrapped target exception, too.
472 throw WrappedTargetException(
473 lcl_message( sID
, OUString() ), *this, Any() );
478 void SAL_CALL
Submission::submit( )
480 submitWithInteraction( nullptr );
483 void SAL_CALL
Submission::addSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ )
486 throw NoSupportException();
489 void SAL_CALL
Submission::removeSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ )
492 throw NoSupportException();
495 static bool isIgnorable(const Reference
< XNode
>& aNode
)
497 // ignore whitespace-only textnodes
498 if (aNode
->getNodeType() == NodeType_TEXT_NODE
)
500 OUString aTrimmedValue
= aNode
->getNodeValue().trim();
501 if (aTrimmedValue
.isEmpty()) return true;
507 // recursively copy relevant nodes from A to B
508 static void cloneNodes(Model
& aModel
, const Reference
< XNode
>& dstParent
, const Reference
< XNode
>& source
, bool bRemoveWSNodes
)
510 if (!source
.is()) return;
512 Reference
< XNode
> cur
= source
;
513 Reference
< XDocument
> dstDoc
= dstParent
->getOwnerDocument();
514 Reference
< XNode
> imported
;
519 // is this node relevant?
520 MIP mip
= aModel
.queryMIP(cur
);
521 if(mip
.isRelevant() && !(bRemoveWSNodes
&& isIgnorable(cur
)))
523 imported
= dstDoc
->importNode(cur
, false);
524 imported
= dstParent
->appendChild(imported
);
525 // append source children to new imported parent
526 for( cur
= cur
->getFirstChild(); cur
.is(); cur
= cur
->getNextSibling() )
527 cloneNodes(aModel
, imported
, cur
, bRemoveWSNodes
);
530 Reference
< XDocument
> Submission::getInstanceDocument(const Reference
< XXPathObject
>& aObj
)
532 using namespace css::xml::xpath
;
534 Reference
< XDocument
> aDocument
;
536 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
538 Reference
< XNodeList
> aList
= aObj
->getNodeList();
539 if (aList
->getLength() > 0)
540 aDocument
= aList
->item(0)->getOwnerDocument();
545 Reference
< XDocumentFragment
> Submission::createSubmissionDocument(const Reference
< XXPathObject
>& aObj
, bool bRemoveWSNodes
)
547 using namespace css::xml::xpath
;
548 Reference
< XDocumentBuilder
> aDocBuilder
= DocumentBuilder::create(comphelper::getProcessComponentContext());
549 Reference
< XDocument
> aDocument
= aDocBuilder
->newDocument();
550 Reference
< XDocumentFragment
> aFragment
= aDocument
->createDocumentFragment();
553 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
555 Reference
< XNodeList
> aList
= aObj
->getNodeList();
556 Reference
< XNode
> aListItem
;
557 for (sal_Int32 i
=0; i
< aList
->getLength(); i
++)
559 aListItem
= aList
->item(i
);
560 if (aListItem
->getNodeType()==NodeType_DOCUMENT_NODE
)
561 aListItem
= (Reference
< XDocument
>(aListItem
, UNO_QUERY
))->getDocumentElement();
562 // copy relevant nodes from instance into fragment
563 cloneNodes(*getModelImpl(), aFragment
, aListItem
, bRemoveWSNodes
);
569 // some forwarding: XPropertySet is implemented in our base class,
570 // but also available as base of XSubmission
571 Reference
< css::beans::XPropertySetInfo
> SAL_CALL
Submission::getPropertySetInfo( )
573 return PropertySetBase::getPropertySetInfo();
575 void SAL_CALL
Submission::setPropertyValue( const OUString
& aPropertyName
, const Any
& aValue
)
577 PropertySetBase::setPropertyValue( aPropertyName
, aValue
);
579 Any SAL_CALL
Submission::getPropertyValue( const OUString
& PropertyName
)
581 return PropertySetBase::getPropertyValue( PropertyName
);
583 void SAL_CALL
Submission::addPropertyChangeListener( const OUString
& aPropertyName
, const Reference
< css::beans::XPropertyChangeListener
>& xListener
)
585 PropertySetBase::addPropertyChangeListener( aPropertyName
, xListener
);
587 void SAL_CALL
Submission::removePropertyChangeListener( const OUString
& aPropertyName
, const Reference
< css::beans::XPropertyChangeListener
>& aListener
)
589 PropertySetBase::removePropertyChangeListener( aPropertyName
, aListener
);
591 void SAL_CALL
Submission::addVetoableChangeListener( const OUString
& PropertyName
, const Reference
< css::beans::XVetoableChangeListener
>& aListener
)
593 PropertySetBase::addVetoableChangeListener( PropertyName
, aListener
);
595 void SAL_CALL
Submission::removeVetoableChangeListener( const OUString
& PropertyName
, const Reference
< css::beans::XVetoableChangeListener
>& aListener
)
597 PropertySetBase::removeVetoableChangeListener( PropertyName
, aListener
);
600 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */