1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: binding.cxx,v $
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"
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>
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
;
75 using rtl::OUStringBuffer
;
77 using xforms::Binding
;
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
141 maBindingExpression(),
143 mxNamespaces( new NameContainer
<OUString
>() ),
144 mbInCalculate( false ),
145 mnDeferModifyNotifications( 0 ),
146 mbValueModified( false ),
147 mbBindingModified( false )
150 initializePropertySet();
153 Binding::~Binding() throw()
159 Binding::Model_t
Binding::getModel() const
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
175 // set namespaces (and move to model, if appropriate)
176 setBindingNamespaces( xNamespaces
);
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() )
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();
221 maConstraint
.clear();
224 // let's just pretend the binding has been modified -> full rebind()
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
)
237 if( mbValueModified
)
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()
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)
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();
282 OUString
Binding::explainInvalid()
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!" );
320 EvaluationContext
Binding::getEvaluationContext() const
322 OSL_ENSURE( getModelImpl() != NULL
, "need model impl" );
323 EvaluationContext aContext
= getModelImpl()->getEvaluationContext();
324 aContext
.mxNamespaces
= getBindingNamespaces();
328 ::std::vector
<EvaluationContext
> Binding::getMIPEvaluationContexts()
330 OSL_ENSURE( getModelImpl() != NULL
, "need model impl" );
332 // bind (in case we were not bound before)
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
);
348 ? reinterpret_cast<Binding
*>( xTunnel
->getSomething(getUnoTunnelID()))
355 OUString
Binding::getBindingID() const
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
);
376 OUString
Binding::getReadonlyExpression() const
378 return maReadonly
.getExpression();
381 void Binding::setReadonlyExpression( const OUString
& sReadonly
)
383 maReadonly
.setExpression( sReadonly
);
387 OUString
Binding::getRelevantExpression() const
389 return maRelevant
.getExpression();
392 void Binding::setRelevantExpression( const OUString
& sRelevant
)
394 maRelevant
.setExpression( sRelevant
);
398 OUString
Binding::getRequiredExpression() const
400 return maRequired
.getExpression();
403 void Binding::setRequiredExpression( const OUString
& sRequired
)
405 maRequired
.setExpression( sRequired
);
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
,
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.
426 OUString
Binding::getCalculateExpression() const
428 return maCalculate
.getExpression();
431 void Binding::setCalculateExpression( const OUString
& sCalculate
)
433 maCalculate
.setExpression( sCalculate
);
437 OUString
Binding::getType() const
442 void Binding::setType( const OUString
& sTypeName
)
444 msTypeName
= sTypeName
;
448 Binding::XNameContainer_t
Binding::getBindingNamespaces() const
450 // return _getNamespaces();
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;
483 return bExternalData
;
487 Reference
< XPropertySet
> xModelProps( mxModel
, UNO_QUERY_THROW
);
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
)
503 throw RuntimeException( EXCEPT("Binding not initialized") );
506 void Binding::checkModel()
507 throw( RuntimeException
)
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() ) )
534 void lcl_addListenerToNode( Reference
<XNode
> xNode
,
535 Reference
<XEventListener
> xListener
)
537 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
540 xTarget
->addEventListener( OUSTRING("DOMCharacterDataModified"),
542 xTarget
->addEventListener( OUSTRING("DOMCharacterDataModified"),
544 xTarget
->addEventListener( OUSTRING("DOMAttrModified"),
546 xTarget
->addEventListener( OUSTRING("DOMAttrModified"),
548 xTarget
->addEventListener( OUSTRING("DOMAttrModified"),
550 xTarget
->addEventListener( OUSTRING("xforms-generic"),
555 void lcl_removeListenerFromNode( Reference
<XNode
> xNode
,
556 Reference
<XEventListener
> xListener
)
558 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
561 xTarget
->removeEventListener( OUSTRING("DOMCharacterDataModified"),
563 xTarget
->removeEventListener( OUSTRING("DOMCharacterDataModified"),
565 xTarget
->removeEventListener( OUSTRING("DOMAttrModified"),
567 xTarget
->removeEventListener( OUSTRING("DOMAttrModified"),
569 xTarget
->removeEventListener( OUSTRING("xforms-generic"),
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();
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() ) );
597 void Binding::bind( bool bForceRebind
)
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(
624 aContext
.mxContextNode
->getOwnerDocument()->createElement(
625 maBindingExpression
.getExpression() ),
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();
640 lcl_removeListenerFromNode( *aIter
, this );
641 maEventNodes
.clear();
642 if( isSimpleBinding() )
643 for( PathExpression::NodeVector_t::iterator aIter
= aNodes
.begin();
644 aIter
!= aNodes
.end();
646 maEventNodes
.push_back( *aIter
);
648 maEventNodes
.push_back(
649 Reference
<XNode
>( aContext
.mxContextNode
->getOwnerDocument(),
651 for( PathExpression::NodeVector_t::iterator aIter2
= maEventNodes
.begin();
652 aIter2
!= maEventNodes
.end();
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();
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;
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
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
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
);
775 // notifications should be triggered at the
776 // leaf nodes first, bubbling upwards the hierarchy.
777 XNode_t
child(xNode
->getFirstChild());
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;
800 mbBindingModified
= false;
802 // rebind (if live); then call valueModified
803 // A binding should be inert until its model is fully constructed.
812 MIP
Binding::getLocalMIP() const
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() );
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() )
859 rtl::OUString
Binding::explainInvalid_DataType()
861 Reference
<XDataType
> xDataType
= getDataType();
862 return xDataType
.is()
863 ? xDataType
->explainInvalid( maBindingExpression
.getString() )
867 void Binding::clear()
869 // remove MIPs contributed by this binding
870 Model
* pModel
= getModelImpl();
872 pModel
->removeMIPs( this );
874 // remove all references
875 for( XNodes_t::iterator aIter
= maEventNodes
.begin();
876 aIter
!= maEventNodes
.end();
878 lcl_removeListenerFromNode( *aIter
, this );
879 maEventNodes
.clear();
882 maBindingExpression
.clear();
886 maConstraint
.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
,
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
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!
948 xTo
->replaceByName( rName
, xFrom
->getByName( rName
) );
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();
965 lcl_copyNamespaces( pModel
->getNamespaces(), xNamespaces
, false );
970 // implement set*Namespaces()
971 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
972 void Binding::_setNamespaces( const XNameContainer_t
& rNamespaces
,
975 Model
* pModel
= getModelImpl();
976 XNameContainer_t xModelNamespaces
= ( pModel
!= NULL
)
977 ? pModel
->getNamespaces()
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
998 ! xModelNamespaces
.is()
999 || mxNamespaces
->hasByName( rName
)
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
);
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!
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;
1041 sName
= sIDPrefix
+ OUString::valueOf( nNumber
);
1043 while( xBindings
->hasByName( sName
) );
1044 setBindingID( sName
);
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
,
1072 // first, check for model
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 )
1094 void Binding::setValue( const Any_t
& aValue
)
1095 throw( IncompatibleTypesException
,
1096 InvalidBindingStateException
,
1100 // first, check for model
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();
1112 OUString sValue
= Convert::get().toXSD( aValue
);
1113 bool bSuccess
= getModelImpl()->setSimpleContent( xNode
, sValue
);
1115 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1118 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1121 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1126 // XListEntry Source
1129 sal_Int32
Binding::getListEntryCount()
1130 throw( RuntimeException
)
1132 // first, check for model
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() );
1148 for( Reference
<XNode
> xChild
= xNode
->getFirstChild();
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
,
1168 // first, check for model
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
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
] );
1196 void Binding::addListEntryListener( const XListEntryListener_t
& xListener
)
1197 throw( NullPointerException
,
1200 OSL_ENSURE( xListener
.is(), "need listener!" );
1201 if( ::std::find( maListEntryListeners
.begin(),
1202 maListEntryListeners
.end(),
1204 == maListEntryListeners
.end() )
1205 maListEntryListeners
.push_back( xListener
);
1208 void Binding::removeListEntryListener( const XListEntryListener_t
& xListener
)
1209 throw( NullPointerException
,
1212 XListEntryListeners_t::iterator aIter
=
1213 ::std::find( maListEntryListeners
.begin(), maListEntryListeners
.end(),
1215 if( aIter
!= maListEntryListeners
.end() )
1216 maListEntryListeners
.erase( aIter
);
1224 sal_Bool
Binding::isValid( const Any_t
& )
1225 throw( RuntimeException
)
1227 // first, check for model
1230 // ignore value; determine validate only on current data
1234 rtl::OUString
Binding::explainInvalid(
1235 const Any_t
& /*Value*/ )
1236 throw( RuntimeException
)
1238 // first, check for model
1241 // ignore value; determine explanation only on current data
1242 return explainInvalid();
1245 void Binding::addValidityConstraintListener(
1246 const XValidityConstraintListener_t
& xListener
)
1247 throw( NullPointerException
,
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
,
1261 XValidityConstraintListeners_t::iterator aIter
=
1262 ::std::find( maValidityListeners
.begin(), maValidityListeners
.end(),
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
1287 bool bPreserveValueModified
= mbValueModified
;
1288 mnDeferModifyNotifications
++;
1290 --mnDeferModifyNotifications
;
1291 mbValueModified
= bPreserveValueModified
;
1295 // if we're a dynamic binding, we better re-bind, too!
1298 // our value was maybe modified
1307 sal_Int64
Binding::getSomething( const IntSequence_t
& xId
)
1308 throw( RuntimeException
)
1310 return reinterpret_cast<sal_Int64
>( ( xId
== getUnoTunnelID() ) ? this : NULL
);
1317 Binding::XCloneable_t SAL_CALL
Binding::createClone()
1318 throw( RuntimeException
)
1320 Reference
< XPropertySet
> xClone
;
1322 Model
* pModel
= getModelImpl();
1324 xClone
= pModel
->cloneBinding( this );
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.
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
) );