bump product version to 4.1.6.2
[LibreOffice.git] / forms / source / xforms / submission.cxx
bloba9dd5348627edca36e8ff2f34e461aed26ab38a6
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 <memory>
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;
79 using std::auto_ptr;
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() :
86 msID(),
87 msBind(),
88 maRef(),
89 msAction(),
90 msMethod(),
91 msVersion(),
92 mbIndent(),
93 msMediaType(),
94 msEncoding(),
95 mbOmitXmlDeclaration(),
96 mbStandalone(),
97 msCDataSectionElement(),
98 msReplace( "none" ),
99 msSeparator(),
100 msIncludeNamespacePrefixes()
102 initializePropertySet();
105 Submission::~Submission() throw()
109 Reference<XModel> Submission::getModel() const
111 return mxModel;
114 void Submission::setModel( const Reference<XModel>& xModel )
116 mxModel = xModel;
119 OUString Submission::getID() const
121 return msID;
124 void Submission::setID( const OUString& sID )
126 msID = sID;
129 OUString Submission::getBind() const
131 return msBind;
134 void Submission::setBind( const OUString& sBind )
136 msBind = 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
151 return msAction;
154 void Submission::setAction( const OUString& sAction )
156 msAction = sAction;
159 OUString Submission::getMethod() const
161 return msMethod;
164 void Submission::setMethod( const OUString& sMethod )
166 msMethod = sMethod;
169 OUString Submission::getVersion() const
171 return msVersion;
174 void Submission::setVersion( const OUString& sVersion )
176 msVersion = sVersion;
179 bool Submission::getIndent() const
181 return mbIndent;
184 void Submission::setIndent( bool bIndent )
186 mbIndent = bIndent;
189 OUString Submission::getMediaType() const
191 return msMediaType;
194 void Submission::setMediaType( const OUString& sMediaType )
196 msMediaType = sMediaType;
199 OUString Submission::getEncoding() const
201 return msEncoding;
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
221 return mbStandalone;
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
241 return msReplace;
244 void Submission::setReplace( const OUString& sReplace )
246 msReplace = sReplace;
249 OUString Submission::getSeparator() const
251 return msSeparator;
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 )
271 liveCheck();
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();
291 else
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
301 if( ! xResult.is() )
302 return false;
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));
323 else
325 OSL_FAIL("Unsupported xforms submission method");
326 return false;
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 );
351 return xTunnel.is()
352 ? reinterpret_cast<Submission*>(
353 xTunnel->getSomething( getUnoTunnelID() ) )
354 : NULL;
362 void Submission::liveCheck()
363 throw( RuntimeException )
365 bool bValid = mxModel.is();
367 if( ! bValid )
368 throw RuntimeException();
371 Model* Submission::getModelImpl() const
373 Model* pModel = NULL;
374 if( mxModel.is() )
375 pModel = Model::getModel( mxModel );
376 return pModel;
381 // Property-Set implementation
384 #define HANDLE_ID 0
385 #define HANDLE_Bind 1
386 #define HANDLE_Ref 2
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
441 OUString sTokenList;
442 if ( rValue >>= sTokenList )
444 std::vector< OUString > aPrefixes;
445 sal_Int32 p = 0;
446 while ( p >= 0 )
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 )
460 return getID();
463 void SAL_CALL Submission::setName( const OUString& sID )
464 throw( RuntimeException )
466 setID( sID );
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,
494 RuntimeException )
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." ),
504 *this
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();
513 if( ! bValid )
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() )
543 bValid = true;
546 // abort if invalid (and user didn't tell us to continue)
547 if( ! bValid )
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?" );
560 // allowed to leave
561 throw;
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 ) );
571 if( bResult )
573 mxModel->rebuild();
575 else
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)
590 // TODO
591 throw NoSupportException();
594 void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ ) throw (NoSupportException, RuntimeException)
596 // TODO
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;
609 return sal_False;
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;
621 if (cur.is())
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;
638 // result
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();
647 return aDocument;
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);
672 return aFragment;
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: */