1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: submission.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_forms.hxx"
33 #include "submission.hxx"
36 #include "binding.hxx"
38 #include "evaluationcontext.hxx"
39 #include "unohelper.hxx"
40 #include "submission/submission_put.hxx"
41 #include "submission/submission_post.hxx"
42 #include "submission/submission_get.hxx"
44 #include <rtl/ustring.hxx>
45 #include <rtl/ustrbuf.hxx>
47 #include <com/sun/star/uno/Sequence.hxx>
48 #include <com/sun/star/uno/Reference.hxx>
49 #include <com/sun/star/xforms/XModel.hpp>
50 #include <com/sun/star/uno/RuntimeException.hpp>
51 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
52 #include <com/sun/star/container/XNameAccess.hpp>
53 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
54 #include <com/sun/star/xml/dom/XNodeList.hpp>
55 #include <com/sun/star/xml/dom/XDocument.hpp>
56 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
57 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
58 #include <com/sun/star/xml/dom/NodeType.hpp>
59 #include <com/sun/star/task/XInteractionHandler.hpp>
60 #include <com/sun/star/task/XInteractionRequest.hpp>
61 #include <com/sun/star/task/XInteractionContinuation.hpp>
62 #include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
63 #include <com/sun/star/frame/XFrame.hpp>
64 #include <cppuhelper/typeprovider.hxx>
65 #include <comphelper/propertysetinfo.hxx>
66 #include <comphelper/interaction.hxx>
67 #include <unotools/processfactory.hxx>
74 using rtl::OUStringBuffer
;
75 using com::sun::star::beans::UnknownPropertyException
;
76 using com::sun::star::beans::PropertyVetoException
;
77 using com::sun::star::lang::IllegalArgumentException
;
78 using com::sun::star::util::VetoException
;
79 using com::sun::star::form::submission::XSubmissionVetoListener
;
80 using com::sun::star::lang::WrappedTargetException
;
81 using com::sun::star::lang::NoSupportException
;
82 using com::sun::star::task::XInteractionHandler
;
83 using com::sun::star::task::XInteractionRequest
;
84 using com::sun::star::task::XInteractionContinuation
;
85 using com::sun::star::xforms::XModel
;
86 using com::sun::star::xforms::InvalidDataOnSubmitException
;
87 using com::sun::star::container::XNameAccess
;
88 using com::sun::star::xml::xpath::XXPathObject
;
89 using com::sun::star::xml::xpath::XPathObjectType
;
90 using com::sun::star::frame::XFrame
;
91 using xforms::Submission
;
96 using namespace com::sun::star::uno
;
97 using namespace com::sun::star::lang
;
98 using namespace com::sun::star::xml::dom
;
100 Submission::Submission() :
110 mbOmitXmlDeclaration(),
112 msCDataSectionElement(),
113 msReplace( OUSTRING("none") ),
115 msIncludeNamespacePrefixes(),
116 m_aFactory(utl::getProcessServiceFactory())
118 initializePropertySet();
121 Submission::~Submission() throw()
125 Reference
<XModel
> Submission::getModel() const
130 void Submission::setModel( const Reference
<XModel
>& xModel
)
135 OUString
Submission::getID() const
140 void Submission::setID( const OUString
& sID
)
145 OUString
Submission::getBind() const
150 void Submission::setBind( const OUString
& sBind
)
155 OUString
Submission::getRef() const
157 return maRef
.getExpression();
160 void Submission::setRef( const OUString
& sRef
)
162 maRef
.setExpression( sRef
);
165 OUString
Submission::getAction() const
170 void Submission::setAction( const OUString
& sAction
)
175 OUString
Submission::getMethod() const
180 void Submission::setMethod( const OUString
& sMethod
)
185 OUString
Submission::getVersion() const
190 void Submission::setVersion( const OUString
& sVersion
)
192 msVersion
= sVersion
;
195 bool Submission::getIndent() const
200 void Submission::setIndent( bool bIndent
)
205 OUString
Submission::getMediaType() const
210 void Submission::setMediaType( const OUString
& sMediaType
)
212 msMediaType
= sMediaType
;
215 OUString
Submission::getEncoding() const
220 void Submission::setEncoding( const OUString
& sEncoding
)
222 msEncoding
= sEncoding
;
225 bool Submission::getOmitXmlDeclaration() const
227 return mbOmitXmlDeclaration
;
230 void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration
)
232 mbOmitXmlDeclaration
= bOmitXmlDeclaration
;
235 bool Submission::getStandalone() const
240 void Submission::setStandalone( bool bStandalone
)
242 mbStandalone
= bStandalone
;
245 OUString
Submission::getCDataSectionElement() const
247 return msCDataSectionElement
;
250 void Submission::setCDataSectionElement( const OUString
& sCDataSectionElement
)
252 msCDataSectionElement
= sCDataSectionElement
;
255 OUString
Submission::getReplace() const
260 void Submission::setReplace( const OUString
& sReplace
)
262 msReplace
= sReplace
;
265 OUString
Submission::getSeparator() const
270 void Submission::setSeparator( const OUString
& sSeparator
)
272 msSeparator
= sSeparator
;
275 Sequence
< OUString
> Submission::getIncludeNamespacePrefixes() const
277 return msIncludeNamespacePrefixes
;
280 void Submission::setIncludeNamespacePrefixes( const Sequence
< OUString
>& rIncludeNamespacePrefixes
)
282 msIncludeNamespacePrefixes
= rIncludeNamespacePrefixes
;
285 bool Submission::doSubmit( const Reference
< XInteractionHandler
>& xHandler
)
289 // construct XXPathObject for submission doc; use bind in preference of ref
290 EvaluationContext aEvalContext
;
291 ComputedExpression aExpression
;
292 if( msBind
.getLength() != 0 )
294 Binding
* pBinding
= Binding::getBinding( mxModel
->getBinding(msBind
) );
295 if( pBinding
!= NULL
)
297 aExpression
.setExpression( pBinding
->getBindingExpression() );
298 aEvalContext
= pBinding
->getEvaluationContext();
300 // TODO: else: illegal binding name -> raise error
302 else if( maRef
.getExpression().getLength() != 0 )
304 aExpression
.setExpression( maRef
.getExpression() );
305 aEvalContext
= Model::getModel( mxModel
)->getEvaluationContext();
309 aExpression
.setExpression( OUSTRING( "/" ) );
310 aEvalContext
= Model::getModel( mxModel
)->getEvaluationContext();
312 aExpression
.evaluate( aEvalContext
);
313 Reference
<XXPathObject
> xResult
= aExpression
.getXPath();
314 OSL_ENSURE( xResult
.is(), "no result?" );
316 // early out if we have not obtained any result
321 // Reference< XNodeList > aList = xResult->getNodeList();
322 OUString aMethod
= getMethod();
324 // strip whitespace-only text node for get submission
325 Reference
< XDocumentFragment
> aFragment
= createSubmissionDocument(
326 xResult
, aMethod
.equalsIgnoreAsciiCaseAscii("get"));
328 // submit result; set encoding, etc.
329 auto_ptr
<CSubmission
> xSubmission
;
330 if (aMethod
.equalsIgnoreAsciiCaseAscii("PUT"))
331 xSubmission
= auto_ptr
<CSubmission
>(
332 new CSubmissionPut( getAction(), aFragment
));
333 else if (aMethod
.equalsIgnoreAsciiCaseAscii("post"))
334 xSubmission
= auto_ptr
<CSubmission
>(
335 new CSubmissionPost( getAction(), aFragment
));
336 else if (aMethod
.equalsIgnoreAsciiCaseAscii("get"))
337 xSubmission
= auto_ptr
<CSubmission
>(
338 new CSubmissionGet( getAction(), aFragment
));
341 OSL_ENSURE(sal_False
, "Unsupported xforms submission method");
345 xSubmission
->setEncoding(getEncoding());
346 CSubmission::SubmissionResult aResult
= xSubmission
->submit( xHandler
);
348 if (aResult
== CSubmission::SUCCESS
)
350 Reference
< XDocument
> aInstanceDoc
= getInstanceDocument(xResult
);
351 aResult
= xSubmission
->replace(getReplace(), aInstanceDoc
, Reference
< XFrame
>());
354 return ( aResult
== CSubmission::SUCCESS
);
357 Sequence
<sal_Int8
> Submission::getUnoTunnelID()
359 static cppu::OImplementationId aImplementationId
;
360 return aImplementationId
.getImplementationId();
363 Submission
* Submission::getSubmission(
364 const Reference
<XPropertySet
>& xPropertySet
)
366 Reference
<XUnoTunnel
> xTunnel( xPropertySet
, UNO_QUERY
);
368 ? reinterpret_cast<Submission
*>(
369 xTunnel
->getSomething( getUnoTunnelID() ) )
378 void Submission::liveCheck()
379 throw( RuntimeException
)
381 bool bValid
= mxModel
.is();
384 throw RuntimeException();
387 Model
* Submission::getModelImpl() const
389 Model
* pModel
= NULL
;
391 pModel
= Model::getModel( mxModel
);
397 // Property-Set implementation
401 #define HANDLE_Bind 1
403 #define HANDLE_Action 3
404 #define HANDLE_Method 4
405 #define HANDLE_Version 5
406 #define HANDLE_Indent 6
407 #define HANDLE_MediaType 7
408 #define HANDLE_Encoding 8
409 #define HANDLE_OmitXmlDeclaration 9
410 #define HANDLE_Standalone 10
411 #define HANDLE_CDataSectionElement 11
412 #define HANDLE_Replace 12
413 #define HANDLE_Separator 13
414 #define HANDLE_IncludeNamespacePrefixes 14
415 #define HANDLE_Model 15
417 #define REGISTER_PROPERTY( property, type ) \
418 registerProperty( PROPERTY( property, type ), \
419 new DirectPropertyAccessor< Submission, type >( this, &Submission::set##property, &Submission::get##property ) );
421 #define REGISTER_PROPERTY_BOOL( property ) \
422 registerProperty( PROPERTY( property, bool ), \
423 new BooleanPropertyAccessor< Submission, bool >( this, &Submission::set##property, &Submission::get##property ) );
425 void Submission::initializePropertySet()
427 REGISTER_PROPERTY ( ID
, OUString
);
428 REGISTER_PROPERTY ( Bind
, OUString
);
429 REGISTER_PROPERTY ( Ref
, OUString
);
430 REGISTER_PROPERTY ( Action
, OUString
);
431 REGISTER_PROPERTY ( Method
, OUString
);
432 REGISTER_PROPERTY ( Version
, OUString
);
433 REGISTER_PROPERTY_BOOL( Indent
);
434 REGISTER_PROPERTY ( MediaType
, OUString
);
435 REGISTER_PROPERTY ( Encoding
, OUString
);
436 REGISTER_PROPERTY_BOOL( OmitXmlDeclaration
);
437 REGISTER_PROPERTY_BOOL( Standalone
);
438 REGISTER_PROPERTY ( CDataSectionElement
, OUString
);
439 REGISTER_PROPERTY ( Replace
, OUString
);
440 REGISTER_PROPERTY ( Separator
, OUString
);
441 REGISTER_PROPERTY ( IncludeNamespacePrefixes
, Sequence
< OUString
> );
442 REGISTER_PROPERTY ( Model
, Reference
<XModel
> );
444 initializePropertyValueCache( HANDLE_Indent
);
445 initializePropertyValueCache( HANDLE_OmitXmlDeclaration
);
446 initializePropertyValueCache( HANDLE_Standalone
);
449 sal_Bool SAL_CALL
Submission::convertFastPropertyValue(
450 Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
451 throw ( IllegalArgumentException
)
453 if ( nHandle
== HANDLE_IncludeNamespacePrefixes
)
455 // for convinience reasons (????), we accept a string which contains
456 // a comma-separated list of namespace prefixes
457 ::rtl::OUString sTokenList
;
458 if ( rValue
>>= sTokenList
)
460 std::vector
< OUString
> aPrefixes
;
463 aPrefixes
.push_back( sTokenList
.getToken( 0, ',', p
) );
465 Sequence
< ::rtl::OUString
> aConvertedPrefixes( &aPrefixes
[0], aPrefixes
.size() );
466 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, makeAny( aConvertedPrefixes
) );
470 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, rValue
);
473 OUString SAL_CALL
Submission::getName()
474 throw( RuntimeException
)
479 void SAL_CALL
Submission::setName( const OUString
& sID
)
480 throw( RuntimeException
)
487 sal_Int64 SAL_CALL
Submission::getSomething(
488 const Sequence
<sal_Int8
>& aId
)
489 throw( RuntimeException
)
491 return ( aId
== getUnoTunnelID() ) ? reinterpret_cast<sal_Int64
>(this) : 0;
495 OUString
lcl_message( const OUString
& rID
, const OUString
& rText
)
497 OUStringBuffer aMessage
;
498 aMessage
.append( OUSTRING("XForms submission '") );
499 aMessage
.append( rID
);
500 aMessage
.append( OUSTRING("' failed") );
501 aMessage
.append( rText
);
502 aMessage
.append( OUSTRING(".") );
503 return aMessage
.makeStringAndClear();
506 void SAL_CALL
Submission::submitWithInteraction(
507 const Reference
<XInteractionHandler
>& _rxHandler
)
508 throw ( VetoException
,
509 WrappedTargetException
,
512 // as long as this class is not really threadsafe, we need to copy
513 // the members we're interested in
514 Reference
< XModel
> xModel( mxModel
);
515 ::rtl::OUString
sID( msID
);
517 if ( !xModel
.is() || !msID
.getLength() )
518 throw RuntimeException(
519 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "This is not a valid submission object." ) ),
523 Model
* pModel
= Model::getModel( xModel
);
524 OSL_ENSURE( pModel
!= NULL
, "illegal model?" );
526 // #i36765# #i47248# warning on submission of illegal data
527 // check for validity (and query user if invalid)
528 bool bValid
= pModel
->isValid();
531 InvalidDataOnSubmitException
aInvalidDataException(
532 lcl_message(sID
, OUSTRING(" due to invalid data") ), *this );
534 if( _rxHandler
.is() )
536 // labouriously create interaction request
537 comphelper::OInteractionRequest
* pRequest
538 = new comphelper::OInteractionRequest(
539 makeAny( aInvalidDataException
) );
540 Reference
<XInteractionRequest
> xRequest
= pRequest
;
542 comphelper::OInteractionApprove
* pContinue
543 = new comphelper::OInteractionApprove();
544 Reference
<XInteractionContinuation
> xContinue
= pContinue
;
545 pRequest
->addContinuation( xContinue
);
547 comphelper::OInteractionDisapprove
* pCancel
548 = new comphelper::OInteractionDisapprove();
549 Reference
<XInteractionContinuation
> xCancel
= pCancel
;
550 pRequest
->addContinuation( xCancel
);
552 // ask the handler...
553 _rxHandler
->handle( xRequest
);
554 OSL_ENSURE( pContinue
->wasSelected() || pCancel
->wasSelected(),
555 "handler didn't select" );
557 // and continue, if user chose 'continue'
558 if( pContinue
->wasSelected() )
562 // abort if invalid (and user didn't tell us to continue)
564 throw aInvalidDataException
;
567 // attempt submission
568 bool bResult
= false;
571 bResult
= doSubmit( _rxHandler
);
573 catch( const VetoException
& )
575 OSL_ENSURE( sal_False
, "Model::submit: Hmm. How can a single submission have a veto right?" );
579 catch( const Exception
& e
)
581 // exception caught: re-throw as wrapped target exception
582 throw WrappedTargetException(
583 lcl_message( sID
, OUSTRING(" due to exception being thrown") ),
584 *this, makeAny( e
) );
593 // other failure: throw wrapped target exception, too.
594 throw WrappedTargetException(
595 lcl_message( sID
, OUString() ), *this, Any() );
599 void SAL_CALL
Submission::submit( ) throw ( VetoException
, WrappedTargetException
, RuntimeException
)
601 submitWithInteraction( NULL
);
604 void SAL_CALL
Submission::addSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ ) throw (NoSupportException
, RuntimeException
)
607 throw NoSupportException();
610 void SAL_CALL
Submission::removeSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ ) throw (NoSupportException
, RuntimeException
)
613 throw NoSupportException();
616 static sal_Bool
_isIgnorable(const Reference
< XNode
>& aNode
)
618 // ignore whitespace-only textnodes
619 if (aNode
->getNodeType() == NodeType_TEXT_NODE
)
621 OUString aTrimmedValue
= aNode
->getNodeValue().trim();
622 if (aTrimmedValue
.getLength() == 0) return sal_True
;
628 // recursively copy relevant nodes from A to B
629 static void _cloneNodes(Model
& aModel
, const Reference
< XNode
>& dstParent
, const Reference
< XNode
>& source
, sal_Bool bRemoveWSNodes
)
631 if (!source
.is()) return;
633 Reference
< XNode
> cur
= source
;
634 Reference
< XDocument
> dstDoc
= dstParent
->getOwnerDocument();
635 Reference
< XNode
> imported
;
639 // is this node relevant?
640 MIP mip
= aModel
.queryMIP(cur
);
641 if(mip
.isRelevant() && !(bRemoveWSNodes
&& _isIgnorable(cur
)))
643 imported
= dstDoc
->importNode(cur
, sal_False
);
644 imported
= dstParent
->appendChild(imported
);
645 // append source children to new imported parent
646 for( cur
= cur
->getFirstChild(); cur
.is(); cur
= cur
->getNextSibling() )
647 _cloneNodes(aModel
, imported
, cur
, bRemoveWSNodes
);
651 Reference
< XDocument
> Submission::getInstanceDocument(const Reference
< XXPathObject
>& aObj
)
653 using namespace com::sun::star::xml::xpath
;
655 Reference
< XDocument
> aDocument
;
657 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
659 Reference
< XNodeList
> aList
= aObj
->getNodeList();
660 if (aList
->getLength() > 0)
661 aDocument
= aList
->item(0)->getOwnerDocument();
666 Reference
< XDocumentFragment
> Submission::createSubmissionDocument(const Reference
< XXPathObject
>& aObj
, sal_Bool bRemoveWSNodes
)
668 using namespace com::sun::star::xml::xpath
;
669 Reference
< XDocumentBuilder
> aDocBuilder(m_aFactory
->createInstance(
670 OUString::createFromAscii("com.sun.star.xml.dom.DocumentBuilder")), UNO_QUERY
);
671 Reference
< XDocument
> aDocument
= aDocBuilder
->newDocument();
672 Reference
< XDocumentFragment
> aFragment
= aDocument
->createDocumentFragment();
675 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
677 Reference
< XNodeList
> aList
= aObj
->getNodeList();
678 Reference
< XNode
> aListItem
;
679 for (sal_Int32 i
=0; i
< aList
->getLength(); i
++)
681 aListItem
= aList
->item(i
);
682 if (aListItem
->getNodeType()==NodeType_DOCUMENT_NODE
)
683 aListItem
= Reference
< XNode
>(
684 (Reference
< XDocument
>(aListItem
, UNO_QUERY
))->getDocumentElement(), UNO_QUERY
);
685 // copy relevant nodes from instance into fragment
686 _cloneNodes(*getModelImpl(), Reference
< XNode
>(aFragment
, UNO_QUERY
), aListItem
, bRemoveWSNodes
);
692 // some forwarding: XPropertySet is implemented in our base class,
693 // but also available as base of XSubmission
694 Reference
< ::com::sun::star::beans::XPropertySetInfo
> SAL_CALL
Submission::getPropertySetInfo( ) throw(RuntimeException
)
696 return PropertySetBase::getPropertySetInfo();
698 void SAL_CALL
Submission::setPropertyValue( const ::rtl::OUString
& aPropertyName
, const Any
& aValue
) throw(UnknownPropertyException
, PropertyVetoException
, IllegalArgumentException
, WrappedTargetException
, RuntimeException
)
700 PropertySetBase::setPropertyValue( aPropertyName
, aValue
);
702 Any SAL_CALL
Submission::getPropertyValue( const ::rtl::OUString
& PropertyName
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
704 return PropertySetBase::getPropertyValue( PropertyName
);
706 void SAL_CALL
Submission::addPropertyChangeListener( const ::rtl::OUString
& aPropertyName
, const Reference
< ::com::sun::star::beans::XPropertyChangeListener
>& xListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
708 PropertySetBase::addPropertyChangeListener( aPropertyName
, xListener
);
710 void SAL_CALL
Submission::removePropertyChangeListener( const ::rtl::OUString
& aPropertyName
, const Reference
< ::com::sun::star::beans::XPropertyChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
712 PropertySetBase::removePropertyChangeListener( aPropertyName
, aListener
);
714 void SAL_CALL
Submission::addVetoableChangeListener( const ::rtl::OUString
& PropertyName
, const Reference
< ::com::sun::star::beans::XVetoableChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
716 PropertySetBase::addVetoableChangeListener( PropertyName
, aListener
);
718 void SAL_CALL
Submission::removeVetoableChangeListener( const ::rtl::OUString
& PropertyName
, const Reference
< ::com::sun::star::beans::XVetoableChangeListener
>& aListener
) throw(UnknownPropertyException
, WrappedTargetException
, RuntimeException
)
720 PropertySetBase::removeVetoableChangeListener( PropertyName
, aListener
);