Bump version to 5.0-43
[LibreOffice.git] / forms / source / xforms / binding.cxx
blob96487e3d030e6582ff564ae2875639e02524a8fc
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 .
21 #include "binding.hxx"
23 #include "model.hxx"
24 #include "unohelper.hxx"
25 #include "NameContainer.hxx"
26 #include "evaluationcontext.hxx"
27 #include "convert.hxx"
28 #include "resourcehelper.hxx"
29 #include "xmlhelper.hxx"
30 #include "xformsevent.hxx"
32 #include <rtl/ustrbuf.hxx>
33 #include <osl/diagnose.h>
35 #include <tools/diagnose_ex.h>
37 #include <algorithm>
38 #include <functional>
40 #include <com/sun/star/uno/Any.hxx>
41 #include <com/sun/star/xml/dom/XNodeList.hpp>
42 #include <com/sun/star/xml/dom/XNode.hpp>
43 #include <com/sun/star/xml/dom/XDocument.hpp>
44 #include <com/sun/star/xml/dom/XElement.hpp>
45 #include <com/sun/star/xml/dom/NodeType.hpp>
46 #include <com/sun/star/xml/dom/events/XEventTarget.hpp>
47 #include <com/sun/star/xml/dom/events/XEventListener.hpp>
48 #include <com/sun/star/xml/dom/events/XDocumentEvent.hpp>
49 #include <com/sun/star/lang/XUnoTunnel.hpp>
50 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
51 #include <com/sun/star/container/XSet.hpp>
52 #include <com/sun/star/container/XNameContainer.hpp>
54 #include <comphelper/propertysetinfo.hxx>
55 #include <unotools/textsearch.hxx>
56 #include <cppuhelper/typeprovider.hxx>
58 using namespace com::sun::star::xml::xpath;
59 using namespace com::sun::star::xml::dom::events;
61 using std::vector;
62 using xforms::Binding;
63 using xforms::MIP;
64 using xforms::Model;
65 using xforms::getResource;
66 using xforms::EvaluationContext;
67 using com::sun::star::beans::PropertyVetoException;
68 using com::sun::star::beans::UnknownPropertyException;
69 using com::sun::star::beans::XPropertySet;
70 using com::sun::star::container::XSet;
71 using com::sun::star::container::XNameAccess;
72 using com::sun::star::form::binding::IncompatibleTypesException;
73 using com::sun::star::form::binding::InvalidBindingStateException;
74 using com::sun::star::form::binding::XValueBinding;
75 using com::sun::star::lang::EventObject;
76 using com::sun::star::lang::IllegalArgumentException;
77 using com::sun::star::lang::IndexOutOfBoundsException;
78 using com::sun::star::lang::NoSupportException;
79 using com::sun::star::lang::NullPointerException;
80 using com::sun::star::lang::WrappedTargetException;
81 using com::sun::star::lang::XUnoTunnel;
82 using com::sun::star::uno::Any;
83 using com::sun::star::uno::Reference;
84 using com::sun::star::uno::RuntimeException;
85 using com::sun::star::uno::Sequence;
86 using com::sun::star::uno::UNO_QUERY;
87 using com::sun::star::uno::UNO_QUERY_THROW;
88 using com::sun::star::uno::XInterface;
89 using com::sun::star::uno::Exception;
90 using com::sun::star::uno::makeAny;
91 using com::sun::star::util::XModifyListener;
92 using com::sun::star::xforms::XDataTypeRepository;
93 using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE;
94 using com::sun::star::xml::dom::NodeType_TEXT_NODE;
95 using com::sun::star::xml::dom::XNode;
96 using com::sun::star::xml::dom::XNodeList;
97 using com::sun::star::xml::dom::events::XEventListener;
98 using com::sun::star::xml::dom::events::XEventTarget;
99 using com::sun::star::xsd::XDataType;
104 #define EXCEPT(msg) OUString(msg),static_cast<XValueBinding*>(this)
106 #define HANDLE_BindingID 0
107 #define HANDLE_BindingExpression 1
108 #define HANDLE_Model 2
109 #define HANDLE_ModelID 3
110 #define HANDLE_BindingNamespaces 4
111 #define HANDLE_ReadonlyExpression 5
112 #define HANDLE_RelevantExpression 6
113 #define HANDLE_RequiredExpression 7
114 #define HANDLE_ConstraintExpression 8
115 #define HANDLE_CalculateExpression 9
116 #define HANDLE_Type 10
117 #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
118 #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
119 #define HANDLE_ModelNamespaces 13
120 #define HANDLE_ExternalData 14
123 Binding::Binding() :
124 mxModel(),
125 msBindingID(),
126 maBindingExpression(),
127 maReadonly(),
128 mxNamespaces( new NameContainer<OUString>() ),
129 mbInCalculate( false ),
130 mnDeferModifyNotifications( 0 ),
131 mbValueModified( false ),
132 mbBindingModified( false )
135 initializePropertySet();
138 Binding::~Binding()
140 _setModel(NULL);
143 void Binding::_setModel( const Model_t& xModel )
145 PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model );
146 PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID );
148 // prepare binding for removal of old model
149 clear(); // remove all cached data (e.g. XPath evaluation results)
150 XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces
152 mxModel = xModel;
154 // set namespaces (and move to model, if appropriate)
155 setBindingNamespaces( xNamespaces );
156 _checkBindingID();
158 notifyAndCachePropertyValue( HANDLE_ExternalData );
162 OUString Binding::getModelID() const
164 Model* pModel = getModelImpl();
165 return ( pModel == NULL ) ? OUString() : pModel->getID();
169 Binding::XNodeList_t Binding::getXNodeList()
171 // first make sure we are bound
172 if( ! maBindingExpression.hasValue() )
173 bind( false );
175 return maBindingExpression.getXNodeList();
178 bool Binding::isSimpleBinding() const
180 return maBindingExpression.isSimpleExpression()
181 && maReadonly.isSimpleExpression()
182 && maRelevant.isSimpleExpression()
183 && maRequired.isSimpleExpression()
184 && maConstraint.isSimpleExpression()
185 && maCalculate.isSimpleExpression();
188 bool Binding::isSimpleBindingExpression() const
190 return maBindingExpression.isSimpleExpression();
193 void Binding::update()
195 // clear all expressions (to remove cached node references)
196 maBindingExpression.clear();
197 maReadonly.clear();
198 maRelevant.clear();
199 maRequired.clear();
200 maConstraint.clear();
201 maCalculate.clear();
203 // let's just pretend the binding has been modified -> full rebind()
204 bindingModified();
207 void Binding::deferNotifications( bool bDefer )
209 mnDeferModifyNotifications += ( bDefer ? 1 : -1 );
210 OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" );
212 if( mnDeferModifyNotifications == 0 )
214 if( mbBindingModified )
215 bindingModified();
216 if( mbValueModified )
217 valueModified();
220 OSL_ENSURE( ( mnDeferModifyNotifications > 0 )
221 || ( ! mbBindingModified && ! mbValueModified ),
222 "deferred modifications not delivered?" );
225 bool Binding::isValid()
227 // TODO: determine whether node is suitable, not just whether it exists
228 return maBindingExpression.getNode().is() &&
229 isValid_DataType() &&
230 maMIP.isConstraint() &&
231 ( ! maMIP.isRequired() ||
232 ( maBindingExpression.hasValue() &&
233 !maBindingExpression.getString().isEmpty() ) );
236 bool Binding::isUseful()
238 // we are useful, if
239 // 0) we don't have a model
240 // (at least, in this case we shouldn't be removed from the model)
241 // 1) we have a proper name
242 // 2) we have some MIPs,
243 // 3) we are bound to some control
244 // (this can be assumed if some listeners are set)
245 bool bUseful =
246 getModelImpl() == NULL
247 // || msBindingID.getLength() > 0
248 || ! msTypeName.isEmpty()
249 || ! maReadonly.isEmptyExpression()
250 || ! maRelevant.isEmptyExpression()
251 || ! maRequired.isEmptyExpression()
252 || ! maConstraint.isEmptyExpression()
253 || ! maCalculate.isEmptyExpression()
254 || ! maModifyListeners.empty()
255 || ! maListEntryListeners.empty()
256 || ! maValidityListeners.empty();
258 return bUseful;
261 OUString Binding::explainInvalid()
263 OUString sReason;
264 if( ! maBindingExpression.getNode().is() )
266 sReason = ( maBindingExpression.getExpression().isEmpty() )
267 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION )
268 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION );
270 else if( ! isValid_DataType() )
272 sReason = explainInvalid_DataType();
273 if( sReason.isEmpty() )
275 // no explanation given by data type? Then give generic message
276 sReason = getResource( RID_STR_XFORMS_INVALID_VALUE,
277 maMIP.getTypeName() );
280 else if( ! maMIP.isConstraint() )
282 sReason = maMIP.getConstraintExplanation();
284 else if( maMIP.isRequired() && maBindingExpression.hasValue() &&
285 maBindingExpression.getString().isEmpty() )
287 sReason = getResource( RID_STR_XFORMS_REQUIRED );
289 // else: no explanation given; should only happen if data is valid
291 OSL_ENSURE( sReason.isEmpty() == isValid(),
292 "invalid data should have an explanation!" );
294 return sReason;
299 EvaluationContext Binding::getEvaluationContext() const
301 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
302 EvaluationContext aContext = getModelImpl()->getEvaluationContext();
303 aContext.mxNamespaces = getBindingNamespaces();
304 return aContext;
307 ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
309 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
311 // bind (in case we were not bound before)
312 bind( false );
313 return _getMIPEvaluationContexts();
317 Binding::IntSequence_t Binding::getUnoTunnelID()
319 static cppu::OImplementationId aImplementationId;
320 return aImplementationId.getImplementationId();
323 Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet )
325 Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY );
326 return xTunnel.is()
327 ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID()))
328 : NULL;
335 void Binding::setBindingID( const OUString& sBindingID )
337 msBindingID = sBindingID;
340 OUString Binding::getBindingExpression() const
342 return maBindingExpression.getExpression();
345 void Binding::setBindingExpression( const OUString& sBindingExpression)
347 maBindingExpression.setExpression( sBindingExpression );
348 bindingModified();
351 OUString Binding::getReadonlyExpression() const
353 return maReadonly.getExpression();
356 void Binding::setReadonlyExpression( const OUString& sReadonly)
358 maReadonly.setExpression( sReadonly );
359 bindingModified();
362 OUString Binding::getRelevantExpression() const
364 return maRelevant.getExpression();
367 void Binding::setRelevantExpression( const OUString& sRelevant )
369 maRelevant.setExpression( sRelevant );
370 bindingModified();
373 OUString Binding::getRequiredExpression() const
375 return maRequired.getExpression();
378 void Binding::setRequiredExpression( const OUString& sRequired )
380 maRequired.setExpression( sRequired );
381 bindingModified();
384 OUString Binding::getConstraintExpression() const
386 return maConstraint.getExpression();
389 void Binding::setConstraintExpression( const OUString& sConstraint )
391 maConstraint.setExpression( sConstraint );
392 msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT,
393 sConstraint );
395 // TODO: This should only re-evaluate the constraint, and notify
396 // the validity constraint listeners; instead we currently pretend
397 // the entire binding was notified, which does a little too much.
398 bindingModified();
401 OUString Binding::getCalculateExpression() const
403 return maCalculate.getExpression();
406 void Binding::setCalculateExpression( const OUString& sCalculate )
408 maCalculate.setExpression( sCalculate );
409 bindingModified();
413 void Binding::setType( const OUString& sTypeName )
415 msTypeName = sTypeName;
416 bindingModified();
419 void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces )
421 _setNamespaces( rNamespaces, true );
424 Binding::XNameContainer_t Binding::getModelNamespaces() const
426 return _getNamespaces();
429 void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces )
431 _setNamespaces( rNamespaces, false );
434 bool Binding::getReadOnly() const
436 return maMIP.isReadonly();
439 bool Binding::getRelevant() const
441 return maMIP.isRelevant();
444 bool Binding::getExternalData() const
446 bool bExternalData = true;
447 if ( !mxModel.is() )
448 return bExternalData;
452 Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW );
453 OSL_VERIFY(
454 xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
456 catch( const Exception& )
458 DBG_UNHANDLED_EXCEPTION();
460 return bExternalData;
464 void Binding::checkLive()
465 throw( RuntimeException )
467 if( ! isLive() )
468 throw RuntimeException( EXCEPT("Binding not initialized") );
471 void Binding::checkModel()
472 throw( RuntimeException )
474 if( ! mxModel.is() )
475 throw RuntimeException( EXCEPT("Binding has no Model") );
478 bool Binding::isLive() const
480 const Model* pModel = getModelImpl();
481 return pModel && pModel->isInitialized();
484 Model* Binding::getModelImpl() const
486 return getModelImpl( mxModel );
489 Model* Binding::getModelImpl( const Model_t& xModel )
491 Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY );
492 Model* pModel = xTunnel.is()
493 ? reinterpret_cast<Model*>(
494 xTunnel->getSomething( Model::getUnoTunnelID() ) )
495 : NULL;
496 return pModel;
499 static void lcl_addListenerToNode( Reference<XNode> xNode,
500 Reference<XEventListener> xListener )
502 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
503 if( xTarget.is() )
505 xTarget->addEventListener( "DOMCharacterDataModified",
506 xListener, false );
507 xTarget->addEventListener( "DOMCharacterDataModified",
508 xListener, true );
509 xTarget->addEventListener( "DOMAttrModified",
510 xListener, false );
511 xTarget->addEventListener( "DOMAttrModified",
512 xListener, true );
513 xTarget->addEventListener( "DOMAttrModified",
514 xListener, true );
515 xTarget->addEventListener( "xforms-generic",
516 xListener, true );
520 static void lcl_removeListenerFromNode( Reference<XNode> xNode,
521 Reference<XEventListener> xListener )
523 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
524 if( xTarget.is() )
526 xTarget->removeEventListener( "DOMCharacterDataModified",
527 xListener, false );
528 xTarget->removeEventListener( "DOMCharacterDataModified",
529 xListener, true );
530 xTarget->removeEventListener( "DOMAttrModified",
531 xListener, false );
532 xTarget->removeEventListener( "DOMAttrModified",
533 xListener, true );
534 xTarget->removeEventListener( "xforms-generic",
535 xListener, true );
539 ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const
541 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
543 // iterate over nodes of bind expression and create
544 // EvaluationContext for each
545 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
546 ::std::vector<EvaluationContext> aVector;
547 sal_Int32 nCount = 0; // count nodes for context position
548 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
549 aIter != aNodes.end();
550 ++aIter, ++nCount )
552 OSL_ENSURE( aIter->is(), "no node?" );
554 // create proper evaluation context for this MIP
555 aVector.push_back( EvaluationContext( *aIter, getModel(),
556 getBindingNamespaces(),
557 nCount, aNodes.size() ) );
559 return aVector;
562 void Binding::bind( bool bForceRebind )
564 checkModel();
566 // bind() will evaluate this binding as follows:
567 // 1) evaluate the binding expression
568 // 1b) if necessary, create node according to 'lazy author' rules
569 // 2) register suitable listeners on the instance (and remove old ones)
570 // 3) remove old MIPs defined by this binding
571 // 4) for every node in the binding nodeset do:
572 // 1) create proper evaluation context for this MIP
573 // 2) evaluate calculate expression (and push value into instance)
574 // 3) evaluate remaining MIPs
575 // 4) evaluate the locally defined MIPs, and push them to the model
578 // 1) evaluate the binding expression
579 EvaluationContext aContext = getEvaluationContext();
580 maBindingExpression.evaluate( aContext );
581 if( ! maBindingExpression.getNode().is() )
583 // 1b) create node (if valid element name)
584 if( isValidQName( maBindingExpression.getExpression(),
585 aContext.mxNamespaces ) )
587 aContext.mxContextNode->appendChild(
588 Reference<XNode>(
589 aContext.mxContextNode->getOwnerDocument()->createElement(
590 maBindingExpression.getExpression() ),
591 UNO_QUERY ) );
592 maBindingExpression.evaluate( aContext );
593 OSL_ENSURE( maBindingExpression.getNode().is(),
594 "we should bind to the newly inserted node!" );
597 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
599 // 2) register suitable listeners on the instance (and remove old ones)
600 if( maEventNodes.empty() || bForceRebind )
602 for( XNodes_t::iterator aIter = maEventNodes.begin();
603 aIter != maEventNodes.end();
604 ++aIter )
605 lcl_removeListenerFromNode( *aIter, this );
606 maEventNodes.clear();
607 if( isSimpleBinding() )
608 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
609 aIter != aNodes.end();
610 ++aIter )
611 maEventNodes.push_back( *aIter );
612 else
613 maEventNodes.push_back(
614 Reference<XNode>( aContext.mxContextNode->getOwnerDocument(),
615 UNO_QUERY_THROW ) );
616 for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin();
617 aIter2 != maEventNodes.end();
618 ++aIter2 )
619 lcl_addListenerToNode( *aIter2, this );
622 // 3) remove old MIPs defined by this binding
623 Model* pModel = getModelImpl();
624 OSL_ENSURE( pModel != NULL, "need model" );
625 pModel->removeMIPs( this );
627 // 4) calculate all MIPs
628 ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts();
629 for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin();
630 aIter != aMIPContexts.end();
631 ++aIter )
633 EvaluationContext& rContext = *aIter;
635 // evaluate calculate expression (and push value into instance)
636 // (prevent recursion using mbInCalculate
637 if( ! maCalculate.isEmptyExpression() )
639 if( ! mbInCalculate )
641 mbInCalculate = true;
642 maCalculate.evaluate( rContext );
643 pModel->setSimpleContent( rContext.mxContextNode,
644 maCalculate.getString() );
645 mbInCalculate = false;
649 // now evaluate remaining MIPs in the appropriate context
650 maReadonly.evaluate( rContext );
651 maRelevant.evaluate( rContext );
652 maRequired.evaluate( rContext );
653 maConstraint.evaluate( rContext );
654 // type is static; does not need updating
656 // evaluate the locally defined MIPs, and push them to the model
657 pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() );
662 // helper for Binding::valueModified
663 static void lcl_modified( const Binding::XModifyListener_t xListener,
664 const Reference<XInterface>& xSource )
666 OSL_ENSURE( xListener.is(), "no listener?" );
667 xListener->modified( EventObject( xSource ) );
670 // helper for Binding::valueModified
671 static void lcl_listentry( const Binding::XListEntryListener_t xListener,
672 const Reference<XInterface>& xSource )
674 OSL_ENSURE( xListener.is(), "no listener?" );
675 // TODO: send fine granular events
676 xListener->allEntriesChanged( EventObject( xSource ) );
679 // helper for Binding::valueModified
680 static void lcl_validate( const Binding::XValidityConstraintListener_t xListener,
681 const Reference<XInterface>& xSource )
683 OSL_ENSURE( xListener.is(), "no listener?" );
684 xListener->validityConstraintChanged( EventObject( xSource ) );
688 void Binding::valueModified()
690 // defer notifications, if so desired
691 if( mnDeferModifyNotifications > 0 )
693 mbValueModified = true;
694 return;
696 mbValueModified = false;
698 // query MIP used by our first node (also note validity)
699 Reference<XNode> xNode = maBindingExpression.getNode();
700 maMIP = getModelImpl()->queryMIP( xNode );
702 // distribute MIPs _used_ by this binding
703 if( xNode.is() )
705 notifyAndCachePropertyValue( HANDLE_ReadOnly );
706 notifyAndCachePropertyValue( HANDLE_Relevant );
709 // iterate over _value_ listeners and send each a modified signal,
710 // using this object as source (will also update validity, because
711 // control will query once the value has changed)
712 Reference<XInterface> xSource = static_cast<XPropertySet*>( this );
713 ::std::for_each( maModifyListeners.begin(),
714 maModifyListeners.end(),
715 ::std::bind( lcl_modified, std::placeholders::_1, xSource ) );
716 ::std::for_each( maListEntryListeners.begin(),
717 maListEntryListeners.end(),
718 ::std::bind( lcl_listentry, std::placeholders::_1, xSource ) );
719 ::std::for_each( maValidityListeners.begin(),
720 maValidityListeners.end(),
721 ::std::bind( lcl_validate, std::placeholders::_1, xSource ) );
723 // now distribute MIPs to children
724 if( xNode.is() )
725 distributeMIP( xNode->getFirstChild() );
728 void Binding::distributeMIP( const XNode_t & rxNode ) {
730 typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t;
731 OUString sEventName("xforms-generic");
732 XFormsEvent_t *pEvent = new XFormsEvent_t;
733 pEvent->initXFormsEvent(sEventName, sal_True, sal_False);
734 Reference<XEvent> xEvent(pEvent);
736 // naive depth-first traversal
737 XNode_t xNode( rxNode );
738 while(xNode.is()) {
740 // notifications should be triggered at the
741 // leaf nodes first, bubbling upwards the hierarchy.
742 XNode_t child(xNode->getFirstChild());
743 if(child.is())
744 distributeMIP(child);
746 // we're standing at a particular node somewhere
747 // below the one which changed a property (MIP).
748 // bindings which are listening at this node will receive
749 // a notification message about what exactly happened.
750 Reference< XEventTarget > target(xNode,UNO_QUERY);
751 target->dispatchEvent(xEvent);
753 xNode = xNode->getNextSibling();
757 void Binding::bindingModified()
759 // defer notifications, if so desired
760 if( mnDeferModifyNotifications > 0 )
762 mbBindingModified = true;
763 return;
765 mbBindingModified = false;
767 // rebind (if live); then call valueModified
768 // A binding should be inert until its model is fully constructed.
769 if( isLive() )
771 bind( true );
772 valueModified();
777 MIP Binding::getLocalMIP() const
779 MIP aMIP;
781 if( maReadonly.hasValue() )
782 aMIP.setReadonly( maReadonly.getBool( false ) );
783 if( maRelevant.hasValue() )
784 aMIP.setRelevant( maRelevant.getBool( true ) );
785 if( maRequired.hasValue() )
786 aMIP.setRequired( maRequired.getBool( false ) );
787 if( maConstraint.hasValue() )
789 aMIP.setConstraint( maConstraint.getBool( true ) );
790 if( ! aMIP.isConstraint() )
791 aMIP.setConstraintExplanation( msExplainConstraint );
793 if( !msTypeName.isEmpty() )
794 aMIP.setTypeName( msTypeName );
796 // calculate: only handle presence of calculate; value set elsewhere
797 aMIP.setHasCalculate( !maCalculate.isEmptyExpression() );
799 return aMIP;
802 Binding::XDataType_t Binding::getDataType()
804 OSL_ENSURE( getModel().is(), "need model" );
805 OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" );
807 Reference<XDataTypeRepository> xRepository(
808 getModel()->getDataTypeRepository(), UNO_QUERY );
809 OUString sTypeName = maMIP.getTypeName();
811 return ( xRepository.is() && xRepository->hasByName( sTypeName ) )
812 ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY)
813 : Reference<XDataType>( NULL );
816 bool Binding::isValid_DataType()
818 Reference<XDataType> xDataType = getDataType();
819 return !xDataType.is()
820 || xDataType->validate( maBindingExpression.getString() );
823 OUString Binding::explainInvalid_DataType()
825 Reference<XDataType> xDataType = getDataType();
826 return xDataType.is()
827 ? xDataType->explainInvalid( maBindingExpression.getString() )
828 : OUString();
831 void Binding::clear()
833 // remove MIPs contributed by this binding
834 Model* pModel = getModelImpl();
835 if( pModel != NULL )
836 pModel->removeMIPs( this );
838 // remove all references
839 for( XNodes_t::iterator aIter = maEventNodes.begin();
840 aIter != maEventNodes.end();
841 ++aIter )
842 lcl_removeListenerFromNode( *aIter, this );
843 maEventNodes.clear();
845 // clear expressions
846 maBindingExpression.clear();
847 maReadonly.clear();
848 maRelevant.clear();
849 maRequired.clear();
850 maConstraint.clear();
851 maCalculate.clear();
853 // TODO: what about our listeners?
857 static void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom,
858 Binding::XNameContainer_t& xTo )
860 OSL_ENSURE( xFrom.is(), "no source" );
861 OSL_ENSURE( xTo.is(), "no target" );
863 // iterate over name in source
864 Sequence<OUString> aNames = xTo->getElementNames();
865 sal_Int32 nNames = aNames.getLength();
866 const OUString* pNames = aNames.getConstArray();
867 for( sal_Int32 i = 0; i < nNames; i++ )
869 const OUString& rName = pNames[i];
871 if( ! xFrom->hasByName( rName ) )
872 xTo->removeByName( rName );
876 /** copy namespaces from one namespace container into another
877 * @param bOverwrite true: overwrite namespaces in target
878 * false: do not overwrite namespaces in target
879 * @param bMove true: move namespaces (i.e., delete in source)
880 * false: copy namespaces (do not modify source)
881 * @param bFromSource true: use elements from source
882 * false: use only elements from target
884 static void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom,
885 Binding::XNameContainer_t& xTo,
886 bool bOverwrite )
888 OSL_ENSURE( xFrom.is(), "no source" );
889 OSL_ENSURE( xTo.is(), "no target" );
891 // iterate over name in source
892 Sequence<OUString> aNames = xFrom->getElementNames();
893 sal_Int32 nNames = aNames.getLength();
894 const OUString* pNames = aNames.getConstArray();
895 for( sal_Int32 i = 0; i < nNames; i++ )
897 const OUString& rName = pNames[i];
899 // determine whether to copy the value, and whether to delete
900 // it in the source:
902 bool bInTarget = xTo->hasByName( rName );
904 // we copy: if property is in target, and
905 // if bOverwrite is set, or when the namespace prefix is free
906 bool bCopy = bOverwrite || ! bInTarget;
908 // and now... ACTION!
909 if( bCopy )
911 if( bInTarget )
912 xTo->replaceByName( rName, xFrom->getByName( rName ) );
913 else
914 xTo->insertByName( rName, xFrom->getByName( rName ) );
919 // implement get*Namespaces()
920 // (identical for both variants)
921 Binding::XNameContainer_t Binding::_getNamespaces() const
923 XNameContainer_t xNamespaces = new NameContainer<OUString>();
924 lcl_copyNamespaces( mxNamespaces, xNamespaces, true );
926 // merge model's with binding's own namespaces
927 Model* pModel = getModelImpl();
928 if( pModel != NULL )
929 lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false );
931 return xNamespaces;
934 // implement set*Namespaces()
935 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
936 void Binding::_setNamespaces( const XNameContainer_t& rNamespaces,
937 bool bBinding )
939 Model* pModel = getModelImpl();
940 XNameContainer_t xModelNamespaces = ( pModel != NULL )
941 ? pModel->getNamespaces()
942 : NULL;
943 OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?");
945 // remove deleted namespaces
946 lcl_removeOtherNamespaces( rNamespaces, mxNamespaces );
947 if( !bBinding && xModelNamespaces.is() )
948 lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces );
950 // copy namespaces as appropriate
951 Sequence<OUString> aNames = rNamespaces->getElementNames();
952 sal_Int32 nNames = aNames.getLength();
953 const OUString* pNames = aNames.getConstArray();
954 for( sal_Int32 i = 0; i < nNames; i++ )
956 const OUString& rName = pNames[i];
957 Any aValue = rNamespaces->getByName( rName );
959 // determine whether the namespace should go into model's or
960 // into binding's namespaces
961 bool bLocal =
962 ! xModelNamespaces.is()
963 || mxNamespaces->hasByName( rName )
964 || ( bBinding
965 && xModelNamespaces.is()
966 && xModelNamespaces->hasByName( rName ) );
968 // write namespace into the appropriate namespace container
969 XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces;
970 OSL_ENSURE( rWhich.is(), "whoops" );
971 if( rWhich->hasByName( rName ) )
972 rWhich->replaceByName( rName, aValue );
973 else
974 rWhich->insertByName( rName, aValue );
976 // always 'promote' namespaces from binding to model, if equal
977 if( xModelNamespaces.is()
978 && xModelNamespaces->hasByName( rName )
979 && mxNamespaces->hasByName( rName )
980 && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) )
982 mxNamespaces->removeByName( rName );
986 // ... done. But we modified the binding!
987 bindingModified();
990 void Binding::_checkBindingID()
992 if( getModel().is() )
994 Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW );
995 if( msBindingID.isEmpty() )
997 // no binding ID? then make one up!
998 OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ) + " ";
999 sal_Int32 nNumber = 0;
1000 OUString sName;
1003 nNumber++;
1004 sName = sIDPrefix + OUString::number( nNumber );
1006 while( xBindings->hasByName( sName ) );
1007 setBindingID( sName );
1016 // XValueBinding
1019 Binding::Sequence_Type_t Binding::getSupportedValueTypes()
1020 throw( RuntimeException, std::exception )
1022 return Convert::get().getTypes();
1025 sal_Bool Binding::supportsType( const Type_t& rType )
1026 throw( RuntimeException, std::exception )
1028 return Convert::get().hasType( rType );
1031 Binding::Any_t Binding::getValue( const Type_t& rType )
1032 throw( IncompatibleTypesException,
1033 RuntimeException, std::exception )
1035 // first, check for model
1036 checkLive();
1038 // second, check for type
1039 if( ! supportsType( rType ) )
1040 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1042 // return string value (if present; else return empty Any)
1043 Binding::Any_t result = Any();
1044 if(maBindingExpression.hasValue()) {
1045 OUString pathExpr(maBindingExpression.getString());
1046 Convert &rConvert = Convert::get();
1047 result = rConvert.toAny(pathExpr,rType);
1050 return result;
1053 void Binding::setValue( const Any_t& aValue )
1054 throw( IncompatibleTypesException,
1055 InvalidBindingStateException,
1056 NoSupportException,
1057 RuntimeException, std::exception )
1059 // first, check for model
1060 checkLive();
1062 // check for supported type
1063 if( ! supportsType( aValue.getValueType() ) )
1064 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1066 if( maBindingExpression.hasValue() )
1068 Binding::XNode_t xNode = maBindingExpression.getNode();
1069 if( xNode.is() )
1071 OUString sValue = Convert::get().toXSD( aValue );
1072 bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue );
1073 if( ! bSuccess )
1074 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1076 else
1077 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1079 else
1080 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1085 // XListEntry Source
1088 sal_Int32 Binding::getListEntryCount()
1089 throw( RuntimeException, std::exception )
1091 // first, check for model
1092 checkLive();
1094 // return size of node list
1095 return maBindingExpression.getNodeList().size();
1098 static void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer )
1100 if( xNode->getNodeType() == NodeType_TEXT_NODE
1101 || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
1103 rBuffer.append( xNode->getNodeValue() );
1105 else
1107 for( Reference<XNode> xChild = xNode->getFirstChild();
1108 xChild.is();
1109 xChild = xChild->getNextSibling() )
1111 lcl_getString( xChild, rBuffer );
1116 static OUString lcl_getString( const Reference<XNode>& xNode )
1118 OUStringBuffer aBuffer;
1119 lcl_getString( xNode, aBuffer );
1120 return aBuffer.makeStringAndClear();
1123 OUString Binding::getListEntry( sal_Int32 nPosition )
1124 throw( IndexOutOfBoundsException,
1125 RuntimeException, std::exception )
1127 // first, check for model
1128 checkLive();
1130 // check bounds and return proper item
1131 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1132 if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) )
1133 throw IndexOutOfBoundsException( EXCEPT("") );
1134 return lcl_getString( aNodes[ nPosition ] );
1137 Sequence<OUString> Binding::getAllListEntries()
1138 throw( RuntimeException, std::exception )
1140 // first, check for model
1141 checkLive();
1143 // create sequence of string values
1144 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1145 Sequence<OUString> aSequence( aNodes.size() );
1146 OUString* pSequence = aSequence.getArray();
1147 for( sal_Int32 n = 0; n < aSequence.getLength(); n++ )
1149 pSequence[n] = lcl_getString( aNodes[n] );
1152 return aSequence;
1155 void Binding::addListEntryListener( const XListEntryListener_t& xListener )
1156 throw( NullPointerException,
1157 RuntimeException, std::exception )
1159 OSL_ENSURE( xListener.is(), "need listener!" );
1160 if( ::std::find( maListEntryListeners.begin(),
1161 maListEntryListeners.end(),
1162 xListener)
1163 == maListEntryListeners.end() )
1164 maListEntryListeners.push_back( xListener );
1167 void Binding::removeListEntryListener( const XListEntryListener_t& xListener )
1168 throw( NullPointerException,
1169 RuntimeException, std::exception )
1171 XListEntryListeners_t::iterator aIter =
1172 ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(),
1173 xListener );
1174 if( aIter != maListEntryListeners.end() )
1175 maListEntryListeners.erase( aIter );
1180 // XValidator
1183 sal_Bool Binding::isValid( const Any_t& )
1184 throw( RuntimeException, std::exception )
1186 // first, check for model
1187 checkLive();
1189 // ignore value; determine validate only on current data
1190 return isValid();
1193 OUString Binding::explainInvalid(
1194 const Any_t& /*Value*/ )
1195 throw( RuntimeException, std::exception )
1197 // first, check for model
1198 checkLive();
1200 // ignore value; determine explanation only on current data
1201 return explainInvalid();
1204 void Binding::addValidityConstraintListener(
1205 const XValidityConstraintListener_t& xListener )
1206 throw( NullPointerException,
1207 RuntimeException, std::exception )
1209 OSL_ENSURE( xListener.is(), "need listener!" );
1210 if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener)
1211 == maValidityListeners.end() )
1212 maValidityListeners.push_back( xListener );
1215 void Binding::removeValidityConstraintListener(
1216 const XValidityConstraintListener_t& xListener )
1217 throw( NullPointerException,
1218 RuntimeException, std::exception )
1220 XValidityConstraintListeners_t::iterator aIter =
1221 ::std::find( maValidityListeners.begin(), maValidityListeners.end(),
1222 xListener );
1223 if( aIter != maValidityListeners.end() )
1224 maValidityListeners.erase( aIter );
1230 // xml::dom::event::XEventListener
1233 void Binding::handleEvent( const XEvent_t& xEvent )
1234 throw( RuntimeException, std::exception )
1236 OUString sType(xEvent->getType());
1237 //OUString sEventMIPChanged("xforms-generic");
1238 //if(sType.equals(sEventMIPChanged)) {
1239 if(sType == "xforms-generic") {
1241 // the modification of the 'mnDeferModifyNotifications'-member
1242 // is necessary to prevent infinite notication looping.
1243 // This can happened in case the binding which caused
1244 // the notification chain is listening to those events
1245 // as well...
1246 bool bPreserveValueModified = mbValueModified;
1247 mnDeferModifyNotifications++;
1248 valueModified();
1249 --mnDeferModifyNotifications;
1250 mbValueModified = bPreserveValueModified;
1251 return;
1254 // if we're a dynamic binding, we better re-bind, too!
1255 bind( false );
1257 // our value was maybe modified
1258 valueModified();
1263 // lang::XUnoTunnel
1266 sal_Int64 Binding::getSomething( const IntSequence_t& xId )
1267 throw( RuntimeException, std::exception )
1269 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
1273 // XCloneable
1276 Binding::XCloneable_t SAL_CALL Binding::createClone()
1277 throw( RuntimeException, std::exception )
1279 Reference< XPropertySet > xClone;
1281 Model* pModel = getModelImpl();
1282 if ( pModel )
1283 xClone = pModel->cloneBinding( this );
1284 else
1286 xClone = new Binding;
1287 copy( this, xClone );
1289 return XCloneable_t( xClone, UNO_QUERY );
1293 // property set implementations
1296 #define REGISTER_PROPERTY( property, type ) \
1297 registerProperty( PROPERTY( property, type ), \
1298 new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) );
1300 #define REGISTER_PROPERTY_RO( property, type ) \
1301 registerProperty( PROPERTY_RO( property, type ), \
1302 new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) );
1304 #define REGISTER_BOOL_PROPERTY_RO( property ) \
1305 registerProperty( PROPERTY_RO( property, sal_Bool ), \
1306 new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) );
1308 void Binding::initializePropertySet()
1310 REGISTER_PROPERTY ( BindingID, OUString );
1311 REGISTER_PROPERTY ( BindingExpression, OUString );
1312 REGISTER_PROPERTY_RO ( Model, Model_t );
1313 REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t );
1314 REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t );
1315 REGISTER_PROPERTY_RO ( ModelID, OUString );
1316 REGISTER_PROPERTY ( ReadonlyExpression, OUString );
1317 REGISTER_PROPERTY ( RelevantExpression, OUString );
1318 REGISTER_PROPERTY ( RequiredExpression, OUString );
1319 REGISTER_PROPERTY ( ConstraintExpression, OUString );
1320 REGISTER_PROPERTY ( CalculateExpression, OUString );
1321 REGISTER_PROPERTY ( Type, OUString );
1322 REGISTER_PROPERTY_RO ( ReadOnly, bool );
1323 REGISTER_PROPERTY_RO ( Relevant, bool );
1324 REGISTER_BOOL_PROPERTY_RO( ExternalData );
1326 initializePropertyValueCache( HANDLE_ReadOnly );
1327 initializePropertyValueCache( HANDLE_Relevant );
1328 initializePropertyValueCache( HANDLE_ExternalData );
1331 void Binding::addModifyListener(
1332 const XModifyListener_t& xListener )
1333 throw( RuntimeException, std::exception )
1335 OSL_ENSURE( xListener.is(), "need listener!" );
1336 if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener )
1337 == maModifyListeners.end() )
1338 maModifyListeners.push_back( xListener );
1340 // HACK: currently, we have to 'push' some MIPs to the control
1341 // (read-only, relevant, etc.) To enable this, we need to update
1342 // the control at least once when it registers here.
1343 valueModified();
1346 void Binding::removeModifyListener(
1347 const XModifyListener_t& xListener )
1348 throw( RuntimeException, std::exception )
1350 ModifyListeners_t::iterator aIter =
1351 ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener );
1352 if( aIter != maModifyListeners.end() )
1353 maModifyListeners.erase( aIter );
1359 OUString Binding::getName()
1360 throw( RuntimeException, std::exception )
1362 return getBindingID();
1365 void SAL_CALL Binding::setName( const OUString& rName )
1366 throw( RuntimeException, std::exception )
1368 // use the XPropertySet methods, so the change in the name is notified to the
1369 // property listeners
1370 setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) );
1373 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */