nss: upgrade to release 3.73
[LibreOffice.git] / forms / source / xforms / binding.cxx
bloba3a60864d62ab1a96f843c49525cfdf39ce01464
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "binding.hxx"
23 #include "model.hxx"
24 #include "unohelper.hxx"
25 #include "NameContainer.hxx"
26 #include "evaluationcontext.hxx"
27 #include "convert.hxx"
28 #include "resourcehelper.hxx"
29 #include "xmlhelper.hxx"
30 #include "xformsevent.hxx"
31 #include <strings.hrc>
33 #include <rtl/ustrbuf.hxx>
34 #include <osl/diagnose.h>
36 #include <tools/diagnose_ex.h>
38 #include <algorithm>
39 #include <functional>
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;
61 using std::vector;
62 using xforms::Binding;
63 using xforms::MIP;
64 using xforms::Model;
65 using xforms::getResource;
66 using xforms::EvaluationContext;
67 using com::sun::star::beans::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
114 Binding::Binding() :
115 mxModel(),
116 msBindingID(),
117 maBindingExpression(),
118 maReadonly(),
119 mxNamespaces( new NameContainer<OUString>() ),
120 mbInCalculate( false ),
121 mnDeferModifyNotifications( 0 ),
122 mbValueModified( false ),
123 mbBindingModified( false )
126 initializePropertySet();
129 Binding::~Binding()
131 _setModel(nullptr);
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
143 mxModel = xModel;
145 // set namespaces (and move to model, if appropriate)
146 setBindingNamespaces( xNamespaces );
147 _checkBindingID();
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() )
164 bind();
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();
188 maReadonly.clear();
189 maRelevant.clear();
190 maRequired.clear();
191 maConstraint.clear();
192 maCalculate.clear();
194 // let's just pretend the binding has been modified -> full rebind()
195 bindingModified();
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 )
206 bindingModified();
207 if( mbValueModified )
208 valueModified();
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
229 // we are useful, if
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)
236 bool bUseful =
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();
249 return bUseful;
252 OUString Binding::explainInvalid()
254 OUString sReason;
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!" );
285 return sReason;
289 EvaluationContext Binding::getEvaluationContext() const
291 OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
292 EvaluationContext aContext = getModelImpl()->getEvaluationContext();
293 aContext.mxNamespaces = getBindingNamespaces();
294 return aContext;
297 ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
299 OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
301 // bind (in case we were not bound before)
302 bind();
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 );
327 bindingModified();
330 OUString Binding::getReadonlyExpression() const
332 return maReadonly.getExpression();
335 void Binding::setReadonlyExpression( const OUString& sReadonly)
337 maReadonly.setExpression( sReadonly );
338 bindingModified();
341 OUString Binding::getRelevantExpression() const
343 return maRelevant.getExpression();
346 void Binding::setRelevantExpression( const OUString& sRelevant )
348 maRelevant.setExpression( sRelevant );
349 bindingModified();
352 OUString Binding::getRequiredExpression() const
354 return maRequired.getExpression();
357 void Binding::setRequiredExpression( const OUString& sRequired )
359 maRequired.setExpression( sRequired );
360 bindingModified();
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,
372 sConstraint );
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.
377 bindingModified();
380 OUString Binding::getCalculateExpression() const
382 return maCalculate.getExpression();
385 void Binding::setCalculateExpression( const OUString& sCalculate )
387 maCalculate.setExpression( sCalculate );
388 bindingModified();
392 void Binding::setType( const OUString& sTypeName )
394 msTypeName = sTypeName;
395 bindingModified();
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;
426 if ( !mxModel.is() )
427 return bExternalData;
431 Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW );
432 OSL_VERIFY(
433 xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
435 catch( const Exception& )
437 DBG_UNHANDLED_EXCEPTION("forms.xforms");
439 return bExternalData;
443 void Binding::checkLive()
445 if( ! isLive() )
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 );
464 if( !xTarget.is() )
465 return;
467 xTarget->addEventListener( "DOMCharacterDataModified",
468 xListener, false );
469 xTarget->addEventListener( "DOMCharacterDataModified",
470 xListener, true );
471 xTarget->addEventListener( "DOMAttrModified",
472 xListener, false );
473 xTarget->addEventListener( "DOMAttrModified",
474 xListener, true );
475 xTarget->addEventListener( "DOMAttrModified",
476 xListener, true );
477 xTarget->addEventListener( "xforms-generic",
478 xListener, true );
481 static void lcl_removeListenerFromNode( const Reference<XNode>& xNode,
482 const Reference<XEventListener>& xListener )
484 Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
485 if( !xTarget.is() )
486 return;
488 xTarget->removeEventListener( "DOMCharacterDataModified",
489 xListener, false );
490 xTarget->removeEventListener( "DOMCharacterDataModified",
491 xListener, true );
492 xTarget->removeEventListener( "DOMAttrModified",
493 xListener, false );
494 xTarget->removeEventListener( "DOMAttrModified",
495 xListener, true );
496 xTarget->removeEventListener( "xforms-generic",
497 xListener, true );
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() );
515 return aVector;
518 void Binding::bind( bool bForceRebind )
520 if( ! mxModel.is() )
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);
563 else
564 maEventNodes.emplace_back( aContext.mxContextNode->getOwnerDocument(),
565 UNO_QUERY_THROW );
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;
640 return;
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
649 if( xNode.is() )
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
670 if( xNode.is() )
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 );
682 while(xNode.is()) {
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());
687 if(child.is())
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;
707 return;
709 mbBindingModified = false;
711 // rebind (if live); then call valueModified
712 // A binding should be inert until its model is fully constructed.
713 if( isLive() )
715 bind( true );
716 valueModified();
721 MIP Binding::getLocalMIP() const
723 MIP aMIP;
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() );
743 return aMIP;
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() )
772 : OUString();
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();
787 // clear expressions
788 maBindingExpression.clear();
789 maReadonly.clear();
790 maRelevant.clear();
791 maRequired.clear();
792 maConstraint.clear();
793 maCalculate.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,
828 bool bOverwrite )
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
842 // it in the source:
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!
851 if( bCopy )
853 if( bInTarget )
854 xTo->replaceByName( rName, xFrom->getByName( rName ) );
855 else
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 );
873 return xNamespaces;
876 // implement set*Namespaces()
877 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
878 void Binding::_setNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces,
879 bool bBinding )
881 Model* pModel = getModelImpl();
882 css::uno::Reference<css::container::XNameContainer> xModelNamespaces = ( pModel != nullptr )
883 ? pModel->getNamespaces()
884 : nullptr;
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
903 bool bLocal =
904 ! xModelNamespaces.is()
905 || mxNamespaces->hasByName( rName )
906 || ( bBinding
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 );
915 else
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!
929 bindingModified();
932 void Binding::_checkBindingID()
934 if( !getModel().is() )
935 return;
937 Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW );
938 if( !msBindingID.isEmpty() )
939 return;
941 // no binding ID? then make one up!
942 OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ) + " ";
943 sal_Int32 nNumber = 0;
944 OUString sName;
947 nNumber++;
948 sName = sIDPrefix + OUString::number( nNumber );
950 while( xBindings->hasByName( sName ) );
951 setBindingID( sName );
955 // XValueBinding
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
971 checkLive();
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);
985 return result;
988 void Binding::setValue( const css::uno::Any& aValue )
990 // first, check for model
991 checkLive();
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();
1001 if( !xNode.is() )
1002 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) );
1004 OUString sValue = Convert::get().toXSD( aValue );
1005 bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue );
1006 if( ! bSuccess )
1007 throw InvalidBindingStateException( EXCEPT( "can't set value" ) );
1013 // XListEntry Source
1016 sal_Int32 Binding::getListEntryCount()
1018 // first, check for model
1019 checkLive();
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() );
1032 else
1034 for( Reference<XNode> xChild = xNode->getFirstChild();
1035 xChild.is();
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
1053 checkLive();
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
1065 checkLive();
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] );
1076 return aSequence;
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(),
1084 xListener)
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(),
1093 xListener );
1094 if( aIter != maListEntryListeners.end() )
1095 maListEntryListeners.erase( aIter );
1099 // XValidator
1102 sal_Bool Binding::isValid( const css::uno::Any& )
1104 // first, check for model
1105 checkLive();
1107 // ignore value; determine validate only on current data
1108 return isValid();
1111 OUString Binding::explainInvalid(
1112 const css::uno::Any& /*Value*/ )
1114 // first, check for model
1115 checkLive();
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(),
1135 xListener );
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
1155 // as well...
1156 bool bPreserveValueModified = mbValueModified;
1157 mnDeferModifyNotifications++;
1158 valueModified();
1159 --mnDeferModifyNotifications;
1160 mbValueModified = bPreserveValueModified;
1161 return;
1164 // if we're a dynamic binding, we better re-bind, too!
1165 bind();
1167 // our value was maybe modified
1168 valueModified();
1172 // lang::XUnoTunnel
1175 sal_Int64 Binding::getSomething( const css::uno::Sequence<sal_Int8>& xId )
1177 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelId() ) ? this : nullptr );
1181 // XCloneable
1184 css::uno::Reference<css::util::XCloneable> SAL_CALL Binding::createClone()
1186 Reference< XPropertySet > xClone;
1188 Model* pModel = getModelImpl();
1189 if ( pModel )
1190 xClone = pModel->cloneBinding( this );
1191 else
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.
1249 valueModified();
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: */