nss: upgrade to release 3.73
[LibreOffice.git] / forms / source / xforms / submission.cxx
blob6b759e18453e792aae9bfb416f2e136707147cd8
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 "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>
51 #include <memory>
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;
65 using xforms::Model;
66 using xforms::MIP;
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() :
73 msID(),
74 msBind(),
75 maRef(),
76 msAction(),
77 msMethod(),
78 msVersion(),
79 mbIndent(),
80 msMediaType(),
81 msEncoding(),
82 mbOmitXmlDeclaration(),
83 mbStandalone(),
84 msCDataSectionElement(),
85 msReplace( "none" ),
86 msSeparator(),
87 msIncludeNamespacePrefixes()
89 initializePropertySet();
92 Submission::~Submission() throw()
97 void Submission::setModel( const Reference<XModel>& xModel )
99 mxModel = xModel;
103 void Submission::setID( const OUString& sID )
105 msID = sID;
109 void Submission::setBind( const OUString& sBind )
111 msBind = 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 )
127 msAction = sAction;
131 void Submission::setMethod( const OUString& sMethod )
133 msMethod = sMethod;
137 void Submission::setVersion( const OUString& sVersion )
139 msVersion = sVersion;
143 void Submission::setIndent( bool bIndent )
145 mbIndent = 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 )
198 liveCheck();
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();
218 else
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
228 if( ! xResult.is() )
229 return false;
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));
247 else
249 OSL_FAIL("Unsupported xforms submission method");
250 return false;
253 if (!xSubmission->IsWebProtocol())
254 return false;
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();
278 if( ! bValid )
279 throw RuntimeException();
282 Model* Submission::getModelImpl() const
284 Model* pModel = nullptr;
285 if( mxModel.is() )
286 pModel = comphelper::getUnoTunnelImplementation<Model>( mxModel );
287 return pModel;
291 // Property-Set implementation
294 #define HANDLE_ID 0
295 #define HANDLE_Bind 1
296 #define HANDLE_Ref 2
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
350 OUString sTokenList;
351 if ( rValue >>= sTokenList )
353 std::vector< OUString > aPrefixes;
354 sal_Int32 p = 0;
355 while ( p >= 0 )
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()
368 return getID();
371 void SAL_CALL Submission::setName( const OUString& sID )
373 setID( 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 + ".";
387 return aMessage;
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.",
401 *this
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();
410 if( ! bValid )
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() )
440 bValid = true;
443 // abort if invalid (and user didn't tell us to continue)
444 if( ! bValid )
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?" );
457 // allowed to leave
458 throw;
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" ),
466 *this, anyEx );
469 if( !bResult )
471 // other failure: throw wrapped target exception, too.
472 throw WrappedTargetException(
473 lcl_message( sID, OUString() ), *this, Any() );
475 mxModel->rebuild();
478 void SAL_CALL Submission::submit( )
480 submitWithInteraction( nullptr );
483 void SAL_CALL Submission::addSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
485 // TODO
486 throw NoSupportException();
489 void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
491 // TODO
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;
504 return false;
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;
516 if (!cur.is())
517 return;
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;
533 // result
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();
542 return aDocument;
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);
566 return aFragment;
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: */