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 "unohelper.hxx"
27 #include "submission/submission_put.hxx"
28 #include "submission/submission_post.hxx"
29 #include "submission/submission_get.hxx"
31 #include <rtl/ustring.hxx>
32 #include <rtl/ustrbuf.hxx>
34 #include <com/sun/star/uno/Sequence.hxx>
35 #include <com/sun/star/uno/Reference.hxx>
36 #include <com/sun/star/xforms/XModel.hpp>
37 #include <com/sun/star/uno/RuntimeException.hpp>
38 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
39 #include <com/sun/star/container/XNameAccess.hpp>
40 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
41 #include <com/sun/star/xml/dom/XNodeList.hpp>
42 #include <com/sun/star/xml/dom/XDocument.hpp>
43 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
44 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
45 #include <com/sun/star/xml/dom/NodeType.hpp>
46 #include <com/sun/star/task/XInteractionHandler.hpp>
47 #include <com/sun/star/task/XInteractionRequest.hpp>
48 #include <com/sun/star/task/XInteractionContinuation.hpp>
49 #include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
50 #include <com/sun/star/frame/XFrame.hpp>
51 #include <cppuhelper/typeprovider.hxx>
52 #include <comphelper/propertysetinfo.hxx>
53 #include <comphelper/interaction.hxx>
54 #include <comphelper/processfactory.hxx>
60 using com::sun::star::beans::UnknownPropertyException
;
61 using com::sun::star::beans::PropertyVetoException
;
62 using com::sun::star::lang::IllegalArgumentException
;
63 using com::sun::star::util::VetoException
;
64 using com::sun::star::form::submission::XSubmissionVetoListener
;
65 using com::sun::star::lang::WrappedTargetException
;
66 using com::sun::star::lang::NoSupportException
;
67 using com::sun::star::task::XInteractionHandler
;
68 using com::sun::star::task::XInteractionRequest
;
69 using com::sun::star::task::XInteractionContinuation
;
70 using com::sun::star::xforms::XModel
;
71 using com::sun::star::xforms::InvalidDataOnSubmitException
;
72 using com::sun::star::container::XNameAccess
;
73 using com::sun::star::xml::xpath::XXPathObject
;
74 using com::sun::star::xml::xpath::XPathObjectType
;
75 using com::sun::star::frame::XFrame
;
76 using xforms::Submission
;
81 using namespace com::sun::star::uno
;
82 using namespace com::sun::star::lang
;
83 using namespace com::sun::star::xml::dom
;
85 Submission::Submission() :
95 mbOmitXmlDeclaration(),
97 msCDataSectionElement(),
100 msIncludeNamespacePrefixes()
102 initializePropertySet();
105 Submission::~Submission() throw()
109 Reference
<XModel
> Submission::getModel() const
114 void Submission::setModel( const Reference
<XModel
>& xModel
)
119 OUString
Submission::getID() const
124 void Submission::setID( const OUString
& sID
)
129 OUString
Submission::getBind() const
134 void Submission::setBind( const OUString
& sBind
)
139 OUString
Submission::getRef() const
141 return maRef
.getExpression();
144 void Submission::setRef( const OUString
& sRef
)
146 maRef
.setExpression( sRef
);
149 OUString
Submission::getAction() const
154 void Submission::setAction( const OUString
& sAction
)
159 OUString
Submission::getMethod() const
164 void Submission::setMethod( const OUString
& sMethod
)
169 OUString
Submission::getVersion() const
174 void Submission::setVersion( const OUString
& sVersion
)
176 msVersion
= sVersion
;
179 bool Submission::getIndent() const
184 void Submission::setIndent( bool bIndent
)
189 OUString
Submission::getMediaType() const
194 void Submission::setMediaType( const OUString
& sMediaType
)
196 msMediaType
= sMediaType
;
199 OUString
Submission::getEncoding() const
204 void Submission::setEncoding( const OUString
& sEncoding
)
206 msEncoding
= sEncoding
;
209 bool Submission::getOmitXmlDeclaration() const
211 return mbOmitXmlDeclaration
;
214 void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration
)
216 mbOmitXmlDeclaration
= bOmitXmlDeclaration
;
219 bool Submission::getStandalone() const
224 void Submission::setStandalone( bool bStandalone
)
226 mbStandalone
= bStandalone
;
229 OUString
Submission::getCDataSectionElement() const
231 return msCDataSectionElement
;
234 void Submission::setCDataSectionElement( const OUString
& sCDataSectionElement
)
236 msCDataSectionElement
= sCDataSectionElement
;
239 OUString
Submission::getReplace() const
244 void Submission::setReplace( const OUString
& sReplace
)
246 msReplace
= sReplace
;
249 OUString
Submission::getSeparator() const
254 void Submission::setSeparator( const OUString
& sSeparator
)
256 msSeparator
= sSeparator
;
259 Sequence
< OUString
> Submission::getIncludeNamespacePrefixes() const
261 return msIncludeNamespacePrefixes
;
264 void Submission::setIncludeNamespacePrefixes( const Sequence
< OUString
>& rIncludeNamespacePrefixes
)
266 msIncludeNamespacePrefixes
= rIncludeNamespacePrefixes
;
269 bool Submission::doSubmit( const Reference
< XInteractionHandler
>& xHandler
)
273 // construct XXPathObject for submission doc; use bind in preference of ref
274 EvaluationContext aEvalContext
;
275 ComputedExpression aExpression
;
276 if( !msBind
.isEmpty() )
278 Binding
* pBinding
= Binding::getBinding( mxModel
->getBinding(msBind
) );
279 if( pBinding
!= NULL
)
281 aExpression
.setExpression( pBinding
->getBindingExpression() );
282 aEvalContext
= pBinding
->getEvaluationContext();
284 // TODO: else: illegal binding name -> raise error
286 else if( !maRef
.getExpression().isEmpty() )
288 aExpression
.setExpression( maRef
.getExpression() );
289 aEvalContext
= Model::getModel( mxModel
)->getEvaluationContext();
293 aExpression
.setExpression( "/" );
294 aEvalContext
= Model::getModel( mxModel
)->getEvaluationContext();
296 aExpression
.evaluate( aEvalContext
);
297 Reference
<XXPathObject
> xResult
= aExpression
.getXPath();
298 OSL_ENSURE( xResult
.is(), "no result?" );
300 // early out if we have not obtained any result
305 // Reference< XNodeList > aList = xResult->getNodeList();
306 OUString aMethod
= getMethod();
308 // strip whitespace-only text node for get submission
309 Reference
< XDocumentFragment
> aFragment
= createSubmissionDocument(
310 xResult
, aMethod
.equalsIgnoreAsciiCase("get"));
312 // submit result; set encoding, etc.
313 auto_ptr
<CSubmission
> xSubmission
;
314 if (aMethod
.equalsIgnoreAsciiCase("PUT"))
315 xSubmission
= auto_ptr
<CSubmission
>(
316 new CSubmissionPut( getAction(), aFragment
));
317 else if (aMethod
.equalsIgnoreAsciiCase("post"))
318 xSubmission
= auto_ptr
<CSubmission
>(
319 new CSubmissionPost( getAction(), aFragment
));
320 else if (aMethod
.equalsIgnoreAsciiCase("get"))
321 xSubmission
= auto_ptr
<CSubmission
>(
322 new CSubmissionGet( getAction(), aFragment
));
325 OSL_FAIL("Unsupported xforms submission method");
329 xSubmission
->setEncoding(getEncoding());
330 CSubmission::SubmissionResult aResult
= xSubmission
->submit( xHandler
);
332 if (aResult
== CSubmission::SUCCESS
)
334 Reference
< XDocument
> aInstanceDoc
= getInstanceDocument(xResult
);
335 aResult
= xSubmission
->replace(getReplace(), aInstanceDoc
, Reference
< XFrame
>());
338 return ( aResult
== CSubmission::SUCCESS
);
341 Sequence
<sal_Int8
> Submission::getUnoTunnelID()
343 static cppu::OImplementationId aImplementationId
;
344 return aImplementationId
.getImplementationId();
347 Submission
* Submission::getSubmission(
348 const Reference
<XPropertySet
>& xPropertySet
)
350 Reference
<XUnoTunnel
> xTunnel( xPropertySet
, UNO_QUERY
);
352 ? reinterpret_cast<Submission
*>(
353 xTunnel
->getSomething( getUnoTunnelID() ) )
362 void Submission::liveCheck()
363 throw( RuntimeException
)
365 bool bValid
= mxModel
.is();
368 throw RuntimeException();
371 Model
* Submission::getModelImpl() const
373 Model
* pModel
= NULL
;
375 pModel
= Model::getModel( mxModel
);
381 // Property-Set implementation
385 #define HANDLE_Bind 1
387 #define HANDLE_Action 3
388 #define HANDLE_Method 4
389 #define HANDLE_Version 5
390 #define HANDLE_Indent 6
391 #define HANDLE_MediaType 7
392 #define HANDLE_Encoding 8
393 #define HANDLE_OmitXmlDeclaration 9
394 #define HANDLE_Standalone 10
395 #define HANDLE_CDataSectionElement 11
396 #define HANDLE_Replace 12
397 #define HANDLE_Separator 13
398 #define HANDLE_IncludeNamespacePrefixes 14
399 #define HANDLE_Model 15
401 #define REGISTER_PROPERTY( property, type ) \
402 registerProperty( PROPERTY( property, type ), \
403 new DirectPropertyAccessor< Submission, type >( this, &Submission::set##property, &Submission::get##property ) );
405 #define REGISTER_PROPERTY_BOOL( property ) \
406 registerProperty( PROPERTY( property, bool ), \
407 new BooleanPropertyAccessor< Submission, bool >( this, &Submission::set##property, &Submission::get##property ) );
409 void Submission::initializePropertySet()
411 REGISTER_PROPERTY ( ID
, OUString
);
412 REGISTER_PROPERTY ( Bind
, OUString
);
413 REGISTER_PROPERTY ( Ref
, OUString
);
414 REGISTER_PROPERTY ( Action
, OUString
);
415 REGISTER_PROPERTY ( Method
, OUString
);
416 REGISTER_PROPERTY ( Version
, OUString
);
417 REGISTER_PROPERTY_BOOL( Indent
);
418 REGISTER_PROPERTY ( MediaType
, OUString
);
419 REGISTER_PROPERTY ( Encoding
, OUString
);
420 REGISTER_PROPERTY_BOOL( OmitXmlDeclaration
);
421 REGISTER_PROPERTY_BOOL( Standalone
);
422 REGISTER_PROPERTY ( CDataSectionElement
, OUString
);
423 REGISTER_PROPERTY ( Replace
, OUString
);
424 REGISTER_PROPERTY ( Separator
, OUString
);
425 REGISTER_PROPERTY ( IncludeNamespacePrefixes
, Sequence
< OUString
> );
426 REGISTER_PROPERTY ( Model
, Reference
<XModel
> );
428 initializePropertyValueCache( HANDLE_Indent
);
429 initializePropertyValueCache( HANDLE_OmitXmlDeclaration
);
430 initializePropertyValueCache( HANDLE_Standalone
);
433 sal_Bool SAL_CALL
Submission::convertFastPropertyValue(
434 Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
435 throw ( IllegalArgumentException
)
437 if ( nHandle
== HANDLE_IncludeNamespacePrefixes
)
439 // for convinience reasons (????), we accept a string which contains
440 // a comma-separated list of namespace prefixes
442 if ( rValue
>>= sTokenList
)
444 std::vector
< OUString
> aPrefixes
;
447 aPrefixes
.push_back( sTokenList
.getToken( 0, ',', p
) );
449 Sequence
< OUString
> aConvertedPrefixes( &aPrefixes
[0], aPrefixes
.size() );
450 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, makeAny( aConvertedPrefixes
) );
454 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, rValue
);
457 OUString SAL_CALL
Submission::getName()
458 throw( RuntimeException
)
463 void SAL_CALL
Submission::setName( const OUString
& sID
)
464 throw( RuntimeException
)
471 sal_Int64 SAL_CALL
Submission::getSomething(
472 const Sequence
<sal_Int8
>& aId
)
473 throw( RuntimeException
)
475 return ( aId
== getUnoTunnelID() ) ? reinterpret_cast<sal_Int64
>(this) : 0;
479 static OUString
lcl_message( const OUString
& rID
, const OUString
& rText
)
481 OUStringBuffer aMessage
;
482 aMessage
.append( "XForms submission '" );
483 aMessage
.append( rID
);
484 aMessage
.append( "' failed" );
485 aMessage
.append( rText
);
486 aMessage
.append( "." );
487 return aMessage
.makeStringAndClear();
490 void SAL_CALL
Submission::submitWithInteraction(
491 const Reference
<XInteractionHandler
>& _rxHandler
)
492 throw ( VetoException
,
493 WrappedTargetException
,
496 // as long as this class is not really threadsafe, we need to copy
497 // the members we're interested in
498 Reference
< XModel
> xModel( mxModel
);
499 OUString
sID( msID
);
501 if ( !xModel
.is() || msID
.isEmpty() )
502 throw RuntimeException(
503 OUString( "This is not a valid submission object." ),
507 Model
* pModel
= Model::getModel( xModel
);
508 OSL_ENSURE( pModel
!= NULL
, "illegal model?" );
510 // #i36765# #i47248# warning on submission of illegal data
511 // check for validity (and query user if invalid)
512 bool bValid
= pModel
->isValid();
515 InvalidDataOnSubmitException
aInvalidDataException(
516 lcl_message(sID
, " due to invalid data" ), *this );
518 if( _rxHandler
.is() )
520 // labouriously create interaction request
521 comphelper::OInteractionRequest
* pRequest
522 = new comphelper::OInteractionRequest(
523 makeAny( aInvalidDataException
) );
524 Reference
<XInteractionRequest
> xRequest
= pRequest
;
526 comphelper::OInteractionApprove
* pContinue
527 = new comphelper::OInteractionApprove();
528 Reference
<XInteractionContinuation
> xContinue
= pContinue
;
529 pRequest
->addContinuation( xContinue
);
531 comphelper::OInteractionDisapprove
* pCancel
532 = new comphelper::OInteractionDisapprove();
533 Reference
<XInteractionContinuation
> xCancel
= pCancel
;
534 pRequest
->addContinuation( xCancel
);
536 // ask the handler...
537 _rxHandler
->handle( xRequest
);
538 OSL_ENSURE( pContinue
->wasSelected() || pCancel
->wasSelected(),
539 "handler didn't select" );
541 // and continue, if user chose 'continue'
542 if( pContinue
->wasSelected() )
546 // abort if invalid (and user didn't tell us to continue)
548 throw aInvalidDataException
;
551 // attempt submission
552 bool bResult
= false;
555 bResult
= doSubmit( _rxHandler
);
557 catch( const VetoException
& )
559 OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
563 catch( const Exception
& e
)
565 // exception caught: re-throw as wrapped target exception
566 throw WrappedTargetException(
567 lcl_message( sID
, " due to exception being thrown" ),
568 *this, makeAny( e
) );
577 // other failure: throw wrapped target exception, too.
578 throw WrappedTargetException(
579 lcl_message( sID
, OUString() ), *this, Any() );
583 void SAL_CALL
Submission::submit( ) throw ( VetoException
, WrappedTargetException
, RuntimeException
)
585 submitWithInteraction( NULL
);
588 void SAL_CALL
Submission::addSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ ) throw (NoSupportException
, RuntimeException
)
591 throw NoSupportException();
594 void SAL_CALL
Submission::removeSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ ) throw (NoSupportException
, RuntimeException
)
597 throw NoSupportException();
600 static sal_Bool
_isIgnorable(const Reference
< XNode
>& aNode
)
602 // ignore whitespace-only textnodes
603 if (aNode
->getNodeType() == NodeType_TEXT_NODE
)
605 OUString aTrimmedValue
= aNode
->getNodeValue().trim();
606 if (aTrimmedValue
.isEmpty()) return sal_True
;
612 // recursively copy relevant nodes from A to B
613 static void _cloneNodes(Model
& aModel
, const Reference
< XNode
>& dstParent
, const Reference
< XNode
>& source
, sal_Bool bRemoveWSNodes
)
615 if (!source
.is()) return;
617 Reference
< XNode
> cur
= source
;
618 Reference
< XDocument
> dstDoc
= dstParent
->getOwnerDocument();
619 Reference
< XNode
> imported
;
623 // is this node relevant?
624 MIP mip
= aModel
.queryMIP(cur
);
625 if(mip
.isRelevant() && !(bRemoveWSNodes
&& _isIgnorable(cur
)))
627 imported
= dstDoc
->importNode(cur
, sal_False
);
628 imported
= dstParent
->appendChild(imported
);
629 // append source children to new imported parent
630 for( cur
= cur
->getFirstChild(); cur
.is(); cur
= cur
->getNextSibling() )
631 _cloneNodes(aModel
, imported
, cur
, bRemoveWSNodes
);
635 Reference
< XDocument
> Submission::getInstanceDocument(const Reference
< XXPathObject
>& aObj
)
637 using namespace com::sun::star::xml::xpath
;
639 Reference
< XDocument
> aDocument
;
641 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
643 Reference
< XNodeList
> aList
= aObj
->getNodeList();
644 if (aList
->getLength() > 0)
645 aDocument
= aList
->item(0)->getOwnerDocument();
650 Reference
< XDocumentFragment
> Submission::createSubmissionDocument(const Reference
< XXPathObject
>& aObj
, sal_Bool bRemoveWSNodes
)
652 using namespace com::sun::star::xml::xpath
;
653 Reference
< XDocumentBuilder
> aDocBuilder
= DocumentBuilder::create(comphelper::getProcessComponentContext());
654 Reference
< XDocument
> aDocument
= aDocBuilder
->newDocument();
655 Reference
< XDocumentFragment
> aFragment
= aDocument
->createDocumentFragment();
658 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
660 Reference
< XNodeList
> aList
= aObj
->getNodeList();
661 Reference
< XNode
> aListItem
;
662 for (sal_Int32 i
=0; i
< aList
->getLength(); i
++)
664 aListItem
= aList
->item(i
);
665 if (aListItem
->getNodeType()==NodeType_DOCUMENT_NODE
)
666 aListItem
= Reference
< XNode
>(
667 (Reference
< XDocument
>(aListItem
, UNO_QUERY
))->getDocumentElement(), UNO_QUERY
);
668 // copy relevant nodes from instance into fragment
669 _cloneNodes(*getModelImpl(), Reference
< XNode
>(aFragment
, UNO_QUERY
), aListItem
, bRemoveWSNodes
);
675 // some forwarding: XPropertySet is implemented in our base class,
676 // but also available as base of XSubmission
677 Reference
< ::com::sun::star::beans::XPropertySetInfo
> SAL_CALL
Submission::getPropertySetInfo( ) throw(RuntimeException
)
679 return PropertySetBase::getPropertySetInfo();
681 void SAL_CALL
Submission::setPropertyValue( const OUString
& aPropertyName
, const Any
& aValue
) throw(UnknownPropertyException
, PropertyVetoException
, IllegalArgumentException
, WrappedTargetException
, RuntimeException
)
683 PropertySetBase::setPropertyValue( aPropertyName
, aValue
);
685 Any SAL_CALL
Submission::getPropertyValue( const OUString
& PropertyName
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
687 return PropertySetBase::getPropertyValue( PropertyName
);
689 void SAL_CALL
Submission::addPropertyChangeListener( const OUString
& aPropertyName
, const Reference
< ::com::sun::star::beans::XPropertyChangeListener
>& xListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
691 PropertySetBase::addPropertyChangeListener( aPropertyName
, xListener
);
693 void SAL_CALL
Submission::removePropertyChangeListener( const OUString
& aPropertyName
, const Reference
< ::com::sun::star::beans::XPropertyChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
695 PropertySetBase::removePropertyChangeListener( aPropertyName
, aListener
);
697 void SAL_CALL
Submission::addVetoableChangeListener( const OUString
& PropertyName
, const Reference
< ::com::sun::star::beans::XVetoableChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
699 PropertySetBase::addVetoableChangeListener( PropertyName
, aListener
);
701 void SAL_CALL
Submission::removeVetoableChangeListener( const OUString
& PropertyName
, const Reference
< ::com::sun::star::beans::XVetoableChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
703 PropertySetBase::removeVetoableChangeListener( PropertyName
, aListener
);
706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */