update credits
[LibreOffice.git] / forms / source / xforms / binding.cxx
blob99222b267f7efd4198dca1b4fa91e8f3a5b0af3f
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() throw()
140 _setModel(NULL);
144 Binding::Model_t Binding::getModel() const
146 return mxModel;
149 void Binding::_setModel( const Model_t& xModel )
151 PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model );
152 PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID );
154 // prepare binding for removal of old model
155 clear(); // remove all cached data (e.g. XPath evaluation results)
156 XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces
158 mxModel = xModel;
160 // set namespaces (and move to model, if appropriate)
161 setBindingNamespaces( xNamespaces );
162 _checkBindingID();
164 notifyAndCachePropertyValue( HANDLE_ExternalData );
168 OUString Binding::getModelID() const
170 Model* pModel = getModelImpl();
171 return ( pModel == NULL ) ? OUString() : pModel->getID();
175 Binding::XNodeList_t Binding::getXNodeList()
177 // first make sure we are bound
178 if( ! maBindingExpression.hasValue() )
179 bind( sal_False );
181 return maBindingExpression.getXNodeList();
184 bool Binding::isSimpleBinding() const
186 return maBindingExpression.isSimpleExpression()
187 && maReadonly.isSimpleExpression()
188 && maRelevant.isSimpleExpression()
189 && maRequired.isSimpleExpression()
190 && maConstraint.isSimpleExpression()
191 && maCalculate.isSimpleExpression();
194 bool Binding::isSimpleBindingExpression() const
196 return maBindingExpression.isSimpleExpression();
199 void Binding::update()
201 // clear all expressions (to remove cached node references)
202 maBindingExpression.clear();
203 maReadonly.clear();
204 maRelevant.clear();
205 maRequired.clear();
206 maConstraint.clear();
207 maCalculate.clear();
209 // let's just pretend the binding has been modified -> full rebind()
210 bindingModified();
213 void Binding::deferNotifications( bool bDefer )
215 mnDeferModifyNotifications += ( bDefer ? 1 : -1 );
216 OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" );
218 if( mnDeferModifyNotifications == 0 )
220 if( mbBindingModified )
221 bindingModified();
222 if( mbValueModified )
223 valueModified();
226 OSL_ENSURE( ( mnDeferModifyNotifications > 0 )
227 || ( ! mbBindingModified && ! mbValueModified ),
228 "deferred modifications not delivered?" );
231 bool Binding::isValid()
233 // TODO: determine whether node is suitable, not just whether it exists
234 return maBindingExpression.getNode().is() &&
235 isValid_DataType() &&
236 maMIP.isConstraint() &&
237 ( ! maMIP.isRequired() ||
238 ( maBindingExpression.hasValue() &&
239 !maBindingExpression.getString().isEmpty() ) );
242 bool Binding::isUseful()
244 // we are useful, if
245 // 0) we don't have a model
246 // (at least, in this case we shouldn't be removed from the model)
247 // 1) we have a proper name
248 // 2) we have some MIPs,
249 // 3) we are bound to some control
250 // (this can be assumed if some listeners are set)
251 bool bUseful =
252 getModelImpl() == NULL
253 // || msBindingID.getLength() > 0
254 || ! msTypeName.isEmpty()
255 || ! maReadonly.isEmptyExpression()
256 || ! maRelevant.isEmptyExpression()
257 || ! maRequired.isEmptyExpression()
258 || ! maConstraint.isEmptyExpression()
259 || ! maCalculate.isEmptyExpression()
260 || ! maModifyListeners.empty()
261 || ! maListEntryListeners.empty()
262 || ! maValidityListeners.empty();
264 return bUseful;
267 OUString Binding::explainInvalid()
269 OUString sReason;
270 if( ! maBindingExpression.getNode().is() )
272 sReason = ( maBindingExpression.getExpression().isEmpty() )
273 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION )
274 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION );
276 else if( ! isValid_DataType() )
278 sReason = explainInvalid_DataType();
279 if( sReason.isEmpty() )
281 // no explanation given by data type? Then give generic message
282 sReason = getResource( RID_STR_XFORMS_INVALID_VALUE,
283 maMIP.getTypeName() );
286 else if( ! maMIP.isConstraint() )
288 sReason = maMIP.getConstraintExplanation();
290 else if( maMIP.isRequired() && maBindingExpression.hasValue() &&
291 maBindingExpression.getString().isEmpty() )
293 sReason = getResource( RID_STR_XFORMS_REQUIRED );
295 // else: no explanation given; should only happen if data is valid
297 OSL_ENSURE( sReason.isEmpty() == isValid(),
298 "invalid data should have an explanation!" );
300 return sReason;
305 EvaluationContext Binding::getEvaluationContext() const
307 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
308 EvaluationContext aContext = getModelImpl()->getEvaluationContext();
309 aContext.mxNamespaces = getBindingNamespaces();
310 return aContext;
313 ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
315 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
317 // bind (in case we were not bound before)
318 bind( sal_False );
319 return _getMIPEvaluationContexts();
323 Binding::IntSequence_t Binding::getUnoTunnelID()
325 static cppu::OImplementationId aImplementationId;
326 return aImplementationId.getImplementationId();
329 Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet )
331 Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY );
332 return xTunnel.is()
333 ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID()))
334 : NULL;
340 OUString Binding::getBindingID() const
342 return msBindingID;
345 void Binding::setBindingID( const OUString& sBindingID )
347 msBindingID = sBindingID;
350 OUString Binding::getBindingExpression() const
352 return maBindingExpression.getExpression();
355 void Binding::setBindingExpression( const OUString& sBindingExpression)
357 maBindingExpression.setExpression( sBindingExpression );
358 bindingModified();
361 OUString Binding::getReadonlyExpression() const
363 return maReadonly.getExpression();
366 void Binding::setReadonlyExpression( const OUString& sReadonly)
368 maReadonly.setExpression( sReadonly );
369 bindingModified();
372 OUString Binding::getRelevantExpression() const
374 return maRelevant.getExpression();
377 void Binding::setRelevantExpression( const OUString& sRelevant )
379 maRelevant.setExpression( sRelevant );
380 bindingModified();
383 OUString Binding::getRequiredExpression() const
385 return maRequired.getExpression();
388 void Binding::setRequiredExpression( const OUString& sRequired )
390 maRequired.setExpression( sRequired );
391 bindingModified();
394 OUString Binding::getConstraintExpression() const
396 return maConstraint.getExpression();
399 void Binding::setConstraintExpression( const OUString& sConstraint )
401 maConstraint.setExpression( sConstraint );
402 msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT,
403 sConstraint );
405 // TODO: This should only re-evaluate the constraint, and notify
406 // the validity constraint listeners; instead we currently pretend
407 // the entire binding was notified, which does a little too much.
408 bindingModified();
411 OUString Binding::getCalculateExpression() const
413 return maCalculate.getExpression();
416 void Binding::setCalculateExpression( const OUString& sCalculate )
418 maCalculate.setExpression( sCalculate );
419 bindingModified();
422 OUString Binding::getType() const
424 return msTypeName;
427 void Binding::setType( const OUString& sTypeName )
429 msTypeName = sTypeName;
430 bindingModified();
433 Binding::XNameContainer_t Binding::getBindingNamespaces() const
435 // return _getNamespaces();
436 return mxNamespaces;
439 void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces )
441 _setNamespaces( rNamespaces, true );
444 Binding::XNameContainer_t Binding::getModelNamespaces() const
446 return _getNamespaces();
449 void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces )
451 _setNamespaces( rNamespaces, false );
454 bool Binding::getReadOnly() const
456 return maMIP.isReadonly();
459 bool Binding::getRelevant() const
461 return maMIP.isRelevant();
464 bool Binding::getExternalData() const
466 bool bExternalData = true;
467 if ( !mxModel.is() )
468 return bExternalData;
472 Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW );
473 OSL_VERIFY(
474 xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
476 catch( const Exception& )
478 DBG_UNHANDLED_EXCEPTION();
480 return bExternalData;
484 void Binding::checkLive()
485 throw( RuntimeException )
487 if( ! isLive() )
488 throw RuntimeException( EXCEPT("Binding not initialized") );
491 void Binding::checkModel()
492 throw( RuntimeException )
494 if( ! mxModel.is() )
495 throw RuntimeException( EXCEPT("Binding has no Model") );
498 bool Binding::isLive() const
500 const Model* pModel = getModelImpl();
501 return ( pModel != NULL ) ? pModel->isInitialized() : false;
504 Model* Binding::getModelImpl() const
506 return getModelImpl( mxModel );
509 Model* Binding::getModelImpl( const Model_t& xModel ) const
511 Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY );
512 Model* pModel = xTunnel.is()
513 ? reinterpret_cast<Model*>(
514 xTunnel->getSomething( Model::getUnoTunnelID() ) )
515 : NULL;
516 return pModel;
519 static void lcl_addListenerToNode( Reference<XNode> xNode,
520 Reference<XEventListener> xListener )
522 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
523 if( xTarget.is() )
525 xTarget->addEventListener( "DOMCharacterDataModified",
526 xListener, false );
527 xTarget->addEventListener( "DOMCharacterDataModified",
528 xListener, true );
529 xTarget->addEventListener( "DOMAttrModified",
530 xListener, false );
531 xTarget->addEventListener( "DOMAttrModified",
532 xListener, true );
533 xTarget->addEventListener( "DOMAttrModified",
534 xListener, true );
535 xTarget->addEventListener( "xforms-generic",
536 xListener, true );
540 static void lcl_removeListenerFromNode( Reference<XNode> xNode,
541 Reference<XEventListener> xListener )
543 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
544 if( xTarget.is() )
546 xTarget->removeEventListener( "DOMCharacterDataModified",
547 xListener, false );
548 xTarget->removeEventListener( "DOMCharacterDataModified",
549 xListener, true );
550 xTarget->removeEventListener( "DOMAttrModified",
551 xListener, false );
552 xTarget->removeEventListener( "DOMAttrModified",
553 xListener, true );
554 xTarget->removeEventListener( "xforms-generic",
555 xListener, true );
559 ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const
561 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
563 // iterate over nodes of bind expression and create
564 // EvaluationContext for each
565 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
566 ::std::vector<EvaluationContext> aVector;
567 sal_Int32 nCount = 0; // count nodes for context position
568 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
569 aIter != aNodes.end();
570 ++aIter, ++nCount )
572 OSL_ENSURE( aIter->is(), "no node?" );
574 // create proper evaluation context for this MIP
575 aVector.push_back( EvaluationContext( *aIter, getModel(),
576 getBindingNamespaces(),
577 nCount, aNodes.size() ) );
579 return aVector;
582 void Binding::bind( bool bForceRebind )
584 checkModel();
586 // bind() will evaluate this binding as follows:
587 // 1) evaluate the binding expression
588 // 1b) if necessary, create node according to 'lazy author' rules
589 // 2) register suitable listeners on the instance (and remove old ones)
590 // 3) remove old MIPs defined by this binding
591 // 4) for every node in the binding nodeset do:
592 // 1) create proper evaluation context for this MIP
593 // 2) evaluate calculate expression (and push value into instance)
594 // 3) evaluate remaining MIPs
595 // 4) evaluate the locally defined MIPs, and push them to the model
598 // 1) evaluate the binding expression
599 EvaluationContext aContext = getEvaluationContext();
600 maBindingExpression.evaluate( aContext );
601 if( ! maBindingExpression.getNode().is() )
603 // 1b) create node (if valid element name)
604 if( isValidQName( maBindingExpression.getExpression(),
605 aContext.mxNamespaces ) )
607 aContext.mxContextNode->appendChild(
608 Reference<XNode>(
609 aContext.mxContextNode->getOwnerDocument()->createElement(
610 maBindingExpression.getExpression() ),
611 UNO_QUERY ) );
612 maBindingExpression.evaluate( aContext );
613 OSL_ENSURE( maBindingExpression.getNode().is(),
614 "we should bind to the newly inserted node!" );
617 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
619 // 2) register suitable listeners on the instance (and remove old ones)
620 if( maEventNodes.empty() || bForceRebind )
622 for( XNodes_t::iterator aIter = maEventNodes.begin();
623 aIter != maEventNodes.end();
624 ++aIter )
625 lcl_removeListenerFromNode( *aIter, this );
626 maEventNodes.clear();
627 if( isSimpleBinding() )
628 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
629 aIter != aNodes.end();
630 ++aIter )
631 maEventNodes.push_back( *aIter );
632 else
633 maEventNodes.push_back(
634 Reference<XNode>( aContext.mxContextNode->getOwnerDocument(),
635 UNO_QUERY_THROW ) );
636 for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin();
637 aIter2 != maEventNodes.end();
638 ++aIter2 )
639 lcl_addListenerToNode( *aIter2, this );
642 // 3) remove old MIPs defined by this binding
643 Model* pModel = getModelImpl();
644 OSL_ENSURE( pModel != NULL, "need model" );
645 pModel->removeMIPs( this );
647 // 4) calculate all MIPs
648 ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts();
649 for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin();
650 aIter != aMIPContexts.end();
651 ++aIter )
653 EvaluationContext& rContext = *aIter;
655 // evaluate calculate expression (and push value into instance)
656 // (prevent recursion using mbInCalculate
657 if( ! maCalculate.isEmptyExpression() )
659 if( ! mbInCalculate )
661 mbInCalculate = true;
662 maCalculate.evaluate( rContext );
663 pModel->setSimpleContent( rContext.mxContextNode,
664 maCalculate.getString() );
665 mbInCalculate = false;
669 // now evaluate remaining MIPs in the apropriate context
670 maReadonly.evaluate( rContext );
671 maRelevant.evaluate( rContext );
672 maRequired.evaluate( rContext );
673 maConstraint.evaluate( rContext );
674 // type is static; does not need updating
676 // evaluate the locally defined MIPs, and push them to the model
677 pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() );
682 // helper for Binding::valueModified
683 static void lcl_modified( const Binding::XModifyListener_t xListener,
684 const Reference<XInterface> xSource )
686 OSL_ENSURE( xListener.is(), "no listener?" );
687 xListener->modified( EventObject( xSource ) );
690 // helper for Binding::valueModified
691 static void lcl_listentry( const Binding::XListEntryListener_t xListener,
692 const Reference<XInterface> xSource )
694 OSL_ENSURE( xListener.is(), "no listener?" );
695 // TODO: send fine granular events
696 xListener->allEntriesChanged( EventObject( xSource ) );
699 // helper for Binding::valueModified
700 static void lcl_validate( const Binding::XValidityConstraintListener_t xListener,
701 const Reference<XInterface> xSource )
703 OSL_ENSURE( xListener.is(), "no listener?" );
704 xListener->validityConstraintChanged( EventObject( xSource ) );
708 void Binding::valueModified()
710 // defer notifications, if so desired
711 if( mnDeferModifyNotifications > 0 )
713 mbValueModified = true;
714 return;
716 mbValueModified = false;
718 // query MIP used by our first node (also note validity)
719 Reference<XNode> xNode = maBindingExpression.getNode();
720 maMIP = getModelImpl()->queryMIP( xNode );
722 // distribute MIPs _used_ by this binding
723 if( xNode.is() )
725 notifyAndCachePropertyValue( HANDLE_ReadOnly );
726 notifyAndCachePropertyValue( HANDLE_Relevant );
729 // iterate over _value_ listeners and send each a modified signal,
730 // using this object as source (will also update validity, because
731 // control will query once the value has changed)
732 Reference<XInterface> xSource = static_cast<XPropertySet*>( this );
733 ::std::for_each( maModifyListeners.begin(),
734 maModifyListeners.end(),
735 ::std::bind2nd( ::std::ptr_fun( lcl_modified ), xSource ) );
736 ::std::for_each( maListEntryListeners.begin(),
737 maListEntryListeners.end(),
738 ::std::bind2nd( ::std::ptr_fun( lcl_listentry ), xSource ) );
739 ::std::for_each( maValidityListeners.begin(),
740 maValidityListeners.end(),
741 ::std::bind2nd( ::std::ptr_fun( lcl_validate ), xSource ) );
743 // now distribute MIPs to children
744 if( xNode.is() )
745 distributeMIP( xNode->getFirstChild() );
748 void Binding::distributeMIP( const XNode_t & rxNode ) {
750 typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t;
751 OUString sEventName("xforms-generic");
752 XFormsEvent_t *pEvent = new XFormsEvent_t;
753 pEvent->initXFormsEvent(sEventName, sal_True, sal_False);
754 Reference<XEvent> xEvent(pEvent);
756 // naive depth-first traversal
757 XNode_t xNode( rxNode );
758 while(xNode.is()) {
760 // notifications should be triggered at the
761 // leaf nodes first, bubbling upwards the hierarchy.
762 XNode_t child(xNode->getFirstChild());
763 if(child.is())
764 distributeMIP(child);
766 // we're standing at a particular node somewhere
767 // below the one which changed a property (MIP).
768 // bindings which are listening at this node will receive
769 // a notification message about what exactly happened.
770 Reference< XEventTarget > target(xNode,UNO_QUERY);
771 target->dispatchEvent(xEvent);
773 xNode = xNode->getNextSibling();
777 void Binding::bindingModified()
779 // defer notifications, if so desired
780 if( mnDeferModifyNotifications > 0 )
782 mbBindingModified = true;
783 return;
785 mbBindingModified = false;
787 // rebind (if live); then call valueModified
788 // A binding should be inert until its model is fully constructed.
789 if( isLive() )
791 bind( true );
792 valueModified();
797 MIP Binding::getLocalMIP() const
799 MIP aMIP;
801 if( maReadonly.hasValue() )
802 aMIP.setReadonly( maReadonly.getBool( false ) );
803 if( maRelevant.hasValue() )
804 aMIP.setRelevant( maRelevant.getBool( true ) );
805 if( maRequired.hasValue() )
806 aMIP.setRequired( maRequired.getBool( false ) );
807 if( maConstraint.hasValue() )
809 aMIP.setConstraint( maConstraint.getBool( true ) );
810 if( ! aMIP.isConstraint() )
811 aMIP.setConstraintExplanation( msExplainConstraint );
813 if( !msTypeName.isEmpty() )
814 aMIP.setTypeName( msTypeName );
816 // calculate: only handle presence of calculate; value set elsewhere
817 aMIP.setHasCalculate( !maCalculate.isEmptyExpression() );
819 return aMIP;
822 Binding::XDataType_t Binding::getDataType()
824 OSL_ENSURE( getModel().is(), "need model" );
825 OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" );
827 Reference<XDataTypeRepository> xRepository(
828 getModel()->getDataTypeRepository(), UNO_QUERY );
829 OUString sTypeName = maMIP.getTypeName();
831 return ( xRepository.is() && xRepository->hasByName( sTypeName ) )
832 ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY)
833 : Reference<XDataType>( NULL );
836 bool Binding::isValid_DataType()
838 Reference<XDataType> xDataType = getDataType();
839 return xDataType.is()
840 ? xDataType->validate( maBindingExpression.getString() )
841 : true;
844 OUString Binding::explainInvalid_DataType()
846 Reference<XDataType> xDataType = getDataType();
847 return xDataType.is()
848 ? xDataType->explainInvalid( maBindingExpression.getString() )
849 : OUString();
852 void Binding::clear()
854 // remove MIPs contributed by this binding
855 Model* pModel = getModelImpl();
856 if( pModel != NULL )
857 pModel->removeMIPs( this );
859 // remove all references
860 for( XNodes_t::iterator aIter = maEventNodes.begin();
861 aIter != maEventNodes.end();
862 ++aIter )
863 lcl_removeListenerFromNode( *aIter, this );
864 maEventNodes.clear();
866 // clear expressions
867 maBindingExpression.clear();
868 maReadonly.clear();
869 maRelevant.clear();
870 maRequired.clear();
871 maConstraint.clear();
872 maCalculate.clear();
874 // TODO: what about our listeners?
878 static void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom,
879 Binding::XNameContainer_t& xTo )
881 OSL_ENSURE( xFrom.is(), "no source" );
882 OSL_ENSURE( xTo.is(), "no target" );
884 // iterate over name in source
885 Sequence<OUString> aNames = xTo->getElementNames();
886 sal_Int32 nNames = aNames.getLength();
887 const OUString* pNames = aNames.getConstArray();
888 for( sal_Int32 i = 0; i < nNames; i++ )
890 const OUString& rName = pNames[i];
892 if( ! xFrom->hasByName( rName ) )
893 xTo->removeByName( rName );
897 /** copy namespaces from one namespace container into another
898 * @param bOverwrite true: overwrite namespaces in target
899 * false: do not overwrite namespaces in target
900 * @param bMove true: move namespaces (i.e., delete in source)
901 * false: copy namespaces (do not modify source)
902 * @param bFromSource true: use elements from source
903 * false: use only elements from target
905 static void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom,
906 Binding::XNameContainer_t& xTo,
907 bool bOverwrite )
909 OSL_ENSURE( xFrom.is(), "no source" );
910 OSL_ENSURE( xTo.is(), "no target" );
912 // iterate over name in source
913 Sequence<OUString> aNames = xFrom->getElementNames();
914 sal_Int32 nNames = aNames.getLength();
915 const OUString* pNames = aNames.getConstArray();
916 for( sal_Int32 i = 0; i < nNames; i++ )
918 const OUString& rName = pNames[i];
920 // determine whether to copy the value, and whether to delete
921 // it in the source:
923 bool bInTarget = xTo->hasByName( rName );
925 // we copy: if property is in target, and
926 // if bOverwrite is set, or when the namespace prefix is free
927 bool bCopy = bOverwrite || ! bInTarget;
929 // and now... ACTION!
930 if( bCopy )
932 if( bInTarget )
933 xTo->replaceByName( rName, xFrom->getByName( rName ) );
934 else
935 xTo->insertByName( rName, xFrom->getByName( rName ) );
940 // implement get*Namespaces()
941 // (identical for both variants)
942 Binding::XNameContainer_t Binding::_getNamespaces() const
944 XNameContainer_t xNamespaces = new NameContainer<OUString>();
945 lcl_copyNamespaces( mxNamespaces, xNamespaces, true );
947 // merge model's with binding's own namespaces
948 Model* pModel = getModelImpl();
949 if( pModel != NULL )
950 lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false );
952 return xNamespaces;
955 // implement set*Namespaces()
956 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
957 void Binding::_setNamespaces( const XNameContainer_t& rNamespaces,
958 bool bBinding )
960 Model* pModel = getModelImpl();
961 XNameContainer_t xModelNamespaces = ( pModel != NULL )
962 ? pModel->getNamespaces()
963 : NULL;
964 OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?");
966 // remove deleted namespaces
967 lcl_removeOtherNamespaces( rNamespaces, mxNamespaces );
968 if( !bBinding && xModelNamespaces.is() )
969 lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces );
971 // copy namespaces as appropriate
972 Sequence<OUString> aNames = rNamespaces->getElementNames();
973 sal_Int32 nNames = aNames.getLength();
974 const OUString* pNames = aNames.getConstArray();
975 for( sal_Int32 i = 0; i < nNames; i++ )
977 const OUString& rName = pNames[i];
978 Any aValue = rNamespaces->getByName( rName );
980 // determine whether the namespace should go into model's or
981 // into binding's namespaces
982 bool bLocal =
983 ! xModelNamespaces.is()
984 || mxNamespaces->hasByName( rName )
985 || ( bBinding
986 && xModelNamespaces.is()
987 && xModelNamespaces->hasByName( rName ) );
989 // write namespace into the appropriate namespace container
990 XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces;
991 OSL_ENSURE( rWhich.is(), "whoops" );
992 if( rWhich->hasByName( rName ) )
993 rWhich->replaceByName( rName, aValue );
994 else
995 rWhich->insertByName( rName, aValue );
997 // always 'promote' namespaces from binding to model, if equal
998 if( xModelNamespaces.is()
999 && xModelNamespaces->hasByName( rName )
1000 && mxNamespaces->hasByName( rName )
1001 && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) )
1003 mxNamespaces->removeByName( rName );
1007 // ... done. But we modified the binding!
1008 bindingModified();
1011 void Binding::_checkBindingID()
1013 if( getModel().is() )
1015 Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW );
1016 if( msBindingID.isEmpty() )
1018 // no binding ID? then make one up!
1019 OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ) + " ";
1020 sal_Int32 nNumber = 0;
1021 OUString sName;
1024 nNumber++;
1025 sName = sIDPrefix + OUString::valueOf( nNumber );
1027 while( xBindings->hasByName( sName ) );
1028 setBindingID( sName );
1037 // XValueBinding
1040 Binding::Sequence_Type_t Binding::getSupportedValueTypes()
1041 throw( RuntimeException )
1043 return Convert::get().getTypes();
1046 sal_Bool Binding::supportsType( const Type_t& rType )
1047 throw( RuntimeException )
1049 return Convert::get().hasType( rType );
1052 Binding::Any_t Binding::getValue( const Type_t& rType )
1053 throw( IncompatibleTypesException,
1054 RuntimeException )
1056 // first, check for model
1057 checkLive();
1059 // second, check for type
1060 if( ! supportsType( rType ) )
1061 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1063 // return string value (if present; else return empty Any)
1064 Binding::Any_t result = Any();
1065 if(maBindingExpression.hasValue()) {
1066 OUString pathExpr(maBindingExpression.getString());
1067 Convert &rConvert = Convert::get();
1068 result = rConvert.toAny(pathExpr,rType);
1071 return result;
1074 void Binding::setValue( const Any_t& aValue )
1075 throw( IncompatibleTypesException,
1076 InvalidBindingStateException,
1077 NoSupportException,
1078 RuntimeException )
1080 // first, check for model
1081 checkLive();
1083 // check for supported type
1084 if( ! supportsType( aValue.getValueType() ) )
1085 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1087 if( maBindingExpression.hasValue() )
1089 Binding::XNode_t xNode = maBindingExpression.getNode();
1090 if( xNode.is() )
1092 OUString sValue = Convert::get().toXSD( aValue );
1093 bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue );
1094 if( ! bSuccess )
1095 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1097 else
1098 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1100 else
1101 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1106 // XListEntry Source
1109 sal_Int32 Binding::getListEntryCount()
1110 throw( RuntimeException )
1112 // first, check for model
1113 checkLive();
1115 // return size of node list
1116 return maBindingExpression.getNodeList().size();
1119 static void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer )
1121 if( xNode->getNodeType() == NodeType_TEXT_NODE
1122 || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
1124 rBuffer.append( xNode->getNodeValue() );
1126 else
1128 for( Reference<XNode> xChild = xNode->getFirstChild();
1129 xChild.is();
1130 xChild = xChild->getNextSibling() )
1132 lcl_getString( xChild, rBuffer );
1137 static OUString lcl_getString( const Reference<XNode>& xNode )
1139 OUStringBuffer aBuffer;
1140 lcl_getString( xNode, aBuffer );
1141 return aBuffer.makeStringAndClear();
1144 OUString Binding::getListEntry( sal_Int32 nPosition )
1145 throw( IndexOutOfBoundsException,
1146 RuntimeException )
1148 // first, check for model
1149 checkLive();
1151 // check bounds and return proper item
1152 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1153 if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) )
1154 throw IndexOutOfBoundsException( EXCEPT("") );
1155 return lcl_getString( aNodes[ nPosition ] );
1158 Sequence<OUString> Binding::getAllListEntries()
1159 throw( RuntimeException )
1161 // first, check for model
1162 checkLive();
1164 // create sequence of string values
1165 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1166 Sequence<OUString> aSequence( aNodes.size() );
1167 OUString* pSequence = aSequence.getArray();
1168 for( sal_Int32 n = 0; n < aSequence.getLength(); n++ )
1170 pSequence[n] = lcl_getString( aNodes[n] );
1173 return aSequence;
1176 void Binding::addListEntryListener( const XListEntryListener_t& xListener )
1177 throw( NullPointerException,
1178 RuntimeException )
1180 OSL_ENSURE( xListener.is(), "need listener!" );
1181 if( ::std::find( maListEntryListeners.begin(),
1182 maListEntryListeners.end(),
1183 xListener)
1184 == maListEntryListeners.end() )
1185 maListEntryListeners.push_back( xListener );
1188 void Binding::removeListEntryListener( const XListEntryListener_t& xListener )
1189 throw( NullPointerException,
1190 RuntimeException )
1192 XListEntryListeners_t::iterator aIter =
1193 ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(),
1194 xListener );
1195 if( aIter != maListEntryListeners.end() )
1196 maListEntryListeners.erase( aIter );
1201 // XValidator
1204 sal_Bool Binding::isValid( const Any_t& )
1205 throw( RuntimeException )
1207 // first, check for model
1208 checkLive();
1210 // ignore value; determine validate only on current data
1211 return isValid();
1214 OUString Binding::explainInvalid(
1215 const Any_t& /*Value*/ )
1216 throw( RuntimeException )
1218 // first, check for model
1219 checkLive();
1221 // ignore value; determine explanation only on current data
1222 return explainInvalid();
1225 void Binding::addValidityConstraintListener(
1226 const XValidityConstraintListener_t& xListener )
1227 throw( NullPointerException,
1228 RuntimeException )
1230 OSL_ENSURE( xListener.is(), "need listener!" );
1231 if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener)
1232 == maValidityListeners.end() )
1233 maValidityListeners.push_back( xListener );
1236 void Binding::removeValidityConstraintListener(
1237 const XValidityConstraintListener_t& xListener )
1238 throw( NullPointerException,
1239 RuntimeException )
1241 XValidityConstraintListeners_t::iterator aIter =
1242 ::std::find( maValidityListeners.begin(), maValidityListeners.end(),
1243 xListener );
1244 if( aIter != maValidityListeners.end() )
1245 maValidityListeners.erase( aIter );
1251 // xml::dom::event::XEventListener
1254 void Binding::handleEvent( const XEvent_t& xEvent )
1255 throw( RuntimeException )
1257 OUString sType(xEvent->getType());
1258 //OUString sEventMIPChanged("xforms-generic");
1259 //if(sType.equals(sEventMIPChanged)) {
1260 if(!sType.compareToAscii("xforms-generic")) {
1262 // the modification of the 'mnDeferModifyNotifications'-member
1263 // is necessary to prevent infinite notication looping.
1264 // This can happened in case the binding which caused
1265 // the notification chain is listening to those events
1266 // as well...
1267 bool bPreserveValueModified = mbValueModified;
1268 mnDeferModifyNotifications++;
1269 valueModified();
1270 --mnDeferModifyNotifications;
1271 mbValueModified = bPreserveValueModified;
1272 return;
1275 // if we're a dynamic binding, we better re-bind, too!
1276 bind( false );
1278 // our value was maybe modified
1279 valueModified();
1284 // lang::XUnoTunnel
1287 sal_Int64 Binding::getSomething( const IntSequence_t& xId )
1288 throw( RuntimeException )
1290 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
1294 // XCloneable
1297 Binding::XCloneable_t SAL_CALL Binding::createClone()
1298 throw( RuntimeException )
1300 Reference< XPropertySet > xClone;
1302 Model* pModel = getModelImpl();
1303 if ( pModel )
1304 xClone = pModel->cloneBinding( this );
1305 else
1307 xClone = new Binding;
1308 copy( this, xClone );
1310 return XCloneable_t( xClone, UNO_QUERY );
1314 // property set implementations
1317 #define REGISTER_PROPERTY( property, type ) \
1318 registerProperty( PROPERTY( property, type ), \
1319 new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) );
1321 #define REGISTER_PROPERTY_RO( property, type ) \
1322 registerProperty( PROPERTY_RO( property, type ), \
1323 new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) );
1325 #define REGISTER_BOOL_PROPERTY_RO( property ) \
1326 registerProperty( PROPERTY_RO( property, sal_Bool ), \
1327 new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) );
1329 void Binding::initializePropertySet()
1331 REGISTER_PROPERTY ( BindingID, OUString );
1332 REGISTER_PROPERTY ( BindingExpression, OUString );
1333 REGISTER_PROPERTY_RO ( Model, Model_t );
1334 REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t );
1335 REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t );
1336 REGISTER_PROPERTY_RO ( ModelID, OUString );
1337 REGISTER_PROPERTY ( ReadonlyExpression, OUString );
1338 REGISTER_PROPERTY ( RelevantExpression, OUString );
1339 REGISTER_PROPERTY ( RequiredExpression, OUString );
1340 REGISTER_PROPERTY ( ConstraintExpression, OUString );
1341 REGISTER_PROPERTY ( CalculateExpression, OUString );
1342 REGISTER_PROPERTY ( Type, OUString );
1343 REGISTER_PROPERTY_RO ( ReadOnly, bool );
1344 REGISTER_PROPERTY_RO ( Relevant, bool );
1345 REGISTER_BOOL_PROPERTY_RO( ExternalData );
1347 initializePropertyValueCache( HANDLE_ReadOnly );
1348 initializePropertyValueCache( HANDLE_Relevant );
1349 initializePropertyValueCache( HANDLE_ExternalData );
1352 void Binding::addModifyListener(
1353 const XModifyListener_t& xListener )
1354 throw( RuntimeException )
1356 OSL_ENSURE( xListener.is(), "need listener!" );
1357 if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener )
1358 == maModifyListeners.end() )
1359 maModifyListeners.push_back( xListener );
1361 // HACK: currently, we have to 'push' some MIPs to the control
1362 // (read-only, relevant, etc.) To enable this, we need to update
1363 // the control at least once when it registers here.
1364 valueModified();
1367 void Binding::removeModifyListener(
1368 const XModifyListener_t& xListener )
1369 throw( RuntimeException )
1371 ModifyListeners_t::iterator aIter =
1372 ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener );
1373 if( aIter != maModifyListeners.end() )
1374 maModifyListeners.erase( aIter );
1380 OUString Binding::getName()
1381 throw( RuntimeException )
1383 return getBindingID();
1386 void SAL_CALL Binding::setName( const OUString& rName )
1387 throw( RuntimeException )
1389 // use the XPropertySet methods, so the change in the name is notified to the
1390 // property listeners
1391 setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) );
1394 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */