1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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"
31 #include <strings.hrc>
33 #include <rtl/ustrbuf.hxx>
34 #include <osl/diagnose.h>
36 #include <tools/diagnose_ex.h>
41 #include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
42 #include <com/sun/star/form/binding/InvalidBindingStateException.hpp>
43 #include <com/sun/star/uno/Any.hxx>
44 #include <com/sun/star/xml/dom/XNodeList.hpp>
45 #include <com/sun/star/xml/dom/XNode.hpp>
46 #include <com/sun/star/xml/dom/XDocument.hpp>
47 #include <com/sun/star/xml/dom/XElement.hpp>
48 #include <com/sun/star/xml/dom/NodeType.hpp>
49 #include <com/sun/star/xml/dom/events/XEventTarget.hpp>
50 #include <com/sun/star/xml/dom/events/XEventListener.hpp>
51 #include <com/sun/star/lang/XUnoTunnel.hpp>
52 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
53 #include <com/sun/star/container/XNameContainer.hpp>
55 #include <comphelper/servicehelper.hxx>
56 #include <cppuhelper/typeprovider.hxx>
58 using namespace com::sun::star::xml::xpath
;
59 using namespace com::sun::star::xml::dom::events
;
62 using xforms::Binding
;
65 using xforms::getResource
;
66 using xforms::EvaluationContext
;
67 using com::sun::star::beans::XPropertySet
;
68 using com::sun::star::container::XNameAccess
;
69 using com::sun::star::form::binding::IncompatibleTypesException
;
70 using com::sun::star::form::binding::InvalidBindingStateException
;
71 using com::sun::star::form::binding::XValueBinding
;
72 using com::sun::star::lang::EventObject
;
73 using com::sun::star::lang::IndexOutOfBoundsException
;
74 using com::sun::star::lang::XUnoTunnel
;
75 using com::sun::star::uno::Any
;
76 using com::sun::star::uno::Reference
;
77 using com::sun::star::uno::RuntimeException
;
78 using com::sun::star::uno::Sequence
;
79 using com::sun::star::uno::UNO_QUERY
;
80 using com::sun::star::uno::UNO_QUERY_THROW
;
81 using com::sun::star::uno::XInterface
;
82 using com::sun::star::uno::Exception
;
83 using com::sun::star::uno::makeAny
;
84 using com::sun::star::util::XModifyListener
;
85 using com::sun::star::xforms::XDataTypeRepository
;
86 using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE
;
87 using com::sun::star::xml::dom::NodeType_TEXT_NODE
;
88 using com::sun::star::xml::dom::XNode
;
89 using com::sun::star::xml::dom::XNodeList
;
90 using com::sun::star::xml::dom::events::XEventListener
;
91 using com::sun::star::xml::dom::events::XEventTarget
;
92 using com::sun::star::xsd::XDataType
;
95 #define EXCEPT(msg) msg,static_cast<XValueBinding*>(this)
97 #define HANDLE_BindingID 0
98 #define HANDLE_BindingExpression 1
99 #define HANDLE_Model 2
100 #define HANDLE_ModelID 3
101 #define HANDLE_BindingNamespaces 4
102 #define HANDLE_ReadonlyExpression 5
103 #define HANDLE_RelevantExpression 6
104 #define HANDLE_RequiredExpression 7
105 #define HANDLE_ConstraintExpression 8
106 #define HANDLE_CalculateExpression 9
107 #define HANDLE_Type 10
108 #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
109 #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
110 #define HANDLE_ModelNamespaces 13
111 #define HANDLE_ExternalData 14
117 maBindingExpression(),
119 mxNamespaces( new NameContainer
<OUString
>() ),
120 mbInCalculate( false ),
121 mnDeferModifyNotifications( 0 ),
122 mbValueModified( false ),
123 mbBindingModified( false )
126 initializePropertySet();
134 void Binding::_setModel( const css::uno::Reference
<css::xforms::XModel
>& xModel
)
136 PropertyChangeNotifier
aNotifyModelChange( *this, HANDLE_Model
);
137 PropertyChangeNotifier
aNotifyModelIDChange( *this, HANDLE_ModelID
);
139 // prepare binding for removal of old model
140 clear(); // remove all cached data (e.g. XPath evaluation results)
141 css::uno::Reference
<css::container::XNameContainer
> xNamespaces
= getModelNamespaces(); // save namespaces
145 // set namespaces (and move to model, if appropriate)
146 setBindingNamespaces( xNamespaces
);
149 notifyAndCachePropertyValue( HANDLE_ExternalData
);
153 OUString
Binding::getModelID() const
155 Model
* pModel
= getModelImpl();
156 return ( pModel
== nullptr ) ? OUString() : pModel
->getID();
160 css::uno::Reference
<css::xml::dom::XNodeList
> Binding::getXNodeList()
162 // first make sure we are bound
163 if( ! maBindingExpression
.hasValue() )
166 return maBindingExpression
.getXNodeList();
169 bool Binding::isSimpleBinding() const
171 return maBindingExpression
.isSimpleExpression()
172 && maReadonly
.isSimpleExpression()
173 && maRelevant
.isSimpleExpression()
174 && maRequired
.isSimpleExpression()
175 && maConstraint
.isSimpleExpression()
176 && maCalculate
.isSimpleExpression();
179 bool Binding::isSimpleBindingExpression() const
181 return maBindingExpression
.isSimpleExpression();
184 void Binding::update()
186 // clear all expressions (to remove cached node references)
187 maBindingExpression
.clear();
191 maConstraint
.clear();
194 // let's just pretend the binding has been modified -> full rebind()
198 void Binding::deferNotifications( bool bDefer
)
200 mnDeferModifyNotifications
+= ( bDefer
? 1 : -1 );
201 OSL_ENSURE( mnDeferModifyNotifications
>= 0, "you're deferring too much" );
203 if( mnDeferModifyNotifications
== 0 )
205 if( mbBindingModified
)
207 if( mbValueModified
)
211 OSL_ENSURE( ( mnDeferModifyNotifications
> 0 )
212 || ( ! mbBindingModified
&& ! mbValueModified
),
213 "deferred modifications not delivered?" );
216 bool Binding::isValid() const
218 // TODO: determine whether node is suitable, not just whether it exists
219 return maBindingExpression
.getNode().is() &&
220 isValid_DataType() &&
221 maMIP
.isConstraint() &&
222 ( ! maMIP
.isRequired() ||
223 ( maBindingExpression
.hasValue() &&
224 !maBindingExpression
.getString().isEmpty() ) );
227 bool Binding::isUseful() const
230 // 0) we don't have a model
231 // (at least, in this case we shouldn't be removed from the model)
232 // 1) we have a proper name
233 // 2) we have some MIPs,
234 // 3) we are bound to some control
235 // (this can be assumed if some listeners are set)
237 getModelImpl() == nullptr
238 // || msBindingID.getLength() > 0
239 || ! msTypeName
.isEmpty()
240 || ! maReadonly
.isEmptyExpression()
241 || ! maRelevant
.isEmptyExpression()
242 || ! maRequired
.isEmptyExpression()
243 || ! maConstraint
.isEmptyExpression()
244 || ! maCalculate
.isEmptyExpression()
245 || ! maModifyListeners
.empty()
246 || ! maListEntryListeners
.empty()
247 || ! maValidityListeners
.empty();
252 OUString
Binding::explainInvalid()
255 if( ! maBindingExpression
.getNode().is() )
257 sReason
= ( maBindingExpression
.getExpression().isEmpty() )
258 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION
)
259 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION
);
261 else if( ! isValid_DataType() )
263 sReason
= explainInvalid_DataType();
264 if( sReason
.isEmpty() )
266 // no explanation given by data type? Then give generic message
267 sReason
= getResource( RID_STR_XFORMS_INVALID_VALUE
,
268 maMIP
.getTypeName() );
271 else if( ! maMIP
.isConstraint() )
273 sReason
= maMIP
.getConstraintExplanation();
275 else if( maMIP
.isRequired() && maBindingExpression
.hasValue() &&
276 maBindingExpression
.getString().isEmpty() )
278 sReason
= getResource( RID_STR_XFORMS_REQUIRED
);
280 // else: no explanation given; should only happen if data is valid
282 OSL_ENSURE( sReason
.isEmpty() == isValid(),
283 "invalid data should have an explanation!" );
289 EvaluationContext
Binding::getEvaluationContext() const
291 OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
292 EvaluationContext aContext
= getModelImpl()->getEvaluationContext();
293 aContext
.mxNamespaces
= getBindingNamespaces();
297 ::std::vector
<EvaluationContext
> Binding::getMIPEvaluationContexts()
299 OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
301 // bind (in case we were not bound before)
303 return _getMIPEvaluationContexts();
307 css::uno::Sequence
<sal_Int8
> Binding::getUnoTunnelId()
309 static cppu::OImplementationId aImplementationId
;
310 return aImplementationId
.getImplementationId();
314 void Binding::setBindingID( const OUString
& sBindingID
)
316 msBindingID
= sBindingID
;
319 OUString
Binding::getBindingExpression() const
321 return maBindingExpression
.getExpression();
324 void Binding::setBindingExpression( const OUString
& sBindingExpression
)
326 maBindingExpression
.setExpression( sBindingExpression
);
330 OUString
Binding::getReadonlyExpression() const
332 return maReadonly
.getExpression();
335 void Binding::setReadonlyExpression( const OUString
& sReadonly
)
337 maReadonly
.setExpression( sReadonly
);
341 OUString
Binding::getRelevantExpression() const
343 return maRelevant
.getExpression();
346 void Binding::setRelevantExpression( const OUString
& sRelevant
)
348 maRelevant
.setExpression( sRelevant
);
352 OUString
Binding::getRequiredExpression() const
354 return maRequired
.getExpression();
357 void Binding::setRequiredExpression( const OUString
& sRequired
)
359 maRequired
.setExpression( sRequired
);
363 OUString
Binding::getConstraintExpression() const
365 return maConstraint
.getExpression();
368 void Binding::setConstraintExpression( const OUString
& sConstraint
)
370 maConstraint
.setExpression( sConstraint
);
371 msExplainConstraint
= getResource( RID_STR_XFORMS_INVALID_CONSTRAINT
,
374 // TODO: This should only re-evaluate the constraint, and notify
375 // the validity constraint listeners; instead we currently pretend
376 // the entire binding was notified, which does a little too much.
380 OUString
Binding::getCalculateExpression() const
382 return maCalculate
.getExpression();
385 void Binding::setCalculateExpression( const OUString
& sCalculate
)
387 maCalculate
.setExpression( sCalculate
);
392 void Binding::setType( const OUString
& sTypeName
)
394 msTypeName
= sTypeName
;
398 void Binding::setBindingNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
400 _setNamespaces( rNamespaces
, true );
403 css::uno::Reference
<css::container::XNameContainer
> Binding::getModelNamespaces() const
405 return _getNamespaces();
408 void Binding::setModelNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
410 _setNamespaces( rNamespaces
, false );
413 bool Binding::getReadOnly() const
415 return maMIP
.isReadonly();
418 bool Binding::getRelevant() const
420 return maMIP
.isRelevant();
423 bool Binding::getExternalData() const
425 bool bExternalData
= true;
427 return bExternalData
;
431 Reference
< XPropertySet
> xModelProps( mxModel
, UNO_QUERY_THROW
);
433 xModelProps
->getPropertyValue( "ExternalData" ) >>= bExternalData
);
435 catch( const Exception
& )
437 DBG_UNHANDLED_EXCEPTION("forms.xforms");
439 return bExternalData
;
443 void Binding::checkLive()
446 throw RuntimeException( EXCEPT("Binding not initialized") );
449 bool Binding::isLive() const
451 const Model
* pModel
= getModelImpl();
452 return pModel
&& pModel
->isInitialized();
455 Model
* Binding::getModelImpl() const
457 return comphelper::getUnoTunnelImplementation
<Model
>( mxModel
);
460 static void lcl_addListenerToNode( const Reference
<XNode
>& xNode
,
461 const Reference
<XEventListener
>& xListener
)
463 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
467 xTarget
->addEventListener( "DOMCharacterDataModified",
469 xTarget
->addEventListener( "DOMCharacterDataModified",
471 xTarget
->addEventListener( "DOMAttrModified",
473 xTarget
->addEventListener( "DOMAttrModified",
475 xTarget
->addEventListener( "DOMAttrModified",
477 xTarget
->addEventListener( "xforms-generic",
481 static void lcl_removeListenerFromNode( const Reference
<XNode
>& xNode
,
482 const Reference
<XEventListener
>& xListener
)
484 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
488 xTarget
->removeEventListener( "DOMCharacterDataModified",
490 xTarget
->removeEventListener( "DOMCharacterDataModified",
492 xTarget
->removeEventListener( "DOMAttrModified",
494 xTarget
->removeEventListener( "DOMAttrModified",
496 xTarget
->removeEventListener( "xforms-generic",
500 ::std::vector
<EvaluationContext
> Binding::_getMIPEvaluationContexts() const
502 OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
504 // iterate over nodes of bind expression and create
505 // EvaluationContext for each
506 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
507 ::std::vector
<EvaluationContext
> aVector
;
508 for (auto const& node
: aNodes
)
510 OSL_ENSURE( node
.is(), "no node?" );
512 // create proper evaluation context for this MIP
513 aVector
.emplace_back( node
, getModel(), getBindingNamespaces() );
518 void Binding::bind( bool bForceRebind
)
521 throw RuntimeException( EXCEPT("Binding has no Model") );
523 // bind() will evaluate this binding as follows:
524 // 1) evaluate the binding expression
525 // 1b) if necessary, create node according to 'lazy author' rules
526 // 2) register suitable listeners on the instance (and remove old ones)
527 // 3) remove old MIPs defined by this binding
528 // 4) for every node in the binding nodeset do:
529 // 1) create proper evaluation context for this MIP
530 // 2) evaluate calculate expression (and push value into instance)
531 // 3) evaluate remaining MIPs
532 // 4) evaluate the locally defined MIPs, and push them to the model
535 // 1) evaluate the binding expression
536 EvaluationContext aContext
= getEvaluationContext();
537 maBindingExpression
.evaluate( aContext
);
538 if( ! maBindingExpression
.getNode().is() )
540 // 1b) create node (if valid element name)
541 if( isValidQName( maBindingExpression
.getExpression(),
542 aContext
.mxNamespaces
) )
544 aContext
.mxContextNode
->appendChild(
545 aContext
.mxContextNode
->getOwnerDocument()->createElement(
546 maBindingExpression
.getExpression() ) );
547 maBindingExpression
.evaluate( aContext
);
548 OSL_ENSURE( maBindingExpression
.getNode().is(),
549 "we should bind to the newly inserted node!" );
552 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
554 // 2) register suitable listeners on the instance (and remove old ones)
555 if( maEventNodes
.empty() || bForceRebind
)
557 for (auto const& eventNode
: maEventNodes
)
558 lcl_removeListenerFromNode( eventNode
, this );
559 maEventNodes
.clear();
560 if( isSimpleBinding() )
561 for (auto const& node
: aNodes
)
562 maEventNodes
.push_back(node
);
564 maEventNodes
.emplace_back( aContext
.mxContextNode
->getOwnerDocument(),
566 for (auto const& eventNode
: maEventNodes
)
567 lcl_addListenerToNode( eventNode
, this );
570 // 3) remove old MIPs defined by this binding
571 Model
* pModel
= getModelImpl();
572 OSL_ENSURE( pModel
!= nullptr, "need model" );
573 pModel
->removeMIPs( this );
575 // 4) calculate all MIPs
576 ::std::vector
<EvaluationContext
> aMIPContexts
= _getMIPEvaluationContexts();
577 for (auto & context
: aMIPContexts
)
579 EvaluationContext
& rContext
= context
;
581 // evaluate calculate expression (and push value into instance)
582 // (prevent recursion using mbInCalculate
583 if( ! maCalculate
.isEmptyExpression() )
585 if( ! mbInCalculate
)
587 mbInCalculate
= true;
588 maCalculate
.evaluate( rContext
);
589 pModel
->setSimpleContent( rContext
.mxContextNode
,
590 maCalculate
.getString() );
591 mbInCalculate
= false;
595 // now evaluate remaining MIPs in the appropriate context
596 maReadonly
.evaluate( rContext
);
597 maRelevant
.evaluate( rContext
);
598 maRequired
.evaluate( rContext
);
599 maConstraint
.evaluate( rContext
);
600 // type is static; does not need updating
602 // evaluate the locally defined MIPs, and push them to the model
603 pModel
->addMIP( this, rContext
.mxContextNode
, getLocalMIP() );
608 // helper for Binding::valueModified
609 static void lcl_modified( const css::uno::Reference
<css::util::XModifyListener
>& xListener
,
610 const Reference
<XInterface
>& xSource
)
612 OSL_ENSURE( xListener
.is(), "no listener?" );
613 xListener
->modified( EventObject( xSource
) );
616 // helper for Binding::valueModified
617 static void lcl_listentry( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
,
618 const Reference
<XInterface
>& xSource
)
620 OSL_ENSURE( xListener
.is(), "no listener?" );
621 // TODO: send fine granular events
622 xListener
->allEntriesChanged( EventObject( xSource
) );
625 // helper for Binding::valueModified
626 static void lcl_validate( const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
,
627 const Reference
<XInterface
>& xSource
)
629 OSL_ENSURE( xListener
.is(), "no listener?" );
630 xListener
->validityConstraintChanged( EventObject( xSource
) );
634 void Binding::valueModified()
636 // defer notifications, if so desired
637 if( mnDeferModifyNotifications
> 0 )
639 mbValueModified
= true;
642 mbValueModified
= false;
644 // query MIP used by our first node (also note validity)
645 Reference
<XNode
> xNode
= maBindingExpression
.getNode();
646 maMIP
= getModelImpl()->queryMIP( xNode
);
648 // distribute MIPs _used_ by this binding
651 notifyAndCachePropertyValue( HANDLE_ReadOnly
);
652 notifyAndCachePropertyValue( HANDLE_Relevant
);
655 // iterate over _value_ listeners and send each a modified signal,
656 // using this object as source (will also update validity, because
657 // control will query once the value has changed)
658 Reference
<XInterface
> xSource
= static_cast<XPropertySet
*>( this );
659 ::std::for_each( maModifyListeners
.begin(),
660 maModifyListeners
.end(),
661 ::std::bind( lcl_modified
, std::placeholders::_1
, xSource
) );
662 ::std::for_each( maListEntryListeners
.begin(),
663 maListEntryListeners
.end(),
664 ::std::bind( lcl_listentry
, std::placeholders::_1
, xSource
) );
665 ::std::for_each( maValidityListeners
.begin(),
666 maValidityListeners
.end(),
667 ::std::bind( lcl_validate
, std::placeholders::_1
, xSource
) );
669 // now distribute MIPs to children
671 distributeMIP( xNode
->getFirstChild() );
674 void Binding::distributeMIP( const css::uno::Reference
<css::xml::dom::XNode
> & rxNode
) {
676 css::xforms::XFormsEventConcrete
*pEvent
= new css::xforms::XFormsEventConcrete
;
677 pEvent
->initXFormsEvent("xforms-generic", true, false);
678 Reference
<XEvent
> xEvent(pEvent
);
680 // naive depth-first traversal
681 css::uno::Reference
<css::xml::dom::XNode
> xNode( rxNode
);
684 // notifications should be triggered at the
685 // leaf nodes first, bubbling upwards the hierarchy.
686 css::uno::Reference
<css::xml::dom::XNode
> child(xNode
->getFirstChild());
688 distributeMIP(child
);
690 // we're standing at a particular node somewhere
691 // below the one which changed a property (MIP).
692 // bindings which are listening at this node will receive
693 // a notification message about what exactly happened.
694 Reference
< XEventTarget
> target(xNode
,UNO_QUERY
);
695 target
->dispatchEvent(xEvent
);
697 xNode
= xNode
->getNextSibling();
701 void Binding::bindingModified()
703 // defer notifications, if so desired
704 if( mnDeferModifyNotifications
> 0 )
706 mbBindingModified
= true;
709 mbBindingModified
= false;
711 // rebind (if live); then call valueModified
712 // A binding should be inert until its model is fully constructed.
721 MIP
Binding::getLocalMIP() const
725 if( maReadonly
.hasValue() )
726 aMIP
.setReadonly( maReadonly
.getBool() );
727 if( maRelevant
.hasValue() )
728 aMIP
.setRelevant( maRelevant
.getBool( true ) );
729 if( maRequired
.hasValue() )
730 aMIP
.setRequired( maRequired
.getBool() );
731 if( maConstraint
.hasValue() )
733 aMIP
.setConstraint( maConstraint
.getBool( true ) );
734 if( ! aMIP
.isConstraint() )
735 aMIP
.setConstraintExplanation( msExplainConstraint
);
737 if( !msTypeName
.isEmpty() )
738 aMIP
.setTypeName( msTypeName
);
740 // calculate: only handle presence of calculate; value set elsewhere
741 aMIP
.setHasCalculate( !maCalculate
.isEmptyExpression() );
746 css::uno::Reference
<css::xsd::XDataType
> Binding::getDataType() const
748 OSL_ENSURE( getModel().is(), "need model" );
749 OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" );
751 Reference
<XDataTypeRepository
> xRepository
=
752 getModel()->getDataTypeRepository();
753 OUString sTypeName
= maMIP
.getTypeName();
755 return ( xRepository
.is() && xRepository
->hasByName( sTypeName
) )
756 ? Reference
<XDataType
>( xRepository
->getByName( sTypeName
), UNO_QUERY
)
757 : Reference
<XDataType
>( nullptr );
760 bool Binding::isValid_DataType() const
762 Reference
<XDataType
> xDataType
= getDataType();
763 return !xDataType
.is()
764 || xDataType
->validate( maBindingExpression
.getString() );
767 OUString
Binding::explainInvalid_DataType()
769 Reference
<XDataType
> xDataType
= getDataType();
770 return xDataType
.is()
771 ? xDataType
->explainInvalid( maBindingExpression
.getString() )
775 void Binding::clear()
777 // remove MIPs contributed by this binding
778 Model
* pModel
= getModelImpl();
779 if( pModel
!= nullptr )
780 pModel
->removeMIPs( this );
782 // remove all references
783 for (auto const& eventNode
: maEventNodes
)
784 lcl_removeListenerFromNode( eventNode
, this );
785 maEventNodes
.clear();
788 maBindingExpression
.clear();
792 maConstraint
.clear();
795 // TODO: what about our listeners?
799 static void lcl_removeOtherNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& xFrom
,
800 css::uno::Reference
<css::container::XNameContainer
> const & xTo
)
802 OSL_ENSURE( xFrom
.is(), "no source" );
803 OSL_ENSURE( xTo
.is(), "no target" );
805 // iterate over name in source
806 Sequence
<OUString
> aNames
= xTo
->getElementNames();
807 sal_Int32 nNames
= aNames
.getLength();
808 const OUString
* pNames
= aNames
.getConstArray();
809 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
811 const OUString
& rName
= pNames
[i
];
813 if( ! xFrom
->hasByName( rName
) )
814 xTo
->removeByName( rName
);
818 /** copy namespaces from one namespace container into another
819 * @param bOverwrite true: overwrite namespaces in target
820 * false: do not overwrite namespaces in target
821 * @param bMove true: move namespaces (i.e., delete in source)
822 * false: copy namespaces (do not modify source)
823 * @param bFromSource true: use elements from source
824 * false: use only elements from target
826 static void lcl_copyNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& xFrom
,
827 css::uno::Reference
<css::container::XNameContainer
> const & xTo
,
830 OSL_ENSURE( xFrom
.is(), "no source" );
831 OSL_ENSURE( xTo
.is(), "no target" );
833 // iterate over name in source
834 Sequence
<OUString
> aNames
= xFrom
->getElementNames();
835 sal_Int32 nNames
= aNames
.getLength();
836 const OUString
* pNames
= aNames
.getConstArray();
837 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
839 const OUString
& rName
= pNames
[i
];
841 // determine whether to copy the value, and whether to delete
844 bool bInTarget
= xTo
->hasByName( rName
);
846 // we copy: if property is in target, and
847 // if bOverwrite is set, or when the namespace prefix is free
848 bool bCopy
= bOverwrite
|| ! bInTarget
;
850 // and now... ACTION!
854 xTo
->replaceByName( rName
, xFrom
->getByName( rName
) );
856 xTo
->insertByName( rName
, xFrom
->getByName( rName
) );
861 // implement get*Namespaces()
862 // (identical for both variants)
863 css::uno::Reference
<css::container::XNameContainer
> Binding::_getNamespaces() const
865 css::uno::Reference
<css::container::XNameContainer
> xNamespaces
= new NameContainer
<OUString
>();
866 lcl_copyNamespaces( mxNamespaces
, xNamespaces
, true );
868 // merge model's with binding's own namespaces
869 Model
* pModel
= getModelImpl();
870 if( pModel
!= nullptr )
871 lcl_copyNamespaces( pModel
->getNamespaces(), xNamespaces
, false );
876 // implement set*Namespaces()
877 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
878 void Binding::_setNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
,
881 Model
* pModel
= getModelImpl();
882 css::uno::Reference
<css::container::XNameContainer
> xModelNamespaces
= ( pModel
!= nullptr )
883 ? pModel
->getNamespaces()
885 OSL_ENSURE( ( pModel
!= nullptr ) == xModelNamespaces
.is(), "no model nmsp?");
887 // remove deleted namespaces
888 lcl_removeOtherNamespaces( rNamespaces
, mxNamespaces
);
889 if( !bBinding
&& xModelNamespaces
.is() )
890 lcl_removeOtherNamespaces( rNamespaces
, xModelNamespaces
);
892 // copy namespaces as appropriate
893 Sequence
<OUString
> aNames
= rNamespaces
->getElementNames();
894 sal_Int32 nNames
= aNames
.getLength();
895 const OUString
* pNames
= aNames
.getConstArray();
896 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
898 const OUString
& rName
= pNames
[i
];
899 Any aValue
= rNamespaces
->getByName( rName
);
901 // determine whether the namespace should go into model's or
902 // into binding's namespaces
904 ! xModelNamespaces
.is()
905 || mxNamespaces
->hasByName( rName
)
907 && xModelNamespaces
.is()
908 && xModelNamespaces
->hasByName( rName
) );
910 // write namespace into the appropriate namespace container
911 css::uno::Reference
<css::container::XNameContainer
>& rWhich
= bLocal
? mxNamespaces
: xModelNamespaces
;
912 OSL_ENSURE( rWhich
.is(), "whoops" );
913 if( rWhich
->hasByName( rName
) )
914 rWhich
->replaceByName( rName
, aValue
);
916 rWhich
->insertByName( rName
, aValue
);
918 // always 'promote' namespaces from binding to model, if equal
919 if( xModelNamespaces
.is()
920 && xModelNamespaces
->hasByName( rName
)
921 && mxNamespaces
->hasByName( rName
)
922 && xModelNamespaces
->getByName( rName
) == mxNamespaces
->getByName( rName
) )
924 mxNamespaces
->removeByName( rName
);
928 // ... done. But we modified the binding!
932 void Binding::_checkBindingID()
934 if( !getModel().is() )
937 Reference
<XNameAccess
> xBindings( getModel()->getBindings(), UNO_QUERY_THROW
);
938 if( !msBindingID
.isEmpty() )
941 // no binding ID? then make one up!
942 OUString sIDPrefix
= getResource( RID_STR_XFORMS_BINDING_UI_NAME
) + " ";
943 sal_Int32 nNumber
= 0;
948 sName
= sIDPrefix
+ OUString::number( nNumber
);
950 while( xBindings
->hasByName( sName
) );
951 setBindingID( sName
);
958 css::uno::Sequence
<css::uno::Type
> Binding::getSupportedValueTypes()
960 return Convert::get().getTypes();
963 sal_Bool
Binding::supportsType( const css::uno::Type
& rType
)
965 return Convert::get().hasType( rType
);
968 css::uno::Any
Binding::getValue( const css::uno::Type
& rType
)
970 // first, check for model
973 // second, check for type
974 if( ! supportsType( rType
) )
975 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
977 // return string value (if present; else return empty Any)
978 css::uno::Any result
;
979 if(maBindingExpression
.hasValue()) {
980 OUString
pathExpr(maBindingExpression
.getString());
981 Convert
&rConvert
= Convert::get();
982 result
= rConvert
.toAny(pathExpr
,rType
);
988 void Binding::setValue( const css::uno::Any
& aValue
)
990 // first, check for model
993 // check for supported type
994 if( ! supportsType( aValue
.getValueType() ) )
995 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) );
997 if( !maBindingExpression
.hasValue() )
998 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1000 css::uno::Reference
<css::xml::dom::XNode
> xNode
= maBindingExpression
.getNode();
1002 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1004 OUString sValue
= Convert::get().toXSD( aValue
);
1005 bool bSuccess
= getModelImpl()->setSimpleContent( xNode
, sValue
);
1007 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1013 // XListEntry Source
1016 sal_Int32
Binding::getListEntryCount()
1018 // first, check for model
1021 // return size of node list
1022 return maBindingExpression
.getNodeList().size();
1025 static void lcl_getString( const Reference
<XNode
>& xNode
, OUStringBuffer
& rBuffer
)
1027 if( xNode
->getNodeType() == NodeType_TEXT_NODE
1028 || xNode
->getNodeType() == NodeType_ATTRIBUTE_NODE
)
1030 rBuffer
.append( xNode
->getNodeValue() );
1034 for( Reference
<XNode
> xChild
= xNode
->getFirstChild();
1036 xChild
= xChild
->getNextSibling() )
1038 lcl_getString( xChild
, rBuffer
);
1043 static OUString
lcl_getString( const Reference
<XNode
>& xNode
)
1045 OUStringBuffer aBuffer
;
1046 lcl_getString( xNode
, aBuffer
);
1047 return aBuffer
.makeStringAndClear();
1050 OUString
Binding::getListEntry( sal_Int32 nPosition
)
1052 // first, check for model
1055 // check bounds and return proper item
1056 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
1057 if( nPosition
< 0 || nPosition
>= static_cast<sal_Int32
>( aNodes
.size() ) )
1058 throw IndexOutOfBoundsException( EXCEPT("") );
1059 return lcl_getString( aNodes
[ nPosition
] );
1062 Sequence
<OUString
> Binding::getAllListEntries()
1064 // first, check for model
1067 // create sequence of string values
1068 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
1069 Sequence
<OUString
> aSequence( aNodes
.size() );
1070 OUString
* pSequence
= aSequence
.getArray();
1071 for( sal_Int32 n
= 0; n
< aSequence
.getLength(); n
++ )
1073 pSequence
[n
] = lcl_getString( aNodes
[n
] );
1079 void Binding::addListEntryListener( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
)
1081 OSL_ENSURE( xListener
.is(), "need listener!" );
1082 if( ::std::find( maListEntryListeners
.begin(),
1083 maListEntryListeners
.end(),
1085 == maListEntryListeners
.end() )
1086 maListEntryListeners
.push_back( xListener
);
1089 void Binding::removeListEntryListener( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
)
1091 XListEntryListeners_t::iterator aIter
=
1092 ::std::find( maListEntryListeners
.begin(), maListEntryListeners
.end(),
1094 if( aIter
!= maListEntryListeners
.end() )
1095 maListEntryListeners
.erase( aIter
);
1102 sal_Bool
Binding::isValid( const css::uno::Any
& )
1104 // first, check for model
1107 // ignore value; determine validate only on current data
1111 OUString
Binding::explainInvalid(
1112 const css::uno::Any
& /*Value*/ )
1114 // first, check for model
1117 // ignore value; determine explanation only on current data
1118 return explainInvalid();
1121 void Binding::addValidityConstraintListener(
1122 const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
)
1124 OSL_ENSURE( xListener
.is(), "need listener!" );
1125 if( ::std::find(maValidityListeners
.begin(), maValidityListeners
.end(), xListener
)
1126 == maValidityListeners
.end() )
1127 maValidityListeners
.push_back( xListener
);
1130 void Binding::removeValidityConstraintListener(
1131 const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
)
1133 XValidityConstraintListeners_t::iterator aIter
=
1134 ::std::find( maValidityListeners
.begin(), maValidityListeners
.end(),
1136 if( aIter
!= maValidityListeners
.end() )
1137 maValidityListeners
.erase( aIter
);
1141 // xml::dom::event::XEventListener
1144 void Binding::handleEvent( const css::uno::Reference
<css::xml::dom::events::XEvent
>& xEvent
)
1146 OUString
sType(xEvent
->getType());
1147 //OUString sEventMIPChanged("xforms-generic");
1148 //if(sType.equals(sEventMIPChanged)) {
1149 if(sType
== "xforms-generic") {
1151 // the modification of the 'mnDeferModifyNotifications'-member
1152 // is necessary to prevent infinite notification looping.
1153 // This can happened in case the binding which caused
1154 // the notification chain is listening to those events
1156 bool bPreserveValueModified
= mbValueModified
;
1157 mnDeferModifyNotifications
++;
1159 --mnDeferModifyNotifications
;
1160 mbValueModified
= bPreserveValueModified
;
1164 // if we're a dynamic binding, we better re-bind, too!
1167 // our value was maybe modified
1175 sal_Int64
Binding::getSomething( const css::uno::Sequence
<sal_Int8
>& xId
)
1177 return reinterpret_cast<sal_Int64
>( ( xId
== getUnoTunnelId() ) ? this : nullptr );
1184 css::uno::Reference
<css::util::XCloneable
> SAL_CALL
Binding::createClone()
1186 Reference
< XPropertySet
> xClone
;
1188 Model
* pModel
= getModelImpl();
1190 xClone
= pModel
->cloneBinding( this );
1193 xClone
= new Binding
;
1194 copy( this, xClone
);
1196 return css::uno::Reference
<css::util::XCloneable
>( xClone
, UNO_QUERY
);
1200 // property set implementations
1203 #define REGISTER_PROPERTY( property, type ) \
1204 registerProperty( PROPERTY( property, type ), \
1205 new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) );
1207 #define REGISTER_PROPERTY_RO( property, type ) \
1208 registerProperty( PROPERTY_RO( property, type ), \
1209 new DirectPropertyAccessor< Binding, type >( this, nullptr, &Binding::get##property ) );
1211 #define REGISTER_BOOL_PROPERTY_RO( property ) \
1212 registerProperty( PROPERTY_RO( property, sal_Bool ), \
1213 new BooleanPropertyAccessor< Binding >( this, nullptr, &Binding::get##property ) );
1215 void Binding::initializePropertySet()
1217 REGISTER_PROPERTY ( BindingID
, OUString
);
1218 REGISTER_PROPERTY ( BindingExpression
, OUString
);
1219 REGISTER_PROPERTY_RO ( Model
, css::uno::Reference
<css::xforms::XModel
> );
1220 REGISTER_PROPERTY ( BindingNamespaces
, css::uno::Reference
<css::container::XNameContainer
> );
1221 REGISTER_PROPERTY ( ModelNamespaces
, css::uno::Reference
<css::container::XNameContainer
> );
1222 REGISTER_PROPERTY_RO ( ModelID
, OUString
);
1223 REGISTER_PROPERTY ( ReadonlyExpression
, OUString
);
1224 REGISTER_PROPERTY ( RelevantExpression
, OUString
);
1225 REGISTER_PROPERTY ( RequiredExpression
, OUString
);
1226 REGISTER_PROPERTY ( ConstraintExpression
, OUString
);
1227 REGISTER_PROPERTY ( CalculateExpression
, OUString
);
1228 REGISTER_PROPERTY ( Type
, OUString
);
1229 REGISTER_PROPERTY_RO ( ReadOnly
, bool );
1230 REGISTER_PROPERTY_RO ( Relevant
, bool );
1231 REGISTER_BOOL_PROPERTY_RO( ExternalData
);
1233 initializePropertyValueCache( HANDLE_ReadOnly
);
1234 initializePropertyValueCache( HANDLE_Relevant
);
1235 initializePropertyValueCache( HANDLE_ExternalData
);
1238 void Binding::addModifyListener(
1239 const css::uno::Reference
<css::util::XModifyListener
>& xListener
)
1241 OSL_ENSURE( xListener
.is(), "need listener!" );
1242 if( ::std::find( maModifyListeners
.begin(), maModifyListeners
.end(), xListener
)
1243 == maModifyListeners
.end() )
1244 maModifyListeners
.push_back( xListener
);
1246 // HACK: currently, we have to 'push' some MIPs to the control
1247 // (read-only, relevant, etc.) To enable this, we need to update
1248 // the control at least once when it registers here.
1252 void Binding::removeModifyListener(
1253 const css::uno::Reference
<css::util::XModifyListener
>& xListener
)
1255 ModifyListeners_t::iterator aIter
=
1256 ::std::find( maModifyListeners
.begin(), maModifyListeners
.end(), xListener
);
1257 if( aIter
!= maModifyListeners
.end() )
1258 maModifyListeners
.erase( aIter
);
1262 OUString
Binding::getName()
1264 return getBindingID();
1267 void SAL_CALL
Binding::setName( const OUString
& rName
)
1269 // use the XPropertySet methods, so the change in the name is notified to the
1270 // property listeners
1271 setFastPropertyValue( HANDLE_BindingID
, makeAny( rName
) );
1274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */