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 <o3tl/safeint.hxx>
35 #include <osl/diagnose.h>
37 #include <comphelper/diagnose_ex.hxx>
42 #include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
43 #include <com/sun/star/form/binding/InvalidBindingStateException.hpp>
44 #include <com/sun/star/uno/Any.hxx>
45 #include <com/sun/star/xml/dom/XNodeList.hpp>
46 #include <com/sun/star/xml/dom/XNode.hpp>
47 #include <com/sun/star/xml/dom/XDocument.hpp>
48 #include <com/sun/star/xml/dom/XElement.hpp>
49 #include <com/sun/star/xml/dom/NodeType.hpp>
50 #include <com/sun/star/xml/dom/events/XEventTarget.hpp>
51 #include <com/sun/star/xml/dom/events/XEventListener.hpp>
52 #include <com/sun/star/lang/XUnoTunnel.hpp>
53 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
54 #include <com/sun/star/container/XNameContainer.hpp>
56 #include <comphelper/servicehelper.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::uno::Any
;
75 using com::sun::star::uno::Reference
;
76 using com::sun::star::uno::RuntimeException
;
77 using com::sun::star::uno::Sequence
;
78 using com::sun::star::uno::UNO_QUERY
;
79 using com::sun::star::uno::UNO_QUERY_THROW
;
80 using com::sun::star::uno::XInterface
;
81 using com::sun::star::uno::Exception
;
82 using com::sun::star::util::XModifyListener
;
83 using com::sun::star::xforms::XDataTypeRepository
;
84 using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE
;
85 using com::sun::star::xml::dom::NodeType_TEXT_NODE
;
86 using com::sun::star::xml::dom::XNode
;
87 using com::sun::star::xml::dom::XNodeList
;
88 using com::sun::star::xml::dom::events::XEventListener
;
89 using com::sun::star::xml::dom::events::XEventTarget
;
90 using com::sun::star::xsd::XDataType
;
92 #define HANDLE_BindingID 0
93 #define HANDLE_BindingExpression 1
94 #define HANDLE_Model 2
95 #define HANDLE_ModelID 3
96 #define HANDLE_BindingNamespaces 4
97 #define HANDLE_ReadonlyExpression 5
98 #define HANDLE_RelevantExpression 6
99 #define HANDLE_RequiredExpression 7
100 #define HANDLE_ConstraintExpression 8
101 #define HANDLE_CalculateExpression 9
102 #define HANDLE_Type 10
103 #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
104 #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
105 #define HANDLE_ModelNamespaces 13
106 #define HANDLE_ExternalData 14
110 mxNamespaces( new NameContainer
<OUString
>() ),
111 mbInCalculate( false ),
112 mnDeferModifyNotifications( 0 ),
113 mbValueModified( false ),
114 mbBindingModified( false )
116 initializePropertySet();
124 void Binding::_setModel( const rtl::Reference
<Model
>& xModel
)
126 PropertyChangeNotifier
aNotifyModelChange( *this, HANDLE_Model
);
127 PropertyChangeNotifier
aNotifyModelIDChange( *this, HANDLE_ModelID
);
129 // prepare binding for removal of old model
130 clear(); // remove all cached data (e.g. XPath evaluation results)
131 css::uno::Reference
<css::container::XNameContainer
> xNamespaces
= getModelNamespaces(); // save namespaces
135 // set namespaces (and move to model, if appropriate)
136 setBindingNamespaces( xNamespaces
);
139 notifyAndCachePropertyValue( HANDLE_ExternalData
);
143 OUString
Binding::getModelID() const
145 return ( mxModel
== nullptr ) ? OUString() : mxModel
->getID();
149 css::uno::Reference
<css::xml::dom::XNodeList
> Binding::getXNodeList()
151 // first make sure we are bound
152 if( ! maBindingExpression
.hasValue() )
155 return maBindingExpression
.getXNodeList();
158 bool Binding::isSimpleBinding() const
160 return maBindingExpression
.isSimpleExpression()
161 && maReadonly
.isSimpleExpression()
162 && maRelevant
.isSimpleExpression()
163 && maRequired
.isSimpleExpression()
164 && maConstraint
.isSimpleExpression()
165 && maCalculate
.isSimpleExpression();
168 bool Binding::isSimpleBindingExpression() const
170 return maBindingExpression
.isSimpleExpression();
173 void Binding::update()
175 // clear all expressions (to remove cached node references)
176 maBindingExpression
.clear();
180 maConstraint
.clear();
183 // let's just pretend the binding has been modified -> full rebind()
187 void Binding::deferNotifications( bool bDefer
)
189 mnDeferModifyNotifications
+= ( bDefer
? 1 : -1 );
190 OSL_ENSURE( mnDeferModifyNotifications
>= 0, "you're deferring too much" );
192 if( mnDeferModifyNotifications
== 0 )
194 if( mbBindingModified
)
196 if( mbValueModified
)
200 OSL_ENSURE( ( mnDeferModifyNotifications
> 0 )
201 || ( ! mbBindingModified
&& ! mbValueModified
),
202 "deferred modifications not delivered?" );
205 bool Binding::isValid() const
207 // TODO: determine whether node is suitable, not just whether it exists
208 return maBindingExpression
.getNode().is() &&
210 // tdf#155121, validity rules should be apply when field is required or
211 // when the field is not required but not empty
212 // so if the field is not required and empty, do not check validity
213 (! maMIP
.isRequired() && maBindingExpression
.hasValue()
214 && maBindingExpression
.getString().isEmpty() ) ||
217 maMIP
.isConstraint() &&
218 ( ! maMIP
.isRequired() ||
219 ( maBindingExpression
.hasValue() &&
220 !maBindingExpression
.getString().isEmpty() ) );
223 bool Binding::isUseful() const
226 // 0) we don't have a model
227 // (at least, in this case we shouldn't be removed from the model)
228 // 1) we have a proper name
229 // 2) we have some MIPs,
230 // 3) we are bound to some control
231 // (this can be assumed if some listeners are set)
234 // || msBindingID.getLength() > 0
235 || ! msTypeName
.isEmpty()
236 || ! maReadonly
.isEmptyExpression()
237 || ! maRelevant
.isEmptyExpression()
238 || ! maRequired
.isEmptyExpression()
239 || ! maConstraint
.isEmptyExpression()
240 || ! maCalculate
.isEmptyExpression()
241 || ! maModifyListeners
.empty()
242 || ! maListEntryListeners
.empty()
243 || ! maValidityListeners
.empty();
248 OUString
Binding::explainInvalid()
251 if( ! maBindingExpression
.getNode().is() )
253 sReason
= ( maBindingExpression
.getExpression().isEmpty() )
254 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION
)
255 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION
);
257 else if( ! isValid_DataType() )
259 sReason
= explainInvalid_DataType();
260 if( sReason
.isEmpty() )
262 // no explanation given by data type? Then give generic message
263 sReason
= getResource( RID_STR_XFORMS_INVALID_VALUE
,
264 maMIP
.getTypeName() );
267 else if( ! maMIP
.isConstraint() )
269 sReason
= maMIP
.getConstraintExplanation();
271 else if( maMIP
.isRequired() && maBindingExpression
.hasValue() &&
272 maBindingExpression
.getString().isEmpty() )
274 sReason
= getResource( RID_STR_XFORMS_REQUIRED
);
276 // else: no explanation given; should only happen if data is valid
278 OSL_ENSURE( sReason
.isEmpty() == isValid(),
279 "invalid data should have an explanation!" );
285 EvaluationContext
Binding::getEvaluationContext() const
287 OSL_ENSURE( mxModel
!= nullptr, "need model impl" );
288 EvaluationContext aContext
= mxModel
->getEvaluationContext();
289 aContext
.mxNamespaces
= getBindingNamespaces();
293 ::std::vector
<EvaluationContext
> Binding::getMIPEvaluationContexts()
295 OSL_ENSURE( mxModel
!= nullptr, "need model impl" );
297 // bind (in case we were not bound before)
299 return _getMIPEvaluationContexts();
303 css::uno::Sequence
<sal_Int8
> Binding::getUnoTunnelId()
305 static const comphelper::UnoIdInit aImplementationId
;
306 return aImplementationId
.getSeq();
310 void Binding::setBindingID( const OUString
& sBindingID
)
312 msBindingID
= sBindingID
;
315 OUString
Binding::getBindingExpression() const
317 return maBindingExpression
.getExpression();
320 void Binding::setBindingExpression( const OUString
& sBindingExpression
)
322 maBindingExpression
.setExpression( sBindingExpression
);
326 OUString
Binding::getReadonlyExpression() const
328 return maReadonly
.getExpression();
331 void Binding::setReadonlyExpression( const OUString
& sReadonly
)
333 maReadonly
.setExpression( sReadonly
);
337 OUString
Binding::getRelevantExpression() const
339 return maRelevant
.getExpression();
342 void Binding::setRelevantExpression( const OUString
& sRelevant
)
344 maRelevant
.setExpression( sRelevant
);
348 OUString
Binding::getRequiredExpression() const
350 return maRequired
.getExpression();
353 void Binding::setRequiredExpression( const OUString
& sRequired
)
355 maRequired
.setExpression( sRequired
);
359 OUString
Binding::getConstraintExpression() const
361 return maConstraint
.getExpression();
364 void Binding::setConstraintExpression( const OUString
& sConstraint
)
366 maConstraint
.setExpression( sConstraint
);
367 msExplainConstraint
= getResource( RID_STR_XFORMS_INVALID_CONSTRAINT
,
370 // TODO: This should only re-evaluate the constraint, and notify
371 // the validity constraint listeners; instead we currently pretend
372 // the entire binding was notified, which does a little too much.
376 OUString
Binding::getCalculateExpression() const
378 return maCalculate
.getExpression();
381 void Binding::setCalculateExpression( const OUString
& sCalculate
)
383 maCalculate
.setExpression( sCalculate
);
388 void Binding::setType( const OUString
& sTypeName
)
390 msTypeName
= sTypeName
;
394 void Binding::setBindingNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
396 _setNamespaces( rNamespaces
, true );
399 css::uno::Reference
<css::container::XNameContainer
> Binding::getModelNamespaces() const
401 return _getNamespaces();
404 void Binding::setModelNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
406 _setNamespaces( rNamespaces
, false );
409 bool Binding::getReadOnly() const
411 return maMIP
.isReadonly();
414 bool Binding::getRelevant() const
416 return maMIP
.isRelevant();
419 bool Binding::getExternalData() const
421 bool bExternalData
= true;
423 return bExternalData
;
428 mxModel
->getPropertyValue( u
"ExternalData"_ustr
) >>= bExternalData
);
430 catch( const Exception
& )
432 DBG_UNHANDLED_EXCEPTION("forms.xforms");
434 return bExternalData
;
438 void Binding::checkLive()
441 throw RuntimeException(u
"Binding not initialized"_ustr
, static_cast<XValueBinding
*>(this));
444 bool Binding::isLive() const
446 return mxModel
&& mxModel
->isInitialized();
450 static void lcl_addListenerToNode( const Reference
<XNode
>& xNode
,
451 const Reference
<XEventListener
>& xListener
)
453 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
457 xTarget
->addEventListener( u
"DOMCharacterDataModified"_ustr
,
459 xTarget
->addEventListener( u
"DOMCharacterDataModified"_ustr
,
461 xTarget
->addEventListener( u
"DOMAttrModified"_ustr
,
463 xTarget
->addEventListener( u
"DOMAttrModified"_ustr
,
465 xTarget
->addEventListener( u
"xforms-generic"_ustr
,
469 static void lcl_removeListenerFromNode( const Reference
<XNode
>& xNode
,
470 const Reference
<XEventListener
>& xListener
)
472 Reference
<XEventTarget
> xTarget( xNode
, UNO_QUERY
);
476 xTarget
->removeEventListener( u
"DOMCharacterDataModified"_ustr
,
478 xTarget
->removeEventListener( u
"DOMCharacterDataModified"_ustr
,
480 xTarget
->removeEventListener( u
"DOMAttrModified"_ustr
,
482 xTarget
->removeEventListener( u
"DOMAttrModified"_ustr
,
484 xTarget
->removeEventListener( u
"xforms-generic"_ustr
,
488 ::std::vector
<EvaluationContext
> Binding::_getMIPEvaluationContexts() const
490 OSL_ENSURE( mxModel
!= nullptr, "need model impl" );
492 // iterate over nodes of bind expression and create
493 // EvaluationContext for each
494 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
495 ::std::vector
<EvaluationContext
> aVector
;
496 for (auto const& node
: aNodes
)
498 OSL_ENSURE( node
.is(), "no node?" );
500 // create proper evaluation context for this MIP
501 aVector
.emplace_back( node
, mxModel
, getBindingNamespaces() );
506 void Binding::bind( bool bForceRebind
)
509 throw RuntimeException(u
"Binding has no Model"_ustr
, static_cast<XValueBinding
*>(this));
511 // bind() will evaluate this binding as follows:
512 // 1) evaluate the binding expression
513 // 1b) if necessary, create node according to 'lazy author' rules
514 // 2) register suitable listeners on the instance (and remove old ones)
515 // 3) remove old MIPs defined by this binding
516 // 4) for every node in the binding nodeset do:
517 // 1) create proper evaluation context for this MIP
518 // 2) evaluate calculate expression (and push value into instance)
519 // 3) evaluate remaining MIPs
520 // 4) evaluate the locally defined MIPs, and push them to the model
523 // 1) evaluate the binding expression
524 EvaluationContext aContext
= getEvaluationContext();
525 maBindingExpression
.evaluate( aContext
);
526 if( ! maBindingExpression
.getNode().is() )
528 // 1b) create node (if valid element name)
529 if( isValidQName( maBindingExpression
.getExpression(),
530 aContext
.mxNamespaces
) )
532 aContext
.mxContextNode
->appendChild(
533 aContext
.mxContextNode
->getOwnerDocument()->createElement(
534 maBindingExpression
.getExpression() ) );
535 maBindingExpression
.evaluate( aContext
);
536 OSL_ENSURE( maBindingExpression
.getNode().is(),
537 "we should bind to the newly inserted node!" );
540 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
542 // 2) register suitable listeners on the instance (and remove old ones)
543 if( maEventNodes
.empty() || bForceRebind
)
545 for (auto const& eventNode
: maEventNodes
)
546 lcl_removeListenerFromNode( eventNode
, this );
547 maEventNodes
.clear();
548 if( isSimpleBinding() )
549 maEventNodes
.insert(maEventNodes
.end(), aNodes
.begin(), aNodes
.end());
551 maEventNodes
.emplace_back( aContext
.mxContextNode
->getOwnerDocument(),
553 for (auto const& eventNode
: maEventNodes
)
554 lcl_addListenerToNode( eventNode
, this );
557 // 3) remove old MIPs defined by this binding
558 OSL_ENSURE( mxModel
!= nullptr, "need model" );
559 mxModel
->removeMIPs( this );
561 // 4) calculate all MIPs
562 ::std::vector
<EvaluationContext
> aMIPContexts
= _getMIPEvaluationContexts();
563 for (auto & context
: aMIPContexts
)
565 EvaluationContext
& rContext
= context
;
567 // evaluate calculate expression (and push value into instance)
568 // (prevent recursion using mbInCalculate
569 if( ! maCalculate
.isEmptyExpression() )
571 if( ! mbInCalculate
)
573 mbInCalculate
= true;
574 maCalculate
.evaluate( rContext
);
575 mxModel
->setSimpleContent( rContext
.mxContextNode
,
576 maCalculate
.getString() );
577 mbInCalculate
= false;
581 // now evaluate remaining MIPs in the appropriate context
582 maReadonly
.evaluate( rContext
);
583 maRelevant
.evaluate( rContext
);
584 maRequired
.evaluate( rContext
);
585 maConstraint
.evaluate( rContext
);
586 // type is static; does not need updating
588 // evaluate the locally defined MIPs, and push them to the model
589 mxModel
->addMIP( this, rContext
.mxContextNode
, getLocalMIP() );
594 // helper for Binding::valueModified
595 static void lcl_modified( const css::uno::Reference
<css::util::XModifyListener
>& xListener
,
596 const Reference
<XInterface
>& xSource
)
598 OSL_ENSURE( xListener
.is(), "no listener?" );
599 xListener
->modified( EventObject( xSource
) );
602 // helper for Binding::valueModified
603 static void lcl_listentry( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
,
604 const Reference
<XInterface
>& xSource
)
606 OSL_ENSURE( xListener
.is(), "no listener?" );
607 // TODO: send fine granular events
608 xListener
->allEntriesChanged( EventObject( xSource
) );
611 // helper for Binding::valueModified
612 static void lcl_validate( const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
,
613 const Reference
<XInterface
>& xSource
)
615 OSL_ENSURE( xListener
.is(), "no listener?" );
616 xListener
->validityConstraintChanged( EventObject( xSource
) );
620 void Binding::valueModified()
622 // defer notifications, if so desired
623 if( mnDeferModifyNotifications
> 0 )
625 mbValueModified
= true;
628 mbValueModified
= false;
630 // query MIP used by our first node (also note validity)
631 Reference
<XNode
> xNode
= maBindingExpression
.getNode();
632 maMIP
= mxModel
->queryMIP( xNode
);
634 // distribute MIPs _used_ by this binding
637 notifyAndCachePropertyValue( HANDLE_ReadOnly
);
638 notifyAndCachePropertyValue( HANDLE_Relevant
);
641 // iterate over _value_ listeners and send each a modified signal,
642 // using this object as source (will also update validity, because
643 // control will query once the value has changed)
644 Reference
<XInterface
> xSource
= static_cast<XPropertySet
*>( this );
645 ::std::for_each( maModifyListeners
.begin(),
646 maModifyListeners
.end(),
647 ::std::bind( lcl_modified
, std::placeholders::_1
, xSource
) );
648 ::std::for_each( maListEntryListeners
.begin(),
649 maListEntryListeners
.end(),
650 ::std::bind( lcl_listentry
, std::placeholders::_1
, xSource
) );
651 ::std::for_each( maValidityListeners
.begin(),
652 maValidityListeners
.end(),
653 ::std::bind( lcl_validate
, std::placeholders::_1
, xSource
) );
655 // now distribute MIPs to children
657 distributeMIP( xNode
->getFirstChild() );
660 void Binding::distributeMIP( const css::uno::Reference
<css::xml::dom::XNode
> & rxNode
) {
662 rtl::Reference
<css::xforms::XFormsEventConcrete
> pEvent
= new css::xforms::XFormsEventConcrete
;
663 pEvent
->initXFormsEvent(u
"xforms-generic"_ustr
, true, false);
665 // naive depth-first traversal
666 css::uno::Reference
<css::xml::dom::XNode
> xNode( rxNode
);
669 // notifications should be triggered at the
670 // leaf nodes first, bubbling upwards the hierarchy.
671 css::uno::Reference
<css::xml::dom::XNode
> child(xNode
->getFirstChild());
673 distributeMIP(child
);
675 // we're standing at a particular node somewhere
676 // below the one which changed a property (MIP).
677 // bindings which are listening at this node will receive
678 // a notification message about what exactly happened.
679 Reference
< XEventTarget
> target(xNode
,UNO_QUERY
);
680 target
->dispatchEvent(pEvent
);
682 xNode
= xNode
->getNextSibling();
686 void Binding::bindingModified()
688 // defer notifications, if so desired
689 if( mnDeferModifyNotifications
> 0 )
691 mbBindingModified
= true;
694 mbBindingModified
= false;
696 // rebind (if live); then call valueModified
697 // A binding should be inert until its model is fully constructed.
706 MIP
Binding::getLocalMIP() const
710 if( maReadonly
.hasValue() )
711 aMIP
.setReadonly( maReadonly
.getBool() );
712 if( maRelevant
.hasValue() )
713 aMIP
.setRelevant( maRelevant
.getBool( true ) );
714 if( maRequired
.hasValue() )
715 aMIP
.setRequired( maRequired
.getBool() );
716 if( maConstraint
.hasValue() )
718 aMIP
.setConstraint( maConstraint
.getBool( true ) );
719 if( ! aMIP
.isConstraint() )
720 aMIP
.setConstraintExplanation( msExplainConstraint
);
722 if( !msTypeName
.isEmpty() )
723 aMIP
.setTypeName( msTypeName
);
725 // calculate: only handle presence of calculate; value set elsewhere
726 aMIP
.setHasCalculate( !maCalculate
.isEmptyExpression() );
731 css::uno::Reference
<css::xsd::XDataType
> Binding::getDataType() const
733 OSL_ENSURE( mxModel
.is(), "need model" );
734 OSL_ENSURE( mxModel
->getDataTypeRepository().is(), "need types" );
736 Reference
<XDataTypeRepository
> xRepository
=
737 mxModel
->getDataTypeRepository();
738 OUString sTypeName
= maMIP
.getTypeName();
740 return ( xRepository
.is() && xRepository
->hasByName( sTypeName
) )
741 ? Reference
<XDataType
>( xRepository
->getByName( sTypeName
), UNO_QUERY
)
742 : Reference
<XDataType
>( nullptr );
745 bool Binding::isValid_DataType() const
747 Reference
<XDataType
> xDataType
= getDataType();
748 return !xDataType
.is()
749 || xDataType
->validate( maBindingExpression
.getString() );
752 OUString
Binding::explainInvalid_DataType()
754 Reference
<XDataType
> xDataType
= getDataType();
755 return xDataType
.is()
756 ? xDataType
->explainInvalid( maBindingExpression
.getString() )
760 void Binding::clear()
762 // remove MIPs contributed by this binding
763 if( mxModel
!= nullptr )
764 mxModel
->removeMIPs( this );
766 // remove all references
767 for (auto const& eventNode
: maEventNodes
)
768 lcl_removeListenerFromNode( eventNode
, this );
769 maEventNodes
.clear();
772 maBindingExpression
.clear();
776 maConstraint
.clear();
779 // TODO: what about our listeners?
783 static void lcl_removeOtherNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& xFrom
,
784 css::uno::Reference
<css::container::XNameContainer
> const & xTo
)
786 OSL_ENSURE( xFrom
.is(), "no source" );
787 OSL_ENSURE( xTo
.is(), "no target" );
789 // iterate over name in source
790 Sequence
<OUString
> aNames
= xTo
->getElementNames();
791 sal_Int32 nNames
= aNames
.getLength();
792 const OUString
* pNames
= aNames
.getConstArray();
793 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
795 const OUString
& rName
= pNames
[i
];
797 if( ! xFrom
->hasByName( rName
) )
798 xTo
->removeByName( rName
);
802 /** copy namespaces from one namespace container into another
803 * @param bOverwrite true: overwrite namespaces in target
804 * false: do not overwrite namespaces in target
805 * @param bMove true: move namespaces (i.e., delete in source)
806 * false: copy namespaces (do not modify source)
807 * @param bFromSource true: use elements from source
808 * false: use only elements from target
810 static void lcl_copyNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& xFrom
,
811 css::uno::Reference
<css::container::XNameContainer
> const & xTo
,
814 OSL_ENSURE( xFrom
.is(), "no source" );
815 OSL_ENSURE( xTo
.is(), "no target" );
817 // iterate over name in source
818 Sequence
<OUString
> aNames
= xFrom
->getElementNames();
819 sal_Int32 nNames
= aNames
.getLength();
820 const OUString
* pNames
= aNames
.getConstArray();
821 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
823 const OUString
& rName
= pNames
[i
];
825 // determine whether to copy the value, and whether to delete
828 bool bInTarget
= xTo
->hasByName( rName
);
830 // we copy: if property is in target, and
831 // if bOverwrite is set, or when the namespace prefix is free
832 bool bCopy
= bOverwrite
|| ! bInTarget
;
834 // and now... ACTION!
838 xTo
->replaceByName( rName
, xFrom
->getByName( rName
) );
840 xTo
->insertByName( rName
, xFrom
->getByName( rName
) );
845 // implement get*Namespaces()
846 // (identical for both variants)
847 css::uno::Reference
<css::container::XNameContainer
> Binding::_getNamespaces() const
849 css::uno::Reference
<css::container::XNameContainer
> xNamespaces
= new NameContainer
<OUString
>();
850 lcl_copyNamespaces( mxNamespaces
, xNamespaces
, true );
852 // merge model's with binding's own namespaces
853 if( mxModel
!= nullptr )
854 lcl_copyNamespaces( mxModel
->getNamespaces(), xNamespaces
, false );
859 // implement set*Namespaces()
860 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
861 void Binding::_setNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
,
864 css::uno::Reference
<css::container::XNameContainer
> xModelNamespaces
= ( mxModel
!= nullptr )
865 ? mxModel
->getNamespaces()
867 OSL_ENSURE( ( mxModel
!= nullptr ) == xModelNamespaces
.is(), "no model nmsp?");
869 // remove deleted namespaces
870 lcl_removeOtherNamespaces( rNamespaces
, mxNamespaces
);
871 if( !bBinding
&& xModelNamespaces
.is() )
872 lcl_removeOtherNamespaces( rNamespaces
, xModelNamespaces
);
874 // copy namespaces as appropriate
875 Sequence
<OUString
> aNames
= rNamespaces
->getElementNames();
876 sal_Int32 nNames
= aNames
.getLength();
877 const OUString
* pNames
= aNames
.getConstArray();
878 for( sal_Int32 i
= 0; i
< nNames
; i
++ )
880 const OUString
& rName
= pNames
[i
];
881 Any aValue
= rNamespaces
->getByName( rName
);
883 // determine whether the namespace should go into model's or
884 // into binding's namespaces
886 ! xModelNamespaces
.is()
887 || mxNamespaces
->hasByName( rName
)
889 && xModelNamespaces
.is()
890 && xModelNamespaces
->hasByName( rName
) );
892 // write namespace into the appropriate namespace container
893 css::uno::Reference
<css::container::XNameContainer
>& rWhich
= bLocal
? mxNamespaces
: xModelNamespaces
;
894 OSL_ENSURE( rWhich
.is(), "whoops" );
895 if( rWhich
->hasByName( rName
) )
896 rWhich
->replaceByName( rName
, aValue
);
898 rWhich
->insertByName( rName
, aValue
);
900 // always 'promote' namespaces from binding to model, if equal
901 if( xModelNamespaces
.is()
902 && xModelNamespaces
->hasByName( rName
)
903 && mxNamespaces
->hasByName( rName
)
904 && xModelNamespaces
->getByName( rName
) == mxNamespaces
->getByName( rName
) )
906 mxNamespaces
->removeByName( rName
);
910 // ... done. But we modified the binding!
914 void Binding::_checkBindingID()
919 Reference
<XNameAccess
> xBindings( mxModel
->getBindings(), UNO_QUERY_THROW
);
920 if( !msBindingID
.isEmpty() )
923 // no binding ID? then make one up!
924 OUString sIDPrefix
= getResource( RID_STR_XFORMS_BINDING_UI_NAME
) + " ";
925 sal_Int32 nNumber
= 0;
930 sName
= sIDPrefix
+ OUString::number( nNumber
);
932 while( xBindings
->hasByName( sName
) );
933 setBindingID( sName
);
940 css::uno::Sequence
<css::uno::Type
> Binding::getSupportedValueTypes()
942 return Convert::get().getTypes();
945 sal_Bool
Binding::supportsType( const css::uno::Type
& rType
)
947 return Convert::get().hasType( rType
);
950 css::uno::Any
Binding::getValue( const css::uno::Type
& rType
)
952 // first, check for model
955 // second, check for type
956 if( ! supportsType( rType
) )
957 throw IncompatibleTypesException(u
"type unsupported"_ustr
, static_cast<XValueBinding
*>(this));
959 // return string value (if present; else return empty Any)
960 css::uno::Any result
;
961 if(maBindingExpression
.hasValue()) {
962 OUString
pathExpr(maBindingExpression
.getString());
963 Convert
&rConvert
= Convert::get();
964 result
= rConvert
.toAny(pathExpr
,rType
);
970 void Binding::setValue( const css::uno::Any
& aValue
)
972 // first, check for model
975 // check for supported type
976 if( ! supportsType( aValue
.getValueType() ) )
977 throw IncompatibleTypesException(u
"type unsupported"_ustr
, static_cast<XValueBinding
*>(this));
979 if( !maBindingExpression
.hasValue() )
980 throw InvalidBindingStateException(u
"no suitable node found"_ustr
, static_cast<XValueBinding
*>(this));
982 css::uno::Reference
<css::xml::dom::XNode
> xNode
= maBindingExpression
.getNode();
984 throw InvalidBindingStateException(u
"no suitable node found"_ustr
, static_cast<XValueBinding
*>(this));
986 OUString sValue
= Convert::get().toXSD( aValue
);
987 bool bSuccess
= mxModel
->setSimpleContent( xNode
, sValue
);
989 throw InvalidBindingStateException(u
"can't set value"_ustr
, static_cast<XValueBinding
*>(this));
998 sal_Int32
Binding::getListEntryCount()
1000 // first, check for model
1003 // return size of node list
1004 return maBindingExpression
.getNodeList().size();
1007 static void lcl_getString( const Reference
<XNode
>& xNode
, OUStringBuffer
& rBuffer
)
1009 if( xNode
->getNodeType() == NodeType_TEXT_NODE
1010 || xNode
->getNodeType() == NodeType_ATTRIBUTE_NODE
)
1012 rBuffer
.append( xNode
->getNodeValue() );
1016 for( Reference
<XNode
> xChild
= xNode
->getFirstChild();
1018 xChild
= xChild
->getNextSibling() )
1020 lcl_getString( xChild
, rBuffer
);
1025 static OUString
lcl_getString( const Reference
<XNode
>& xNode
)
1027 OUStringBuffer aBuffer
;
1028 lcl_getString( xNode
, aBuffer
);
1029 return aBuffer
.makeStringAndClear();
1032 OUString
Binding::getListEntry( sal_Int32 nPosition
)
1034 // first, check for model
1037 // check bounds and return proper item
1038 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
1039 if( nPosition
< 0 || o3tl::make_unsigned(nPosition
) >= aNodes
.size() )
1040 throw IndexOutOfBoundsException(u
""_ustr
, static_cast<XValueBinding
*>(this));
1041 return lcl_getString( aNodes
[ nPosition
] );
1044 Sequence
<OUString
> Binding::getAllListEntries()
1046 // first, check for model
1049 // create sequence of string values
1050 PathExpression::NodeVector_t aNodes
= maBindingExpression
.getNodeList();
1051 Sequence
<OUString
> aSequence( aNodes
.size() );
1052 OUString
* pSequence
= aSequence
.getArray();
1053 for( sal_Int32 n
= 0; n
< aSequence
.getLength(); n
++ )
1055 pSequence
[n
] = lcl_getString( aNodes
[n
] );
1061 void Binding::addListEntryListener( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
)
1063 OSL_ENSURE( xListener
.is(), "need listener!" );
1064 if( ::std::find( maListEntryListeners
.begin(),
1065 maListEntryListeners
.end(),
1067 == maListEntryListeners
.end() )
1068 maListEntryListeners
.push_back( xListener
);
1071 void Binding::removeListEntryListener( const css::uno::Reference
<css::form::binding::XListEntryListener
>& xListener
)
1073 XListEntryListeners_t::iterator aIter
=
1074 ::std::find( maListEntryListeners
.begin(), maListEntryListeners
.end(),
1076 if( aIter
!= maListEntryListeners
.end() )
1077 maListEntryListeners
.erase( aIter
);
1084 sal_Bool
Binding::isValid( const css::uno::Any
& )
1086 // first, check for model
1089 // ignore value; determine validate only on current data
1093 OUString
Binding::explainInvalid(
1094 const css::uno::Any
& /*Value*/ )
1096 // first, check for model
1099 // ignore value; determine explanation only on current data
1100 return explainInvalid();
1103 void Binding::addValidityConstraintListener(
1104 const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
)
1106 OSL_ENSURE( xListener
.is(), "need listener!" );
1107 if( ::std::find(maValidityListeners
.begin(), maValidityListeners
.end(), xListener
)
1108 == maValidityListeners
.end() )
1109 maValidityListeners
.push_back( xListener
);
1112 void Binding::removeValidityConstraintListener(
1113 const css::uno::Reference
<css::form::validation::XValidityConstraintListener
>& xListener
)
1115 XValidityConstraintListeners_t::iterator aIter
=
1116 ::std::find( maValidityListeners
.begin(), maValidityListeners
.end(),
1118 if( aIter
!= maValidityListeners
.end() )
1119 maValidityListeners
.erase( aIter
);
1123 // xml::dom::event::XEventListener
1126 void Binding::handleEvent( const css::uno::Reference
<css::xml::dom::events::XEvent
>& xEvent
)
1128 OUString
sType(xEvent
->getType());
1129 //OUString sEventMIPChanged("xforms-generic");
1130 //if(sType.equals(sEventMIPChanged)) {
1131 if(sType
== "xforms-generic") {
1133 // the modification of the 'mnDeferModifyNotifications'-member
1134 // is necessary to prevent infinite notification looping.
1135 // This can happened in case the binding which caused
1136 // the notification chain is listening to those events
1138 bool bPreserveValueModified
= mbValueModified
;
1139 mnDeferModifyNotifications
++;
1141 --mnDeferModifyNotifications
;
1142 mbValueModified
= bPreserveValueModified
;
1146 // if we're a dynamic binding, we better re-bind, too!
1149 // our value was maybe modified
1157 sal_Int64
Binding::getSomething( const css::uno::Sequence
<sal_Int8
>& xId
)
1159 return comphelper::getSomethingImpl(xId
, this);
1166 css::uno::Reference
<css::util::XCloneable
> SAL_CALL
Binding::createClone()
1168 Reference
< XPropertySet
> xClone
;
1171 xClone
= mxModel
->cloneBinding( this );
1174 xClone
= new Binding
;
1175 copy( this, xClone
);
1177 return css::uno::Reference
<css::util::XCloneable
>( xClone
, UNO_QUERY
);
1180 css::uno::Reference
<css::xforms::XModel
> Binding::getModel() const
1185 // property set implementations
1187 void Binding::initializePropertySet()
1189 registerProperty( css::beans::Property(u
"BindingID"_ustr
, HANDLE_BindingID
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1190 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setBindingID
, &Binding::getBindingID
));
1192 registerProperty( css::beans::Property(u
"BindingExpression"_ustr
, HANDLE_BindingExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1193 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setBindingExpression
, &Binding::getBindingExpression
));
1195 registerProperty( css::beans::Property(u
"Model"_ustr
, HANDLE_Model
, cppu::UnoType
<css::uno::Reference
<css::xforms::XModel
>>::get(), css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::READONLY
),
1196 new DirectPropertyAccessor
< Binding
, css::uno::Reference
<css::xforms::XModel
> >(this, nullptr, &Binding::getModel
));
1198 registerProperty( css::beans::Property(u
"BindingNamespaces"_ustr
, HANDLE_BindingNamespaces
, cppu::UnoType
<css::uno::Reference
<css::container::XNameContainer
>>::get(), css::beans::PropertyAttribute::BOUND
),
1199 new DirectPropertyAccessor
< Binding
, css::uno::Reference
<css::container::XNameContainer
> >(this, &Binding::setBindingNamespaces
, &Binding::getBindingNamespaces
));
1201 registerProperty( css::beans::Property(u
"ModelNamespaces"_ustr
, HANDLE_ModelNamespaces
, cppu::UnoType
<css::uno::Reference
<css::container::XNameContainer
>>::get(), css::beans::PropertyAttribute::BOUND
),
1202 new DirectPropertyAccessor
< Binding
, css::uno::Reference
<css::container::XNameContainer
> >(this, &Binding::setModelNamespaces
, &Binding::getModelNamespaces
));
1204 registerProperty( css::beans::Property(u
"ModelID"_ustr
, HANDLE_ModelID
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::READONLY
),
1205 new DirectPropertyAccessor
< Binding
, OUString
>(this, nullptr, &Binding::getModelID
));
1207 registerProperty( css::beans::Property(u
"ReadonlyExpression"_ustr
, HANDLE_ReadonlyExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1208 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setReadonlyExpression
, &Binding::getReadonlyExpression
));
1210 registerProperty( css::beans::Property(u
"RelevantExpression"_ustr
, HANDLE_RelevantExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1211 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setRelevantExpression
, &Binding::getRelevantExpression
));
1213 registerProperty( css::beans::Property(u
"RequiredExpression"_ustr
, HANDLE_RequiredExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1214 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setRequiredExpression
, &Binding::getRequiredExpression
));
1216 registerProperty( css::beans::Property(u
"ConstraintExpression"_ustr
, HANDLE_ConstraintExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1217 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setConstraintExpression
, &Binding::getConstraintExpression
));
1219 registerProperty( css::beans::Property(u
"CalculateExpression"_ustr
, HANDLE_CalculateExpression
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1220 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setCalculateExpression
, &Binding::getCalculateExpression
));
1222 registerProperty( css::beans::Property(u
"Type"_ustr
, HANDLE_Type
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
1223 new DirectPropertyAccessor
< Binding
, OUString
>(this, &Binding::setType
, &Binding::getType
));
1225 registerProperty( css::beans::Property(u
"ReadOnly"_ustr
, HANDLE_ReadOnly
, cppu::UnoType
<bool>::get(), css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::READONLY
),
1226 new DirectPropertyAccessor
< Binding
, bool >(this, nullptr, &Binding::getReadOnly
));
1228 registerProperty( css::beans::Property(u
"Relevant"_ustr
, HANDLE_Relevant
, cppu::UnoType
<bool>::get(), css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::READONLY
),
1229 new DirectPropertyAccessor
< Binding
, bool >(this, nullptr, &Binding::getRelevant
));
1231 registerProperty( css::beans::Property(u
"ExternalData"_ustr
, HANDLE_ExternalData
, cppu::UnoType
<sal_Bool
>::get(), css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::READONLY
),
1232 new BooleanPropertyAccessor
< Binding
>(this, nullptr, &Binding::getExternalData
));
1234 initializePropertyValueCache( HANDLE_ReadOnly
);
1235 initializePropertyValueCache( HANDLE_Relevant
);
1236 initializePropertyValueCache( HANDLE_ExternalData
);
1239 void Binding::addModifyListener(
1240 const css::uno::Reference
<css::util::XModifyListener
>& xListener
)
1242 OSL_ENSURE( xListener
.is(), "need listener!" );
1243 if( ::std::find( maModifyListeners
.begin(), maModifyListeners
.end(), xListener
)
1244 == maModifyListeners
.end() )
1245 maModifyListeners
.push_back( xListener
);
1247 // HACK: currently, we have to 'push' some MIPs to the control
1248 // (read-only, relevant, etc.) To enable this, we need to update
1249 // the control at least once when it registers here.
1253 void Binding::removeModifyListener(
1254 const css::uno::Reference
<css::util::XModifyListener
>& xListener
)
1256 ModifyListeners_t::iterator aIter
=
1257 ::std::find( maModifyListeners
.begin(), maModifyListeners
.end(), xListener
);
1258 if( aIter
!= maModifyListeners
.end() )
1259 maModifyListeners
.erase( aIter
);
1263 OUString
Binding::getName()
1265 return getBindingID();
1268 void SAL_CALL
Binding::setName( const OUString
& rName
)
1270 // use the XPropertySet methods, so the change in the name is notified to the
1271 // property listeners
1272 setFastPropertyValue( HANDLE_BindingID
, Any( rName
) );
1275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */