update dev300-m58
[ooovba.git] / forms / source / xforms / binding.cxx
blob6b8da64b88aa0e71ae7e72cdbf9950a85caf22a2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: binding.cxx,v $
10 * $Revision: 1.11 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_forms.hxx"
34 #include "binding.hxx"
36 #include "model.hxx"
37 #include "unohelper.hxx"
38 #include "NameContainer.hxx"
39 #include "evaluationcontext.hxx"
40 #include "convert.hxx"
41 #include "resourcehelper.hxx"
42 #include "xmlhelper.hxx"
43 #include "xformsevent.hxx"
45 #include <rtl/ustrbuf.hxx>
46 #include <osl/diagnose.h>
48 #include <tools/diagnose_ex.h>
50 #include <algorithm>
51 #include <functional>
53 #include <com/sun/star/uno/Any.hxx>
54 #include <com/sun/star/xml/dom/XNodeList.hpp>
55 #include <com/sun/star/xml/dom/XNode.hpp>
56 #include <com/sun/star/xml/dom/XDocument.hpp>
57 #include <com/sun/star/xml/dom/XElement.hpp>
58 #include <com/sun/star/xml/dom/NodeType.hpp>
59 #include <com/sun/star/xml/dom/events/XEventTarget.hpp>
60 #include <com/sun/star/xml/dom/events/XEventListener.hpp>
61 #include <com/sun/star/xml/dom/events/XDocumentEvent.hpp>
62 #include <com/sun/star/lang/XUnoTunnel.hpp>
63 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
64 #include <com/sun/star/container/XSet.hpp>
65 #include <com/sun/star/container/XNameContainer.hpp>
67 #include <comphelper/propertysetinfo.hxx>
68 #include <unotools/textsearch.hxx>
69 #include <cppuhelper/typeprovider.hxx>
71 using namespace com::sun::star::xml::xpath;
72 using namespace com::sun::star::xml::dom::events;
74 using rtl::OUString;
75 using rtl::OUStringBuffer;
76 using std::vector;
77 using xforms::Binding;
78 using xforms::MIP;
79 using xforms::Model;
80 using xforms::getResource;
81 using xforms::EvaluationContext;
82 using com::sun::star::beans::PropertyVetoException;
83 using com::sun::star::beans::UnknownPropertyException;
84 using com::sun::star::beans::XPropertySet;
85 using com::sun::star::container::XSet;
86 using com::sun::star::container::XNameAccess;
87 using com::sun::star::form::binding::IncompatibleTypesException;
88 using com::sun::star::form::binding::InvalidBindingStateException;
89 using com::sun::star::form::binding::XValueBinding;
90 using com::sun::star::lang::EventObject;
91 using com::sun::star::lang::IllegalArgumentException;
92 using com::sun::star::lang::IndexOutOfBoundsException;
93 using com::sun::star::lang::NoSupportException;
94 using com::sun::star::lang::NullPointerException;
95 using com::sun::star::lang::WrappedTargetException;
96 using com::sun::star::lang::XUnoTunnel;
97 using com::sun::star::uno::Any;
98 using com::sun::star::uno::Reference;
99 using com::sun::star::uno::RuntimeException;
100 using com::sun::star::uno::Sequence;
101 using com::sun::star::uno::UNO_QUERY;
102 using com::sun::star::uno::UNO_QUERY_THROW;
103 using com::sun::star::uno::XInterface;
104 using com::sun::star::uno::Exception;
105 using com::sun::star::uno::makeAny;
106 using com::sun::star::util::XModifyListener;
107 using com::sun::star::xforms::XDataTypeRepository;
108 using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE;
109 using com::sun::star::xml::dom::NodeType_TEXT_NODE;
110 using com::sun::star::xml::dom::XNode;
111 using com::sun::star::xml::dom::XNodeList;
112 using com::sun::star::xml::dom::events::XEventListener;
113 using com::sun::star::xml::dom::events::XEventTarget;
114 using com::sun::star::xsd::XDataType;
119 #define EXCEPT(msg) OUSTRING(msg),static_cast<XValueBinding*>(this)
121 #define HANDLE_BindingID 0
122 #define HANDLE_BindingExpression 1
123 #define HANDLE_Model 2
124 #define HANDLE_ModelID 3
125 #define HANDLE_BindingNamespaces 4
126 #define HANDLE_ReadonlyExpression 5
127 #define HANDLE_RelevantExpression 6
128 #define HANDLE_RequiredExpression 7
129 #define HANDLE_ConstraintExpression 8
130 #define HANDLE_CalculateExpression 9
131 #define HANDLE_Type 10
132 #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
133 #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
134 #define HANDLE_ModelNamespaces 13
135 #define HANDLE_ExternalData 14
138 Binding::Binding() :
139 mxModel(),
140 msBindingID(),
141 maBindingExpression(),
142 maReadonly(),
143 mxNamespaces( new NameContainer<OUString>() ),
144 mbInCalculate( false ),
145 mnDeferModifyNotifications( 0 ),
146 mbValueModified( false ),
147 mbBindingModified( false )
150 initializePropertySet();
153 Binding::~Binding() throw()
155 _setModel(NULL);
159 Binding::Model_t Binding::getModel() const
161 return mxModel;
164 void Binding::_setModel( const Model_t& xModel )
166 PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model );
167 PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID );
169 // prepare binding for removal of old model
170 clear(); // remove all cached data (e.g. XPath evaluation results)
171 XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces
173 mxModel = xModel;
175 // set namespaces (and move to model, if appropriate)
176 setBindingNamespaces( xNamespaces );
177 _checkBindingID();
179 notifyAndCachePropertyValue( HANDLE_ExternalData );
183 OUString Binding::getModelID() const
185 Model* pModel = getModelImpl();
186 return ( pModel == NULL ) ? OUString() : pModel->getID();
190 Binding::XNodeList_t Binding::getXNodeList()
192 // first make sure we are bound
193 if( ! maBindingExpression.hasValue() )
194 bind( sal_False );
196 return maBindingExpression.getXNodeList();
199 bool Binding::isSimpleBinding() const
201 return maBindingExpression.isSimpleExpression()
202 && maReadonly.isSimpleExpression()
203 && maRelevant.isSimpleExpression()
204 && maRequired.isSimpleExpression()
205 && maConstraint.isSimpleExpression()
206 && maCalculate.isSimpleExpression();
209 bool Binding::isSimpleBindingExpression() const
211 return maBindingExpression.isSimpleExpression();
214 void Binding::update()
216 // clear all expressions (to remove cached node references)
217 maBindingExpression.clear();
218 maReadonly.clear();
219 maRelevant.clear();
220 maRequired.clear();
221 maConstraint.clear();
222 maCalculate.clear();
224 // let's just pretend the binding has been modified -> full rebind()
225 bindingModified();
228 void Binding::deferNotifications( bool bDefer )
230 mnDeferModifyNotifications += ( bDefer ? 1 : -1 );
231 OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" );
233 if( mnDeferModifyNotifications == 0 )
235 if( mbBindingModified )
236 bindingModified();
237 if( mbValueModified )
238 valueModified();
241 OSL_ENSURE( ( mnDeferModifyNotifications > 0 )
242 || ( ! mbBindingModified && ! mbValueModified ),
243 "deferred modifications not delivered?" );
246 bool Binding::isValid()
248 // TODO: determine whether node is suitable, not just whether it exists
249 return maBindingExpression.getNode().is() &&
250 isValid_DataType() &&
251 maMIP.isConstraint() &&
252 ( ! maMIP.isRequired() ||
253 ( maBindingExpression.hasValue() &&
254 maBindingExpression.getString().getLength() > 0 ) );
257 bool Binding::isUseful()
259 // we are useful, if
260 // 0) we don't have a model
261 // (at least, in this case we shouldn't be removed from the model)
262 // 1) we have a proper name
263 // 2) we have some MIPs,
264 // 3) we are bound to some control
265 // (this can be assumed if some listeners are set)
266 bool bUseful =
267 getModelImpl() == NULL
268 // || msBindingID.getLength() > 0
269 || msTypeName.getLength() > 0
270 || ! maReadonly.isEmptyExpression()
271 || ! maRelevant.isEmptyExpression()
272 || ! maRequired.isEmptyExpression()
273 || ! maConstraint.isEmptyExpression()
274 || ! maCalculate.isEmptyExpression()
275 || ! maModifyListeners.empty()
276 || ! maListEntryListeners.empty()
277 || ! maValidityListeners.empty();
279 return bUseful;
282 OUString Binding::explainInvalid()
284 OUString sReason;
285 if( ! maBindingExpression.getNode().is() )
287 sReason = ( maBindingExpression.getExpression().getLength() == 0 )
288 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION )
289 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION );
291 else if( ! isValid_DataType() )
293 sReason = explainInvalid_DataType();
294 if( sReason.getLength() == 0 )
296 // no explanation given by data type? Then give generic message
297 sReason = getResource( RID_STR_XFORMS_INVALID_VALUE,
298 maMIP.getTypeName() );
301 else if( ! maMIP.isConstraint() )
303 sReason = maMIP.getConstraintExplanation();
305 else if( maMIP.isRequired() && maBindingExpression.hasValue() &&
306 ( maBindingExpression.getString().getLength() == 0 ) )
308 sReason = getResource( RID_STR_XFORMS_REQUIRED );
310 // else: no explanation given; should only happen if data is valid
312 OSL_ENSURE( ( sReason.getLength() == 0 ) == isValid(),
313 "invalid data should have an explanation!" );
315 return sReason;
320 EvaluationContext Binding::getEvaluationContext() const
322 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
323 EvaluationContext aContext = getModelImpl()->getEvaluationContext();
324 aContext.mxNamespaces = getBindingNamespaces();
325 return aContext;
328 ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
330 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
332 // bind (in case we were not bound before)
333 bind( sal_False );
334 return _getMIPEvaluationContexts();
338 Binding::IntSequence_t Binding::getUnoTunnelID()
340 static cppu::OImplementationId aImplementationId;
341 return aImplementationId.getImplementationId();
344 Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet )
346 Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY );
347 return xTunnel.is()
348 ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID()))
349 : NULL;
355 OUString Binding::getBindingID() const
357 return msBindingID;
360 void Binding::setBindingID( const OUString& sBindingID )
362 msBindingID = sBindingID;
365 OUString Binding::getBindingExpression() const
367 return maBindingExpression.getExpression();
370 void Binding::setBindingExpression( const OUString& sBindingExpression)
372 maBindingExpression.setExpression( sBindingExpression );
373 bindingModified();
376 OUString Binding::getReadonlyExpression() const
378 return maReadonly.getExpression();
381 void Binding::setReadonlyExpression( const OUString& sReadonly)
383 maReadonly.setExpression( sReadonly );
384 bindingModified();
387 OUString Binding::getRelevantExpression() const
389 return maRelevant.getExpression();
392 void Binding::setRelevantExpression( const OUString& sRelevant )
394 maRelevant.setExpression( sRelevant );
395 bindingModified();
398 OUString Binding::getRequiredExpression() const
400 return maRequired.getExpression();
403 void Binding::setRequiredExpression( const OUString& sRequired )
405 maRequired.setExpression( sRequired );
406 bindingModified();
409 OUString Binding::getConstraintExpression() const
411 return maConstraint.getExpression();
414 void Binding::setConstraintExpression( const OUString& sConstraint )
416 maConstraint.setExpression( sConstraint );
417 msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT,
418 sConstraint );
420 // TODO: This should only re-evaluate the constraint, and notify
421 // the validity constraint listeners; instead we currently pretend
422 // the entire binding was notified, which does a little too much.
423 bindingModified();
426 OUString Binding::getCalculateExpression() const
428 return maCalculate.getExpression();
431 void Binding::setCalculateExpression( const OUString& sCalculate )
433 maCalculate.setExpression( sCalculate );
434 bindingModified();
437 OUString Binding::getType() const
439 return msTypeName;
442 void Binding::setType( const OUString& sTypeName )
444 msTypeName = sTypeName;
445 bindingModified();
448 Binding::XNameContainer_t Binding::getBindingNamespaces() const
450 // return _getNamespaces();
451 return mxNamespaces;
454 void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces )
456 _setNamespaces( rNamespaces, true );
459 Binding::XNameContainer_t Binding::getModelNamespaces() const
461 return _getNamespaces();
464 void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces )
466 _setNamespaces( rNamespaces, false );
469 bool Binding::getReadOnly() const
471 return maMIP.isReadonly();
474 bool Binding::getRelevant() const
476 return maMIP.isRelevant();
479 bool Binding::getExternalData() const
481 bool bExternalData = true;
482 if ( !mxModel.is() )
483 return bExternalData;
487 Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW );
488 OSL_VERIFY(
489 xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExternalData" ) ) ) >>= bExternalData );
491 catch( const Exception& )
493 DBG_UNHANDLED_EXCEPTION();
495 return bExternalData;
499 void Binding::checkLive()
500 throw( RuntimeException )
502 if( ! isLive() )
503 throw RuntimeException( EXCEPT("Binding not initialized") );
506 void Binding::checkModel()
507 throw( RuntimeException )
509 if( ! mxModel.is() )
510 throw RuntimeException( EXCEPT("Binding has no Model") );
513 bool Binding::isLive() const
515 const Model* pModel = getModelImpl();
516 return ( pModel != NULL ) ? pModel->isInitialized() : false;
519 Model* Binding::getModelImpl() const
521 return getModelImpl( mxModel );
524 Model* Binding::getModelImpl( const Model_t& xModel ) const
526 Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY );
527 Model* pModel = xTunnel.is()
528 ? reinterpret_cast<Model*>(
529 xTunnel->getSomething( Model::getUnoTunnelID() ) )
530 : NULL;
531 return pModel;
534 void lcl_addListenerToNode( Reference<XNode> xNode,
535 Reference<XEventListener> xListener )
537 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
538 if( xTarget.is() )
540 xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"),
541 xListener, false );
542 xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"),
543 xListener, true );
544 xTarget->addEventListener( OUSTRING("DOMAttrModified"),
545 xListener, false );
546 xTarget->addEventListener( OUSTRING("DOMAttrModified"),
547 xListener, true );
548 xTarget->addEventListener( OUSTRING("DOMAttrModified"),
549 xListener, true );
550 xTarget->addEventListener( OUSTRING("xforms-generic"),
551 xListener, true );
555 void lcl_removeListenerFromNode( Reference<XNode> xNode,
556 Reference<XEventListener> xListener )
558 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
559 if( xTarget.is() )
561 xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"),
562 xListener, false );
563 xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"),
564 xListener, true );
565 xTarget->removeEventListener( OUSTRING("DOMAttrModified"),
566 xListener, false );
567 xTarget->removeEventListener( OUSTRING("DOMAttrModified"),
568 xListener, true );
569 xTarget->removeEventListener( OUSTRING("xforms-generic"),
570 xListener, true );
574 ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const
576 OSL_ENSURE( getModelImpl() != NULL, "need model impl" );
578 // iterate over nodes of bind expression and create
579 // EvaluationContext for each
580 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
581 ::std::vector<EvaluationContext> aVector;
582 sal_Int32 nCount = 0; // count nodes for context position
583 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
584 aIter != aNodes.end();
585 aIter++, nCount++ )
587 OSL_ENSURE( aIter->is(), "no node?" );
589 // create proper evaluation context for this MIP
590 aVector.push_back( EvaluationContext( *aIter, getModel(),
591 getBindingNamespaces(),
592 nCount, aNodes.size() ) );
594 return aVector;
597 void Binding::bind( bool bForceRebind )
599 checkModel();
601 // bind() will evaluate this binding as follows:
602 // 1) evaluate the binding expression
603 // 1b) if necessary, create node according to 'lazy author' rules
604 // 2) register suitable listeners on the instance (and remove old ones)
605 // 3) remove old MIPs defined by this binding
606 // 4) for every node in the binding nodeset do:
607 // 1) create proper evaluation context for this MIP
608 // 2) evaluate calculate expression (and push value into instance)
609 // 3) evaluate remaining MIPs
610 // 4) evaluate the locally defined MIPs, and push them to the model
613 // 1) evaluate the binding expression
614 EvaluationContext aContext = getEvaluationContext();
615 maBindingExpression.evaluate( aContext );
616 if( ! maBindingExpression.getNode().is() )
618 // 1b) create node (if valid element name)
619 if( isValidQName( maBindingExpression.getExpression(),
620 aContext.mxNamespaces ) )
622 aContext.mxContextNode->appendChild(
623 Reference<XNode>(
624 aContext.mxContextNode->getOwnerDocument()->createElement(
625 maBindingExpression.getExpression() ),
626 UNO_QUERY ) );
627 maBindingExpression.evaluate( aContext );
628 OSL_ENSURE( maBindingExpression.getNode().is(),
629 "we should bind to the newly inserted node!" );
632 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
634 // 2) register suitable listeners on the instance (and remove old ones)
635 if( maEventNodes.empty() || bForceRebind )
637 for( XNodes_t::iterator aIter = maEventNodes.begin();
638 aIter != maEventNodes.end();
639 aIter ++ )
640 lcl_removeListenerFromNode( *aIter, this );
641 maEventNodes.clear();
642 if( isSimpleBinding() )
643 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin();
644 aIter != aNodes.end();
645 aIter++ )
646 maEventNodes.push_back( *aIter );
647 else
648 maEventNodes.push_back(
649 Reference<XNode>( aContext.mxContextNode->getOwnerDocument(),
650 UNO_QUERY_THROW ) );
651 for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin();
652 aIter2 != maEventNodes.end();
653 aIter2 ++ )
654 lcl_addListenerToNode( *aIter2, this );
657 // 3) remove old MIPs defined by this binding
658 Model* pModel = getModelImpl();
659 OSL_ENSURE( pModel != NULL, "need model" );
660 pModel->removeMIPs( this );
662 // 4) calculate all MIPs
663 ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts();
664 for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin();
665 aIter != aMIPContexts.end();
666 aIter++ )
668 EvaluationContext& rContext = *aIter;
670 // evaluate calculate expression (and push value into instance)
671 // (prevent recursion using mbInCalculate
672 if( ! maCalculate.isEmptyExpression() )
674 if( ! mbInCalculate )
676 mbInCalculate = true;
677 maCalculate.evaluate( rContext );
678 pModel->setSimpleContent( rContext.mxContextNode,
679 maCalculate.getString() );
680 mbInCalculate = false;
684 // now evaluate remaining MIPs in the apropriate context
685 maReadonly.evaluate( rContext );
686 maRelevant.evaluate( rContext );
687 maRequired.evaluate( rContext );
688 maConstraint.evaluate( rContext );
689 // type is static; does not need updating
691 // evaluate the locally defined MIPs, and push them to the model
692 pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() );
697 // helper for Binding::valueModified
698 void lcl_modified( const Binding::XModifyListener_t xListener,
699 const Reference<XInterface> xSource )
701 OSL_ENSURE( xListener.is(), "no listener?" );
702 xListener->modified( EventObject( xSource ) );
705 // helper for Binding::valueModified
706 void lcl_listentry( const Binding::XListEntryListener_t xListener,
707 const Reference<XInterface> xSource )
709 OSL_ENSURE( xListener.is(), "no listener?" );
710 // TODO: send fine granular events
711 xListener->allEntriesChanged( EventObject( xSource ) );
714 // helper for Binding::valueModified
715 void lcl_validate( const Binding::XValidityConstraintListener_t xListener,
716 const Reference<XInterface> xSource )
718 OSL_ENSURE( xListener.is(), "no listener?" );
719 xListener->validityConstraintChanged( EventObject( xSource ) );
723 void Binding::valueModified()
725 // defer notifications, if so desired
726 if( mnDeferModifyNotifications > 0 )
728 mbValueModified = true;
729 return;
731 mbValueModified = false;
733 // query MIP used by our first node (also note validity)
734 Reference<XNode> xNode = maBindingExpression.getNode();
735 maMIP = getModelImpl()->queryMIP( xNode );
737 // distribute MIPs _used_ by this binding
738 if( xNode.is() )
740 notifyAndCachePropertyValue( HANDLE_ReadOnly );
741 notifyAndCachePropertyValue( HANDLE_Relevant );
744 // iterate over _value_ listeners and send each a modified signal,
745 // using this object as source (will also update validity, because
746 // control will query once the value has changed)
747 Reference<XInterface> xSource = static_cast<XPropertySet*>( this );
748 ::std::for_each( maModifyListeners.begin(),
749 maModifyListeners.end(),
750 ::std::bind2nd( ::std::ptr_fun( lcl_modified ), xSource ) );
751 ::std::for_each( maListEntryListeners.begin(),
752 maListEntryListeners.end(),
753 ::std::bind2nd( ::std::ptr_fun( lcl_listentry ), xSource ) );
754 ::std::for_each( maValidityListeners.begin(),
755 maValidityListeners.end(),
756 ::std::bind2nd( ::std::ptr_fun( lcl_validate ), xSource ) );
758 // now distribute MIPs to childs
759 if( xNode.is() )
760 distributeMIP( xNode->getFirstChild() );
763 void Binding::distributeMIP( const XNode_t & rxNode ) {
765 typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t;
766 OUString sEventName( RTL_CONSTASCII_USTRINGPARAM("xforms-generic") );
767 XFormsEvent_t *pEvent = new XFormsEvent_t;
768 pEvent->initXFormsEvent(sEventName, sal_True, sal_False);
769 Reference<XEvent> xEvent(pEvent);
771 // naive depth-first traversal
772 XNode_t xNode( rxNode );
773 while(xNode.is()) {
775 // notifications should be triggered at the
776 // leaf nodes first, bubbling upwards the hierarchy.
777 XNode_t child(xNode->getFirstChild());
778 if(child.is())
779 distributeMIP(child);
781 // we're standing at a particular node somewhere
782 // below the one which changed a property (MIP).
783 // bindings which are listening at this node will receive
784 // a notification message about what exactly happened.
785 Reference< XEventTarget > target(xNode,UNO_QUERY);
786 target->dispatchEvent(xEvent);
788 xNode = xNode->getNextSibling();
792 void Binding::bindingModified()
794 // defer notifications, if so desired
795 if( mnDeferModifyNotifications > 0 )
797 mbBindingModified = true;
798 return;
800 mbBindingModified = false;
802 // rebind (if live); then call valueModified
803 // A binding should be inert until its model is fully constructed.
804 if( isLive() )
806 bind( true );
807 valueModified();
812 MIP Binding::getLocalMIP() const
814 MIP aMIP;
816 if( maReadonly.hasValue() )
817 aMIP.setReadonly( maReadonly.getBool( false ) );
818 if( maRelevant.hasValue() )
819 aMIP.setRelevant( maRelevant.getBool( true ) );
820 if( maRequired.hasValue() )
821 aMIP.setRequired( maRequired.getBool( false ) );
822 if( maConstraint.hasValue() )
824 aMIP.setConstraint( maConstraint.getBool( true ) );
825 if( ! aMIP.isConstraint() )
826 aMIP.setConstraintExplanation( msExplainConstraint );
828 if( msTypeName.getLength() > 0 )
829 aMIP.setTypeName( msTypeName );
831 // calculate: only handle presence of calculate; value set elsewhere
832 aMIP.setHasCalculate( !maCalculate.isEmptyExpression() );
834 return aMIP;
837 Binding::XDataType_t Binding::getDataType()
839 OSL_ENSURE( getModel().is(), "need model" );
840 OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" );
842 Reference<XDataTypeRepository> xRepository(
843 getModel()->getDataTypeRepository(), UNO_QUERY );
844 OUString sTypeName = maMIP.getTypeName();
846 return ( xRepository.is() && xRepository->hasByName( sTypeName ) )
847 ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY)
848 : Reference<XDataType>( NULL );
851 bool Binding::isValid_DataType()
853 Reference<XDataType> xDataType = getDataType();
854 return xDataType.is()
855 ? xDataType->validate( maBindingExpression.getString() )
856 : true;
859 rtl::OUString Binding::explainInvalid_DataType()
861 Reference<XDataType> xDataType = getDataType();
862 return xDataType.is()
863 ? xDataType->explainInvalid( maBindingExpression.getString() )
864 : OUString();
867 void Binding::clear()
869 // remove MIPs contributed by this binding
870 Model* pModel = getModelImpl();
871 if( pModel != NULL )
872 pModel->removeMIPs( this );
874 // remove all references
875 for( XNodes_t::iterator aIter = maEventNodes.begin();
876 aIter != maEventNodes.end();
877 aIter ++ )
878 lcl_removeListenerFromNode( *aIter, this );
879 maEventNodes.clear();
881 // clear expressions
882 maBindingExpression.clear();
883 maReadonly.clear();
884 maRelevant.clear();
885 maRequired.clear();
886 maConstraint.clear();
887 maCalculate.clear();
889 // TODO: what about our listeners?
893 void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom,
894 Binding::XNameContainer_t& xTo )
896 OSL_ENSURE( xFrom.is(), "no source" );
897 OSL_ENSURE( xTo.is(), "no target" );
899 // iterate over name in source
900 Sequence<OUString> aNames = xTo->getElementNames();
901 sal_Int32 nNames = aNames.getLength();
902 const OUString* pNames = aNames.getConstArray();
903 for( sal_Int32 i = 0; i < nNames; i++ )
905 const OUString& rName = pNames[i];
907 if( ! xFrom->hasByName( rName ) )
908 xTo->removeByName( rName );
912 /** copy namespaces from one namespace container into another
913 * @param bOverwrite true: overwrite namespaces in target
914 * false: do not overwrite namespaces in target
915 * @param bMove true: move namespaces (i.e., delete in source)
916 * false: copy namespaces (do not modify source)
917 * @param bFromSource true: use elements from source
918 * false: use only elements from target
920 void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom,
921 Binding::XNameContainer_t& xTo,
922 bool bOverwrite )
924 OSL_ENSURE( xFrom.is(), "no source" );
925 OSL_ENSURE( xTo.is(), "no target" );
927 // iterate over name in source
928 Sequence<OUString> aNames = xFrom->getElementNames();
929 sal_Int32 nNames = aNames.getLength();
930 const OUString* pNames = aNames.getConstArray();
931 for( sal_Int32 i = 0; i < nNames; i++ )
933 const OUString& rName = pNames[i];
935 // determine whether to copy the value, and whether to delete
936 // it in the source:
938 bool bInTarget = xTo->hasByName( rName );
940 // we copy: if property is in target, and
941 // if bOverwrite is set, or when the namespace prefix is free
942 bool bCopy = bOverwrite || ! bInTarget;
944 // and now... ACTION!
945 if( bCopy )
947 if( bInTarget )
948 xTo->replaceByName( rName, xFrom->getByName( rName ) );
949 else
950 xTo->insertByName( rName, xFrom->getByName( rName ) );
955 // implement get*Namespaces()
956 // (identical for both variants)
957 Binding::XNameContainer_t Binding::_getNamespaces() const
959 XNameContainer_t xNamespaces = new NameContainer<OUString>();
960 lcl_copyNamespaces( mxNamespaces, xNamespaces, true );
962 // merge model's with binding's own namespaces
963 Model* pModel = getModelImpl();
964 if( pModel != NULL )
965 lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false );
967 return xNamespaces;
970 // implement set*Namespaces()
971 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
972 void Binding::_setNamespaces( const XNameContainer_t& rNamespaces,
973 bool bBinding )
975 Model* pModel = getModelImpl();
976 XNameContainer_t xModelNamespaces = ( pModel != NULL )
977 ? pModel->getNamespaces()
978 : NULL;
979 OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?");
981 // remove deleted namespaces
982 lcl_removeOtherNamespaces( rNamespaces, mxNamespaces );
983 if( !bBinding && xModelNamespaces.is() )
984 lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces );
986 // copy namespaces as appropriate
987 Sequence<OUString> aNames = rNamespaces->getElementNames();
988 sal_Int32 nNames = aNames.getLength();
989 const OUString* pNames = aNames.getConstArray();
990 for( sal_Int32 i = 0; i < nNames; i++ )
992 const OUString& rName = pNames[i];
993 Any aValue = rNamespaces->getByName( rName );
995 // determine whether the namespace should go into model's or
996 // into binding's namespaces
997 bool bLocal =
998 ! xModelNamespaces.is()
999 || mxNamespaces->hasByName( rName )
1000 || ( bBinding
1001 && xModelNamespaces.is()
1002 && xModelNamespaces->hasByName( rName ) );
1004 // write namespace into the appropriate namespace container
1005 XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces;
1006 OSL_ENSURE( rWhich.is(), "whoops" );
1007 if( rWhich->hasByName( rName ) )
1008 rWhich->replaceByName( rName, aValue );
1009 else
1010 rWhich->insertByName( rName, aValue );
1012 // always 'promote' namespaces from binding to model, if equal
1013 if( xModelNamespaces.is()
1014 && xModelNamespaces->hasByName( rName )
1015 && mxNamespaces->hasByName( rName )
1016 && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) )
1018 mxNamespaces->removeByName( rName );
1022 // ... done. But we modified the binding!
1023 bindingModified();
1026 void Binding::_checkBindingID()
1028 if( getModel().is() )
1030 Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW );
1031 if( msBindingID.getLength() == 0 )
1033 // no binding ID? then make one up!
1034 OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME );
1035 sIDPrefix += String::CreateFromAscii( " " );
1036 sal_Int32 nNumber = 0;
1037 OUString sName;
1040 nNumber++;
1041 sName = sIDPrefix + OUString::valueOf( nNumber );
1043 while( xBindings->hasByName( sName ) );
1044 setBindingID( sName );
1053 // XValueBinding
1056 Binding::Sequence_Type_t Binding::getSupportedValueTypes()
1057 throw( RuntimeException )
1059 return Convert::get().getTypes();
1062 sal_Bool Binding::supportsType( const Type_t& rType )
1063 throw( RuntimeException )
1065 return Convert::get().hasType( rType );
1068 Binding::Any_t Binding::getValue( const Type_t& rType )
1069 throw( IncompatibleTypesException,
1070 RuntimeException )
1072 // first, check for model
1073 checkLive();
1075 // second, check for type
1076 if( ! supportsType( rType ) )
1077 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1079 // return string value (if present; else return empty Any)
1080 Binding::Any_t result = Any();
1081 if(maBindingExpression.hasValue()) {
1082 rtl::OUString pathExpr(maBindingExpression.getString());
1083 Convert &rConvert = Convert::get();
1084 result = rConvert.toAny(pathExpr,rType);
1087 // return maBindingExpression.hasValue()
1088 // ? Convert::get().toAny( maBindingExpression.getString(), rType )
1089 // : Any();
1091 return result;
1094 void Binding::setValue( const Any_t& aValue )
1095 throw( IncompatibleTypesException,
1096 InvalidBindingStateException,
1097 NoSupportException,
1098 RuntimeException )
1100 // first, check for model
1101 checkLive();
1103 // check for supported type
1104 if( ! supportsType( aValue.getValueType() ) )
1105 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
1107 if( maBindingExpression.hasValue() )
1109 Binding::XNode_t xNode = maBindingExpression.getNode();
1110 if( xNode.is() )
1112 OUString sValue = Convert::get().toXSD( aValue );
1113 bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue );
1114 if( ! bSuccess )
1115 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1117 else
1118 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1120 else
1121 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1126 // XListEntry Source
1129 sal_Int32 Binding::getListEntryCount()
1130 throw( RuntimeException )
1132 // first, check for model
1133 checkLive();
1135 // return size of node list
1136 return maBindingExpression.getNodeList().size();
1139 void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer )
1141 if( xNode->getNodeType() == NodeType_TEXT_NODE
1142 || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
1144 rBuffer.append( xNode->getNodeValue() );
1146 else
1148 for( Reference<XNode> xChild = xNode->getFirstChild();
1149 xChild.is();
1150 xChild = xChild->getNextSibling() )
1152 lcl_getString( xChild, rBuffer );
1157 OUString lcl_getString( const Reference<XNode>& xNode )
1159 OUStringBuffer aBuffer;
1160 lcl_getString( xNode, aBuffer );
1161 return aBuffer.makeStringAndClear();
1164 OUString Binding::getListEntry( sal_Int32 nPosition )
1165 throw( IndexOutOfBoundsException,
1166 RuntimeException )
1168 // first, check for model
1169 checkLive();
1171 // check bounds and return proper item
1172 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1173 if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) )
1174 throw IndexOutOfBoundsException( EXCEPT("") );
1175 return lcl_getString( aNodes[ nPosition ] );
1178 Sequence<OUString> Binding::getAllListEntries()
1179 throw( RuntimeException )
1181 // first, check for model
1182 checkLive();
1184 // create sequence of string values
1185 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
1186 Sequence<OUString> aSequence( aNodes.size() );
1187 OUString* pSequence = aSequence.getArray();
1188 for( sal_Int32 n = 0; n < aSequence.getLength(); n++ )
1190 pSequence[n] = lcl_getString( aNodes[n] );
1193 return aSequence;
1196 void Binding::addListEntryListener( const XListEntryListener_t& xListener )
1197 throw( NullPointerException,
1198 RuntimeException )
1200 OSL_ENSURE( xListener.is(), "need listener!" );
1201 if( ::std::find( maListEntryListeners.begin(),
1202 maListEntryListeners.end(),
1203 xListener)
1204 == maListEntryListeners.end() )
1205 maListEntryListeners.push_back( xListener );
1208 void Binding::removeListEntryListener( const XListEntryListener_t& xListener )
1209 throw( NullPointerException,
1210 RuntimeException )
1212 XListEntryListeners_t::iterator aIter =
1213 ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(),
1214 xListener );
1215 if( aIter != maListEntryListeners.end() )
1216 maListEntryListeners.erase( aIter );
1221 // XValidator
1224 sal_Bool Binding::isValid( const Any_t& )
1225 throw( RuntimeException )
1227 // first, check for model
1228 checkLive();
1230 // ignore value; determine validate only on current data
1231 return isValid();
1234 rtl::OUString Binding::explainInvalid(
1235 const Any_t& /*Value*/ )
1236 throw( RuntimeException )
1238 // first, check for model
1239 checkLive();
1241 // ignore value; determine explanation only on current data
1242 return explainInvalid();
1245 void Binding::addValidityConstraintListener(
1246 const XValidityConstraintListener_t& xListener )
1247 throw( NullPointerException,
1248 RuntimeException )
1250 OSL_ENSURE( xListener.is(), "need listener!" );
1251 if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener)
1252 == maValidityListeners.end() )
1253 maValidityListeners.push_back( xListener );
1256 void Binding::removeValidityConstraintListener(
1257 const XValidityConstraintListener_t& xListener )
1258 throw( NullPointerException,
1259 RuntimeException )
1261 XValidityConstraintListeners_t::iterator aIter =
1262 ::std::find( maValidityListeners.begin(), maValidityListeners.end(),
1263 xListener );
1264 if( aIter != maValidityListeners.end() )
1265 maValidityListeners.erase( aIter );
1271 // xml::dom::event::XEventListener
1274 void Binding::handleEvent( const XEvent_t& xEvent )
1275 throw( RuntimeException )
1277 OUString sType(xEvent->getType());
1278 //OUString sEventMIPChanged(RTL_CONSTASCII_USTRINGPARAM("xforms-generic"));
1279 //if(sType.equals(sEventMIPChanged)) {
1280 if(!sType.compareToAscii("xforms-generic")) {
1282 // the modification of the 'mnDeferModifyNotifications'-member
1283 // is necessary to prevent infinite notication looping.
1284 // This can happend in case the binding which caused
1285 // the notification chain is listening to those events
1286 // as well...
1287 bool bPreserveValueModified = mbValueModified;
1288 mnDeferModifyNotifications++;
1289 valueModified();
1290 --mnDeferModifyNotifications;
1291 mbValueModified = bPreserveValueModified;
1292 return;
1295 // if we're a dynamic binding, we better re-bind, too!
1296 bind( false );
1298 // our value was maybe modified
1299 valueModified();
1304 // lang::XUnoTunnel
1307 sal_Int64 Binding::getSomething( const IntSequence_t& xId )
1308 throw( RuntimeException )
1310 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
1314 // XCloneable
1317 Binding::XCloneable_t SAL_CALL Binding::createClone()
1318 throw( RuntimeException )
1320 Reference< XPropertySet > xClone;
1322 Model* pModel = getModelImpl();
1323 if ( pModel )
1324 xClone = pModel->cloneBinding( this );
1325 else
1327 xClone = new Binding;
1328 copy( this, xClone );
1330 return XCloneable_t( xClone, UNO_QUERY );
1334 // property set implementations
1337 #define REGISTER_PROPERTY( property, type ) \
1338 registerProperty( PROPERTY( property, type ), \
1339 new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) );
1341 #define REGISTER_PROPERTY_RO( property, type ) \
1342 registerProperty( PROPERTY_RO( property, type ), \
1343 new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) );
1345 #define REGISTER_BOOL_PROPERTY_RO( property ) \
1346 registerProperty( PROPERTY_RO( property, sal_Bool ), \
1347 new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) );
1349 void Binding::initializePropertySet()
1351 REGISTER_PROPERTY ( BindingID, OUString );
1352 REGISTER_PROPERTY ( BindingExpression, OUString );
1353 REGISTER_PROPERTY_RO ( Model, Model_t );
1354 REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t );
1355 REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t );
1356 REGISTER_PROPERTY_RO ( ModelID, OUString );
1357 REGISTER_PROPERTY ( ReadonlyExpression, OUString );
1358 REGISTER_PROPERTY ( RelevantExpression, OUString );
1359 REGISTER_PROPERTY ( RequiredExpression, OUString );
1360 REGISTER_PROPERTY ( ConstraintExpression, OUString );
1361 REGISTER_PROPERTY ( CalculateExpression, OUString );
1362 REGISTER_PROPERTY ( Type, OUString );
1363 REGISTER_PROPERTY_RO ( ReadOnly, bool );
1364 REGISTER_PROPERTY_RO ( Relevant, bool );
1365 REGISTER_BOOL_PROPERTY_RO( ExternalData );
1367 initializePropertyValueCache( HANDLE_ReadOnly );
1368 initializePropertyValueCache( HANDLE_Relevant );
1369 initializePropertyValueCache( HANDLE_ExternalData );
1372 void Binding::addModifyListener(
1373 const XModifyListener_t& xListener )
1374 throw( RuntimeException )
1376 OSL_ENSURE( xListener.is(), "need listener!" );
1377 if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener )
1378 == maModifyListeners.end() )
1379 maModifyListeners.push_back( xListener );
1381 // HACK: currently, we have to 'push' some MIPs to the control
1382 // (read-only, relevant, etc.) To enable this, we need to update
1383 // the control at least once when it registers here.
1384 valueModified();
1387 void Binding::removeModifyListener(
1388 const XModifyListener_t& xListener )
1389 throw( RuntimeException )
1391 ModifyListeners_t::iterator aIter =
1392 ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener );
1393 if( aIter != maModifyListeners.end() )
1394 maModifyListeners.erase( aIter );
1400 rtl::OUString Binding::getName()
1401 throw( RuntimeException )
1403 return getBindingID();
1406 void SAL_CALL Binding::setName( const rtl::OUString& rName )
1407 throw( RuntimeException )
1409 // use the XPropertySet methods, so the change in the name is notified to the
1410 // property listeners
1411 setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) );