bump product version to 5.0.4.1
[LibreOffice.git] / forms / source / xforms / submission.cxx
blob82fb8b9bb682c45e160eb072c8026d555bdfe34e
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 "submission.hxx"
22 #include "model.hxx"
23 #include "binding.hxx"
24 #include "mip.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>
55 #include <boost/scoped_ptr.hpp>
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;
77 using xforms::Model;
78 using xforms::MIP;
80 using namespace com::sun::star::uno;
81 using namespace com::sun::star::lang;
82 using namespace com::sun::star::xml::dom;
84 Submission::Submission() :
85 msID(),
86 msBind(),
87 maRef(),
88 msAction(),
89 msMethod(),
90 msVersion(),
91 mbIndent(),
92 msMediaType(),
93 msEncoding(),
94 mbOmitXmlDeclaration(),
95 mbStandalone(),
96 msCDataSectionElement(),
97 msReplace( "none" ),
98 msSeparator(),
99 msIncludeNamespacePrefixes()
101 initializePropertySet();
104 Submission::~Submission() throw()
109 void Submission::setModel( const Reference<XModel>& xModel )
111 mxModel = xModel;
115 void Submission::setID( const OUString& sID )
117 msID = sID;
121 void Submission::setBind( const OUString& sBind )
123 msBind = sBind;
126 OUString Submission::getRef() const
128 return maRef.getExpression();
131 void Submission::setRef( const OUString& sRef )
133 maRef.setExpression( sRef );
137 void Submission::setAction( const OUString& sAction )
139 msAction = sAction;
143 void Submission::setMethod( const OUString& sMethod )
145 msMethod = sMethod;
149 void Submission::setVersion( const OUString& sVersion )
151 msVersion = sVersion;
155 void Submission::setIndent( bool bIndent )
157 mbIndent = bIndent;
161 void Submission::setMediaType( const OUString& sMediaType )
163 msMediaType = sMediaType;
167 void Submission::setEncoding( const OUString& sEncoding )
169 msEncoding = sEncoding;
173 void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration )
175 mbOmitXmlDeclaration = bOmitXmlDeclaration;
179 void Submission::setStandalone( bool bStandalone )
181 mbStandalone = bStandalone;
185 void Submission::setCDataSectionElement( const OUString& sCDataSectionElement )
187 msCDataSectionElement = sCDataSectionElement;
191 void Submission::setReplace( const OUString& sReplace )
193 msReplace = sReplace;
197 void Submission::setSeparator( const OUString& sSeparator )
199 msSeparator = sSeparator;
203 void Submission::setIncludeNamespacePrefixes( const Sequence< OUString >& rIncludeNamespacePrefixes )
205 msIncludeNamespacePrefixes = rIncludeNamespacePrefixes;
208 bool Submission::doSubmit( const Reference< XInteractionHandler >& xHandler )
210 liveCheck();
212 // construct XXPathObject for submission doc; use bind in preference of ref
213 EvaluationContext aEvalContext;
214 ComputedExpression aExpression;
215 if( !msBind.isEmpty() )
217 Binding* pBinding = Binding::getBinding( mxModel->getBinding(msBind) );
218 if( pBinding != NULL )
220 aExpression.setExpression( pBinding->getBindingExpression() );
221 aEvalContext = pBinding->getEvaluationContext();
223 // TODO: else: illegal binding name -> raise error
225 else if( !maRef.getExpression().isEmpty() )
227 aExpression.setExpression( maRef.getExpression() );
228 aEvalContext = Model::getModel( mxModel )->getEvaluationContext();
230 else
232 aExpression.setExpression( "/" );
233 aEvalContext = Model::getModel( mxModel )->getEvaluationContext();
235 aExpression.evaluate( aEvalContext );
236 Reference<XXPathObject> xResult = aExpression.getXPath();
237 OSL_ENSURE( xResult.is(), "no result?" );
239 // early out if we have not obtained any result
240 if( ! xResult.is() )
241 return false;
244 // Reference< XNodeList > aList = xResult->getNodeList();
245 OUString aMethod = getMethod();
247 // strip whitespace-only text node for get submission
248 Reference< XDocumentFragment > aFragment = createSubmissionDocument(
249 xResult, aMethod.equalsIgnoreAsciiCase("get"));
251 // submit result; set encoding, etc.
252 boost::scoped_ptr<CSubmission> xSubmission;
253 if (aMethod.equalsIgnoreAsciiCase("PUT"))
254 xSubmission.reset(new CSubmissionPut( getAction(), aFragment));
255 else if (aMethod.equalsIgnoreAsciiCase("post"))
256 xSubmission.reset(new CSubmissionPost( getAction(), aFragment));
257 else if (aMethod.equalsIgnoreAsciiCase("get"))
258 xSubmission.reset(new CSubmissionGet( getAction(), aFragment));
259 else
261 OSL_FAIL("Unsupported xforms submission method");
262 return false;
265 xSubmission->setEncoding(getEncoding());
266 CSubmission::SubmissionResult aResult = xSubmission->submit( xHandler );
268 if (aResult == CSubmission::SUCCESS)
270 Reference< XDocument > aInstanceDoc = getInstanceDocument(xResult);
271 aResult = xSubmission->replace(getReplace(), aInstanceDoc, Reference< XFrame >());
274 return ( aResult == CSubmission::SUCCESS );
277 Sequence<sal_Int8> Submission::getUnoTunnelID()
279 static cppu::OImplementationId aImplementationId;
280 return aImplementationId.getImplementationId();
283 Submission* Submission::getSubmission(
284 const Reference<XPropertySet>& xPropertySet )
286 Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY );
287 return xTunnel.is()
288 ? reinterpret_cast<Submission*>(
289 xTunnel->getSomething( getUnoTunnelID() ) )
290 : NULL;
298 void Submission::liveCheck()
299 throw( RuntimeException )
301 bool bValid = mxModel.is();
303 if( ! bValid )
304 throw RuntimeException();
307 Model* Submission::getModelImpl() const
309 Model* pModel = NULL;
310 if( mxModel.is() )
311 pModel = Model::getModel( mxModel );
312 return pModel;
317 // Property-Set implementation
320 #define HANDLE_ID 0
321 #define HANDLE_Bind 1
322 #define HANDLE_Ref 2
323 #define HANDLE_Action 3
324 #define HANDLE_Method 4
325 #define HANDLE_Version 5
326 #define HANDLE_Indent 6
327 #define HANDLE_MediaType 7
328 #define HANDLE_Encoding 8
329 #define HANDLE_OmitXmlDeclaration 9
330 #define HANDLE_Standalone 10
331 #define HANDLE_CDataSectionElement 11
332 #define HANDLE_Replace 12
333 #define HANDLE_Separator 13
334 #define HANDLE_IncludeNamespacePrefixes 14
335 #define HANDLE_Model 15
337 #define REGISTER_PROPERTY( property, type ) \
338 registerProperty( PROPERTY( property, type ), \
339 new DirectPropertyAccessor< Submission, type >( this, &Submission::set##property, &Submission::get##property ) );
341 #define REGISTER_PROPERTY_BOOL( property ) \
342 registerProperty( PROPERTY( property, bool ), \
343 new BooleanPropertyAccessor< Submission, bool >( this, &Submission::set##property, &Submission::get##property ) );
345 void Submission::initializePropertySet()
347 REGISTER_PROPERTY ( ID, OUString );
348 REGISTER_PROPERTY ( Bind, OUString );
349 REGISTER_PROPERTY ( Ref, OUString );
350 REGISTER_PROPERTY ( Action, OUString );
351 REGISTER_PROPERTY ( Method, OUString );
352 REGISTER_PROPERTY ( Version, OUString );
353 REGISTER_PROPERTY_BOOL( Indent );
354 REGISTER_PROPERTY ( MediaType, OUString );
355 REGISTER_PROPERTY ( Encoding, OUString );
356 REGISTER_PROPERTY_BOOL( OmitXmlDeclaration );
357 REGISTER_PROPERTY_BOOL( Standalone );
358 REGISTER_PROPERTY ( CDataSectionElement, OUString );
359 REGISTER_PROPERTY ( Replace, OUString );
360 REGISTER_PROPERTY ( Separator, OUString );
361 REGISTER_PROPERTY ( IncludeNamespacePrefixes, Sequence< OUString > );
362 REGISTER_PROPERTY ( Model, Reference<XModel> );
364 initializePropertyValueCache( HANDLE_Indent );
365 initializePropertyValueCache( HANDLE_OmitXmlDeclaration );
366 initializePropertyValueCache( HANDLE_Standalone );
369 sal_Bool SAL_CALL Submission::convertFastPropertyValue(
370 Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue )
371 throw ( IllegalArgumentException )
373 if ( nHandle == HANDLE_IncludeNamespacePrefixes )
375 // for convinience reasons (????), we accept a string which contains
376 // a comma-separated list of namespace prefixes
377 OUString sTokenList;
378 if ( rValue >>= sTokenList )
380 std::vector< OUString > aPrefixes;
381 sal_Int32 p = 0;
382 while ( p >= 0 )
383 aPrefixes.push_back( sTokenList.getToken( 0, ',', p ) );
385 Sequence< OUString > aConvertedPrefixes( &aPrefixes[0], aPrefixes.size() );
386 return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, makeAny( aConvertedPrefixes ) );
390 return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
393 OUString SAL_CALL Submission::getName()
394 throw( RuntimeException, std::exception )
396 return getID();
399 void SAL_CALL Submission::setName( const OUString& sID )
400 throw( RuntimeException, std::exception )
402 setID( sID );
407 sal_Int64 SAL_CALL Submission::getSomething(
408 const Sequence<sal_Int8>& aId )
409 throw( RuntimeException, std::exception )
411 return ( aId == getUnoTunnelID() ) ? reinterpret_cast<sal_Int64>(this) : 0;
415 static OUString lcl_message( const OUString& rID, const OUString& rText )
417 OUString aMessage = "XForms submission '" + rID + "' failed" + rText + ".";
418 return aMessage;
421 void SAL_CALL Submission::submitWithInteraction(
422 const Reference<XInteractionHandler>& _rxHandler )
423 throw ( VetoException,
424 WrappedTargetException,
425 RuntimeException, std::exception )
427 // as long as this class is not really threadsafe, we need to copy
428 // the members we're interested in
429 Reference< XModel > xModel( mxModel );
430 OUString sID( msID );
432 if ( !xModel.is() || msID.isEmpty() )
433 throw RuntimeException(
434 "This is not a valid submission object.",
435 *this
438 Model* pModel = Model::getModel( xModel );
439 OSL_ENSURE( pModel != NULL, "illegal model?" );
441 // #i36765# #i47248# warning on submission of illegal data
442 // check for validity (and query user if invalid)
443 bool bValid = pModel->isValid();
444 if( ! bValid )
446 InvalidDataOnSubmitException aInvalidDataException(
447 lcl_message(sID, " due to invalid data" ), *this );
449 if( _rxHandler.is() )
451 // labouriously create interaction request
452 comphelper::OInteractionRequest* pRequest
453 = new comphelper::OInteractionRequest(
454 makeAny( aInvalidDataException ) );
455 Reference<XInteractionRequest> xRequest = pRequest;
457 comphelper::OInteractionApprove* pContinue
458 = new comphelper::OInteractionApprove();
459 Reference<XInteractionContinuation> xContinue = pContinue;
460 pRequest->addContinuation( xContinue );
462 comphelper::OInteractionDisapprove* pCancel
463 = new comphelper::OInteractionDisapprove();
464 Reference<XInteractionContinuation> xCancel = pCancel;
465 pRequest->addContinuation( xCancel );
467 // ask the handler...
468 _rxHandler->handle( xRequest );
469 OSL_ENSURE( pContinue->wasSelected() || pCancel->wasSelected(),
470 "handler didn't select" );
472 // and continue, if user chose 'continue'
473 if( pContinue->wasSelected() )
474 bValid = true;
477 // abort if invalid (and user didn't tell us to continue)
478 if( ! bValid )
479 throw aInvalidDataException;
482 // attempt submission
483 bool bResult = false;
486 bResult = doSubmit( _rxHandler );
488 catch( const VetoException& )
490 OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
491 // allowed to leave
492 throw;
494 catch( const Exception& e )
496 // exception caught: re-throw as wrapped target exception
497 throw WrappedTargetException(
498 lcl_message( sID, " due to exception being thrown" ),
499 *this, makeAny( e ) );
502 if( bResult )
504 mxModel->rebuild();
506 else
508 // other failure: throw wrapped target exception, too.
509 throw WrappedTargetException(
510 lcl_message( sID, OUString() ), *this, Any() );
514 void SAL_CALL Submission::submit( ) throw ( VetoException, WrappedTargetException, RuntimeException, std::exception )
516 submitWithInteraction( NULL );
519 void SAL_CALL Submission::addSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ ) throw (NoSupportException, RuntimeException, std::exception)
521 // TODO
522 throw NoSupportException();
525 void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ ) throw (NoSupportException, RuntimeException, std::exception)
527 // TODO
528 throw NoSupportException();
531 static bool _isIgnorable(const Reference< XNode >& aNode)
533 // ignore whitespace-only textnodes
534 if (aNode->getNodeType() == NodeType_TEXT_NODE)
536 OUString aTrimmedValue = aNode->getNodeValue().trim();
537 if (aTrimmedValue.isEmpty()) return true;
540 return false;
543 // recursively copy relevant nodes from A to B
544 static void _cloneNodes(Model& aModel, const Reference< XNode >& dstParent, const Reference< XNode >& source, bool bRemoveWSNodes)
546 if (!source.is()) return;
548 Reference< XNode > cur = source;
549 Reference< XDocument > dstDoc = dstParent->getOwnerDocument();
550 Reference< XNode > imported;
552 if (cur.is())
554 // is this node relevant?
555 MIP mip = aModel.queryMIP(cur);
556 if(mip.isRelevant() && !(bRemoveWSNodes && _isIgnorable(cur)))
558 imported = dstDoc->importNode(cur, sal_False);
559 imported = dstParent->appendChild(imported);
560 // append source children to new imported parent
561 for( cur = cur->getFirstChild(); cur.is(); cur = cur->getNextSibling() )
562 _cloneNodes(aModel, imported, cur, bRemoveWSNodes);
566 Reference< XDocument > Submission::getInstanceDocument(const Reference< XXPathObject >& aObj)
568 using namespace com::sun::star::xml::xpath;
569 // result
570 Reference< XDocument > aDocument;
572 if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
574 Reference< XNodeList > aList = aObj->getNodeList();
575 if (aList->getLength() > 0)
576 aDocument = aList->item(0)->getOwnerDocument();
578 return aDocument;
581 Reference< XDocumentFragment > Submission::createSubmissionDocument(const Reference< XXPathObject >& aObj, bool bRemoveWSNodes)
583 using namespace com::sun::star::xml::xpath;
584 Reference< XDocumentBuilder > aDocBuilder = DocumentBuilder::create(comphelper::getProcessComponentContext());
585 Reference< XDocument > aDocument = aDocBuilder->newDocument();
586 Reference< XDocumentFragment > aFragment = aDocument->createDocumentFragment();
589 if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
591 Reference< XNodeList > aList = aObj->getNodeList();
592 Reference< XNode > aListItem;
593 for (sal_Int32 i=0; i < aList->getLength(); i++)
595 aListItem = aList->item(i);
596 if (aListItem->getNodeType()==NodeType_DOCUMENT_NODE)
597 aListItem = Reference< XNode >(
598 (Reference< XDocument >(aListItem, UNO_QUERY))->getDocumentElement(), UNO_QUERY);
599 // copy relevant nodes from instance into fragment
600 _cloneNodes(*getModelImpl(), aFragment, aListItem, bRemoveWSNodes);
603 return aFragment;
606 // some forwarding: XPropertySet is implemented in our base class,
607 // but also available as base of XSubmission
608 Reference< ::com::sun::star::beans::XPropertySetInfo > SAL_CALL Submission::getPropertySetInfo( ) throw(RuntimeException, std::exception)
610 return PropertySetBase::getPropertySetInfo();
612 void SAL_CALL Submission::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) throw(UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException, std::exception)
614 PropertySetBase::setPropertyValue( aPropertyName, aValue );
616 Any SAL_CALL Submission::getPropertyValue( const OUString& PropertyName ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException, std::exception)
618 return PropertySetBase::getPropertyValue( PropertyName );
620 void SAL_CALL Submission::addPropertyChangeListener( const OUString& aPropertyName, const Reference< ::com::sun::star::beans::XPropertyChangeListener >& xListener ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException, std::exception)
622 PropertySetBase::addPropertyChangeListener( aPropertyName, xListener );
624 void SAL_CALL Submission::removePropertyChangeListener( const OUString& aPropertyName, const Reference< ::com::sun::star::beans::XPropertyChangeListener >& aListener ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException, std::exception)
626 PropertySetBase::removePropertyChangeListener( aPropertyName, aListener );
628 void SAL_CALL Submission::addVetoableChangeListener( const OUString& PropertyName, const Reference< ::com::sun::star::beans::XVetoableChangeListener >& aListener ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException, std::exception)
630 PropertySetBase::addVetoableChangeListener( PropertyName, aListener );
632 void SAL_CALL Submission::removeVetoableChangeListener( const OUString& PropertyName, const Reference< ::com::sun::star::beans::XVetoableChangeListener >& aListener ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException, std::exception)
634 PropertySetBase::removeVetoableChangeListener( PropertyName, aListener );
637 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */