| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_forms.hxx" |
| |
| #include "binding.hxx" |
| |
| #include "model.hxx" |
| #include "unohelper.hxx" |
| #include "NameContainer.hxx" |
| #include "evaluationcontext.hxx" |
| #include "convert.hxx" |
| #include "resourcehelper.hxx" |
| #include "xmlhelper.hxx" |
| #include "xformsevent.hxx" |
| |
| #include <rtl/ustrbuf.hxx> |
| #include <osl/diagnose.h> |
| |
| #include <tools/diagnose_ex.h> |
| |
| #include <algorithm> |
| #include <functional> |
| |
| #include <com/sun/star/uno/Any.hxx> |
| #include <com/sun/star/xml/dom/XNodeList.hpp> |
| #include <com/sun/star/xml/dom/XNode.hpp> |
| #include <com/sun/star/xml/dom/XDocument.hpp> |
| #include <com/sun/star/xml/dom/XElement.hpp> |
| #include <com/sun/star/xml/dom/NodeType.hpp> |
| #include <com/sun/star/xml/dom/events/XEventTarget.hpp> |
| #include <com/sun/star/xml/dom/events/XEventListener.hpp> |
| #include <com/sun/star/xml/dom/events/XDocumentEvent.hpp> |
| #include <com/sun/star/lang/XUnoTunnel.hpp> |
| #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> |
| #include <com/sun/star/container/XSet.hpp> |
| #include <com/sun/star/container/XNameContainer.hpp> |
| |
| #include <comphelper/propertysetinfo.hxx> |
| #include <unotools/textsearch.hxx> |
| #include <cppuhelper/typeprovider.hxx> |
| |
| using namespace com::sun::star::xml::xpath; |
| using namespace com::sun::star::xml::dom::events; |
| |
| using rtl::OUString; |
| using rtl::OUStringBuffer; |
| using std::vector; |
| using xforms::Binding; |
| using xforms::MIP; |
| using xforms::Model; |
| using xforms::getResource; |
| using xforms::EvaluationContext; |
| using com::sun::star::beans::PropertyVetoException; |
| using com::sun::star::beans::UnknownPropertyException; |
| using com::sun::star::beans::XPropertySet; |
| using com::sun::star::container::XSet; |
| using com::sun::star::container::XNameAccess; |
| using com::sun::star::form::binding::IncompatibleTypesException; |
| using com::sun::star::form::binding::InvalidBindingStateException; |
| using com::sun::star::form::binding::XValueBinding; |
| using com::sun::star::lang::EventObject; |
| using com::sun::star::lang::IllegalArgumentException; |
| using com::sun::star::lang::IndexOutOfBoundsException; |
| using com::sun::star::lang::NoSupportException; |
| using com::sun::star::lang::NullPointerException; |
| using com::sun::star::lang::WrappedTargetException; |
| using com::sun::star::lang::XUnoTunnel; |
| using com::sun::star::uno::Any; |
| using com::sun::star::uno::Reference; |
| using com::sun::star::uno::RuntimeException; |
| using com::sun::star::uno::Sequence; |
| using com::sun::star::uno::UNO_QUERY; |
| using com::sun::star::uno::UNO_QUERY_THROW; |
| using com::sun::star::uno::XInterface; |
| using com::sun::star::uno::Exception; |
| using com::sun::star::uno::makeAny; |
| using com::sun::star::util::XModifyListener; |
| using com::sun::star::xforms::XDataTypeRepository; |
| using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE; |
| using com::sun::star::xml::dom::NodeType_TEXT_NODE; |
| using com::sun::star::xml::dom::XNode; |
| using com::sun::star::xml::dom::XNodeList; |
| using com::sun::star::xml::dom::events::XEventListener; |
| using com::sun::star::xml::dom::events::XEventTarget; |
| using com::sun::star::xsd::XDataType; |
| |
| |
| |
| |
| #define EXCEPT(msg) OUSTRING(msg),static_cast<XValueBinding*>(this) |
| |
| #define HANDLE_BindingID 0 |
| #define HANDLE_BindingExpression 1 |
| #define HANDLE_Model 2 |
| #define HANDLE_ModelID 3 |
| #define HANDLE_BindingNamespaces 4 |
| #define HANDLE_ReadonlyExpression 5 |
| #define HANDLE_RelevantExpression 6 |
| #define HANDLE_RequiredExpression 7 |
| #define HANDLE_ConstraintExpression 8 |
| #define HANDLE_CalculateExpression 9 |
| #define HANDLE_Type 10 |
| #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control |
| #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control |
| #define HANDLE_ModelNamespaces 13 |
| #define HANDLE_ExternalData 14 |
| |
| |
| Binding::Binding() : |
| mxModel(), |
| msBindingID(), |
| maBindingExpression(), |
| maReadonly(), |
| mxNamespaces( new NameContainer<OUString>() ), |
| mbInCalculate( false ), |
| mnDeferModifyNotifications( 0 ), |
| mbValueModified( false ), |
| mbBindingModified( false ) |
| |
| { |
| initializePropertySet(); |
| } |
| |
| Binding::~Binding() throw() |
| { |
| _setModel(NULL); |
| } |
| |
| |
| Binding::Model_t Binding::getModel() const |
| { |
| return mxModel; |
| } |
| |
| void Binding::_setModel( const Model_t& xModel ) |
| { |
| PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model ); |
| PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID ); |
| |
| // prepare binding for removal of old model |
| clear(); // remove all cached data (e.g. XPath evaluation results) |
| XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces |
| |
| mxModel = xModel; |
| |
| // set namespaces (and move to model, if appropriate) |
| setBindingNamespaces( xNamespaces ); |
| _checkBindingID(); |
| |
| notifyAndCachePropertyValue( HANDLE_ExternalData ); |
| } |
| |
| |
| OUString Binding::getModelID() const |
| { |
| Model* pModel = getModelImpl(); |
| return ( pModel == NULL ) ? OUString() : pModel->getID(); |
| } |
| |
| |
| Binding::XNodeList_t Binding::getXNodeList() |
| { |
| // first make sure we are bound |
| if( ! maBindingExpression.hasValue() ) |
| bind( sal_False ); |
| |
| return maBindingExpression.getXNodeList(); |
| } |
| |
| bool Binding::isSimpleBinding() const |
| { |
| return maBindingExpression.isSimpleExpression() |
| && maReadonly.isSimpleExpression() |
| && maRelevant.isSimpleExpression() |
| && maRequired.isSimpleExpression() |
| && maConstraint.isSimpleExpression() |
| && maCalculate.isSimpleExpression(); |
| } |
| |
| bool Binding::isSimpleBindingExpression() const |
| { |
| return maBindingExpression.isSimpleExpression(); |
| } |
| |
| void Binding::update() |
| { |
| // clear all expressions (to remove cached node references) |
| maBindingExpression.clear(); |
| maReadonly.clear(); |
| maRelevant.clear(); |
| maRequired.clear(); |
| maConstraint.clear(); |
| maCalculate.clear(); |
| |
| // let's just pretend the binding has been modified -> full rebind() |
| bindingModified(); |
| } |
| |
| void Binding::deferNotifications( bool bDefer ) |
| { |
| mnDeferModifyNotifications += ( bDefer ? 1 : -1 ); |
| OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" ); |
| |
| if( mnDeferModifyNotifications == 0 ) |
| { |
| if( mbBindingModified ) |
| bindingModified(); |
| if( mbValueModified ) |
| valueModified(); |
| } |
| |
| OSL_ENSURE( ( mnDeferModifyNotifications > 0 ) |
| || ( ! mbBindingModified && ! mbValueModified ), |
| "deferred modifications not delivered?" ); |
| } |
| |
| bool Binding::isValid() |
| { |
| // TODO: determine whether node is suitable, not just whether it exists |
| return maBindingExpression.getNode().is() && |
| isValid_DataType() && |
| maMIP.isConstraint() && |
| ( ! maMIP.isRequired() || |
| ( maBindingExpression.hasValue() && |
| maBindingExpression.getString().getLength() > 0 ) ); |
| } |
| |
| bool Binding::isUseful() |
| { |
| // we are useful, if |
| // 0) we don't have a model |
| // (at least, in this case we shouldn't be removed from the model) |
| // 1) we have a proper name |
| // 2) we have some MIPs, |
| // 3) we are bound to some control |
| // (this can be assumed if some listeners are set) |
| bool bUseful = |
| getModelImpl() == NULL |
| // || msBindingID.getLength() > 0 |
| || msTypeName.getLength() > 0 |
| || ! maReadonly.isEmptyExpression() |
| || ! maRelevant.isEmptyExpression() |
| || ! maRequired.isEmptyExpression() |
| || ! maConstraint.isEmptyExpression() |
| || ! maCalculate.isEmptyExpression() |
| || ! maModifyListeners.empty() |
| || ! maListEntryListeners.empty() |
| || ! maValidityListeners.empty(); |
| |
| return bUseful; |
| } |
| |
| OUString Binding::explainInvalid() |
| { |
| OUString sReason; |
| if( ! maBindingExpression.getNode().is() ) |
| { |
| sReason = ( maBindingExpression.getExpression().getLength() == 0 ) |
| ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION ) |
| : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION ); |
| } |
| else if( ! isValid_DataType() ) |
| { |
| sReason = explainInvalid_DataType(); |
| if( sReason.getLength() == 0 ) |
| { |
| // no explanation given by data type? Then give generic message |
| sReason = getResource( RID_STR_XFORMS_INVALID_VALUE, |
| maMIP.getTypeName() ); |
| } |
| } |
| else if( ! maMIP.isConstraint() ) |
| { |
| sReason = maMIP.getConstraintExplanation(); |
| } |
| else if( maMIP.isRequired() && maBindingExpression.hasValue() && |
| ( maBindingExpression.getString().getLength() == 0 ) ) |
| { |
| sReason = getResource( RID_STR_XFORMS_REQUIRED ); |
| } |
| // else: no explanation given; should only happen if data is valid |
| |
| OSL_ENSURE( ( sReason.getLength() == 0 ) == isValid(), |
| "invalid data should have an explanation!" ); |
| |
| return sReason; |
| } |
| |
| |
| |
| EvaluationContext Binding::getEvaluationContext() const |
| { |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); |
| EvaluationContext aContext = getModelImpl()->getEvaluationContext(); |
| aContext.mxNamespaces = getBindingNamespaces(); |
| return aContext; |
| } |
| |
| ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts() |
| { |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); |
| |
| // bind (in case we were not bound before) |
| bind( sal_False ); |
| return _getMIPEvaluationContexts(); |
| } |
| |
| |
| Binding::IntSequence_t Binding::getUnoTunnelID() |
| { |
| static cppu::OImplementationId aImplementationId; |
| return aImplementationId.getImplementationId(); |
| } |
| |
| Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet ) |
| { |
| Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY ); |
| return xTunnel.is() |
| ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID())) |
| : NULL; |
| } |
| |
| |
| |
| |
| OUString Binding::getBindingID() const |
| { |
| return msBindingID; |
| } |
| |
| void Binding::setBindingID( const OUString& sBindingID ) |
| { |
| msBindingID = sBindingID; |
| } |
| |
| OUString Binding::getBindingExpression() const |
| { |
| return maBindingExpression.getExpression(); |
| } |
| |
| void Binding::setBindingExpression( const OUString& sBindingExpression) |
| { |
| maBindingExpression.setExpression( sBindingExpression ); |
| bindingModified(); |
| } |
| |
| OUString Binding::getReadonlyExpression() const |
| { |
| return maReadonly.getExpression(); |
| } |
| |
| void Binding::setReadonlyExpression( const OUString& sReadonly) |
| { |
| maReadonly.setExpression( sReadonly ); |
| bindingModified(); |
| } |
| |
| OUString Binding::getRelevantExpression() const |
| { |
| return maRelevant.getExpression(); |
| } |
| |
| void Binding::setRelevantExpression( const OUString& sRelevant ) |
| { |
| maRelevant.setExpression( sRelevant ); |
| bindingModified(); |
| } |
| |
| OUString Binding::getRequiredExpression() const |
| { |
| return maRequired.getExpression(); |
| } |
| |
| void Binding::setRequiredExpression( const OUString& sRequired ) |
| { |
| maRequired.setExpression( sRequired ); |
| bindingModified(); |
| } |
| |
| OUString Binding::getConstraintExpression() const |
| { |
| return maConstraint.getExpression(); |
| } |
| |
| void Binding::setConstraintExpression( const OUString& sConstraint ) |
| { |
| maConstraint.setExpression( sConstraint ); |
| msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT, |
| sConstraint ); |
| |
| // TODO: This should only re-evaluate the constraint, and notify |
| // the validity constraint listeners; instead we currently pretend |
| // the entire binding was notified, which does a little too much. |
| bindingModified(); |
| } |
| |
| OUString Binding::getCalculateExpression() const |
| { |
| return maCalculate.getExpression(); |
| } |
| |
| void Binding::setCalculateExpression( const OUString& sCalculate ) |
| { |
| maCalculate.setExpression( sCalculate ); |
| bindingModified(); |
| } |
| |
| OUString Binding::getType() const |
| { |
| return msTypeName; |
| } |
| |
| void Binding::setType( const OUString& sTypeName ) |
| { |
| msTypeName = sTypeName; |
| bindingModified(); |
| } |
| |
| Binding::XNameContainer_t Binding::getBindingNamespaces() const |
| { |
| // return _getNamespaces(); |
| return mxNamespaces; |
| } |
| |
| void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces ) |
| { |
| _setNamespaces( rNamespaces, true ); |
| } |
| |
| Binding::XNameContainer_t Binding::getModelNamespaces() const |
| { |
| return _getNamespaces(); |
| } |
| |
| void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces ) |
| { |
| _setNamespaces( rNamespaces, false ); |
| } |
| |
| bool Binding::getReadOnly() const |
| { |
| return maMIP.isReadonly(); |
| } |
| |
| bool Binding::getRelevant() const |
| { |
| return maMIP.isRelevant(); |
| } |
| |
| bool Binding::getExternalData() const |
| { |
| bool bExternalData = true; |
| if ( !mxModel.is() ) |
| return bExternalData; |
| |
| try |
| { |
| Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW ); |
| OSL_VERIFY( |
| xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExternalData" ) ) ) >>= bExternalData ); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| return bExternalData; |
| } |
| |
| |
| void Binding::checkLive() |
| throw( RuntimeException ) |
| { |
| if( ! isLive() ) |
| throw RuntimeException( EXCEPT("Binding not initialized") ); |
| } |
| |
| void Binding::checkModel() |
| throw( RuntimeException ) |
| { |
| if( ! mxModel.is() ) |
| throw RuntimeException( EXCEPT("Binding has no Model") ); |
| } |
| |
| bool Binding::isLive() const |
| { |
| const Model* pModel = getModelImpl(); |
| return ( pModel != NULL ) ? pModel->isInitialized() : false; |
| } |
| |
| Model* Binding::getModelImpl() const |
| { |
| return getModelImpl( mxModel ); |
| } |
| |
| Model* Binding::getModelImpl( const Model_t& xModel ) const |
| { |
| Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY ); |
| Model* pModel = xTunnel.is() |
| ? reinterpret_cast<Model*>( |
| xTunnel->getSomething( Model::getUnoTunnelID() ) ) |
| : NULL; |
| return pModel; |
| } |
| |
| void lcl_addListenerToNode( Reference<XNode> xNode, |
| Reference<XEventListener> xListener ) |
| { |
| Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); |
| if( xTarget.is() ) |
| { |
| xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), |
| xListener, false ); |
| xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), |
| xListener, true ); |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), |
| xListener, false ); |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), |
| xListener, true ); |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), |
| xListener, true ); |
| xTarget->addEventListener( OUSTRING("xforms-generic"), |
| xListener, true ); |
| } |
| } |
| |
| void lcl_removeListenerFromNode( Reference<XNode> xNode, |
| Reference<XEventListener> xListener ) |
| { |
| Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); |
| if( xTarget.is() ) |
| { |
| xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), |
| xListener, false ); |
| xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), |
| xListener, true ); |
| xTarget->removeEventListener( OUSTRING("DOMAttrModified"), |
| xListener, false ); |
| xTarget->removeEventListener( OUSTRING("DOMAttrModified"), |
| xListener, true ); |
| xTarget->removeEventListener( OUSTRING("xforms-generic"), |
| xListener, true ); |
| } |
| } |
| |
| ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const |
| { |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); |
| |
| // iterate over nodes of bind expression and create |
| // EvaluationContext for each |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); |
| ::std::vector<EvaluationContext> aVector; |
| sal_Int32 nCount = 0; // count nodes for context position |
| for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); |
| aIter != aNodes.end(); |
| aIter++, nCount++ ) |
| { |
| OSL_ENSURE( aIter->is(), "no node?" ); |
| |
| // create proper evaluation context for this MIP |
| aVector.push_back( EvaluationContext( *aIter, getModel(), |
| getBindingNamespaces(), |
| nCount, aNodes.size() ) ); |
| } |
| return aVector; |
| } |
| |
| void Binding::bind( bool bForceRebind ) |
| { |
| checkModel(); |
| |
| // bind() will evaluate this binding as follows: |
| // 1) evaluate the binding expression |
| // 1b) if necessary, create node according to 'lazy author' rules |
| // 2) register suitable listeners on the instance (and remove old ones) |
| // 3) remove old MIPs defined by this binding |
| // 4) for every node in the binding nodeset do: |
| // 1) create proper evaluation context for this MIP |
| // 2) evaluate calculate expression (and push value into instance) |
| // 3) evaluate remaining MIPs |
| // 4) evaluate the locally defined MIPs, and push them to the model |
| |
| |
| // 1) evaluate the binding expression |
| EvaluationContext aContext = getEvaluationContext(); |
| maBindingExpression.evaluate( aContext ); |
| if( ! maBindingExpression.getNode().is() ) |
| { |
| // 1b) create node (if valid element name) |
| if( isValidQName( maBindingExpression.getExpression(), |
| aContext.mxNamespaces ) ) |
| { |
| aContext.mxContextNode->appendChild( |
| Reference<XNode>( |
| aContext.mxContextNode->getOwnerDocument()->createElement( |
| maBindingExpression.getExpression() ), |
| UNO_QUERY ) ); |
| maBindingExpression.evaluate( aContext ); |
| OSL_ENSURE( maBindingExpression.getNode().is(), |
| "we should bind to the newly inserted node!" ); |
| } |
| } |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); |
| |
| // 2) register suitable listeners on the instance (and remove old ones) |
| if( maEventNodes.empty() || bForceRebind ) |
| { |
| for( XNodes_t::iterator aIter = maEventNodes.begin(); |
| aIter != maEventNodes.end(); |
| aIter ++ ) |
| lcl_removeListenerFromNode( *aIter, this ); |
| maEventNodes.clear(); |
| if( isSimpleBinding() ) |
| for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); |
| aIter != aNodes.end(); |
| aIter++ ) |
| maEventNodes.push_back( *aIter ); |
| else |
| maEventNodes.push_back( |
| Reference<XNode>( aContext.mxContextNode->getOwnerDocument(), |
| UNO_QUERY_THROW ) ); |
| for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin(); |
| aIter2 != maEventNodes.end(); |
| aIter2 ++ ) |
| lcl_addListenerToNode( *aIter2, this ); |
| } |
| |
| // 3) remove old MIPs defined by this binding |
| Model* pModel = getModelImpl(); |
| OSL_ENSURE( pModel != NULL, "need model" ); |
| pModel->removeMIPs( this ); |
| |
| // 4) calculate all MIPs |
| ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts(); |
| for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin(); |
| aIter != aMIPContexts.end(); |
| aIter++ ) |
| { |
| EvaluationContext& rContext = *aIter; |
| |
| // evaluate calculate expression (and push value into instance) |
| // (prevent recursion using mbInCalculate |
| if( ! maCalculate.isEmptyExpression() ) |
| { |
| if( ! mbInCalculate ) |
| { |
| mbInCalculate = true; |
| maCalculate.evaluate( rContext ); |
| pModel->setSimpleContent( rContext.mxContextNode, |
| maCalculate.getString() ); |
| mbInCalculate = false; |
| } |
| } |
| |
| // now evaluate remaining MIPs in the apropriate context |
| maReadonly.evaluate( rContext ); |
| maRelevant.evaluate( rContext ); |
| maRequired.evaluate( rContext ); |
| maConstraint.evaluate( rContext ); |
| // type is static; does not need updating |
| |
| // evaluate the locally defined MIPs, and push them to the model |
| pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() ); |
| } |
| } |
| |
| |
| // helper for Binding::valueModified |
| void lcl_modified( const Binding::XModifyListener_t xListener, |
| const Reference<XInterface> xSource ) |
| { |
| OSL_ENSURE( xListener.is(), "no listener?" ); |
| xListener->modified( EventObject( xSource ) ); |
| } |
| |
| // helper for Binding::valueModified |
| void lcl_listentry( const Binding::XListEntryListener_t xListener, |
| const Reference<XInterface> xSource ) |
| { |
| OSL_ENSURE( xListener.is(), "no listener?" ); |
| // TODO: send fine granular events |
| xListener->allEntriesChanged( EventObject( xSource ) ); |
| } |
| |
| // helper for Binding::valueModified |
| void lcl_validate( const Binding::XValidityConstraintListener_t xListener, |
| const Reference<XInterface> xSource ) |
| { |
| OSL_ENSURE( xListener.is(), "no listener?" ); |
| xListener->validityConstraintChanged( EventObject( xSource ) ); |
| } |
| |
| |
| void Binding::valueModified() |
| { |
| // defer notifications, if so desired |
| if( mnDeferModifyNotifications > 0 ) |
| { |
| mbValueModified = true; |
| return; |
| } |
| mbValueModified = false; |
| |
| // query MIP used by our first node (also note validity) |
| Reference<XNode> xNode = maBindingExpression.getNode(); |
| maMIP = getModelImpl()->queryMIP( xNode ); |
| |
| // distribute MIPs _used_ by this binding |
| if( xNode.is() ) |
| { |
| notifyAndCachePropertyValue( HANDLE_ReadOnly ); |
| notifyAndCachePropertyValue( HANDLE_Relevant ); |
| } |
| |
| // iterate over _value_ listeners and send each a modified signal, |
| // using this object as source (will also update validity, because |
| // control will query once the value has changed) |
| Reference<XInterface> xSource = static_cast<XPropertySet*>( this ); |
| ::std::for_each( maModifyListeners.begin(), |
| maModifyListeners.end(), |
| ::std::bind2nd( ::std::ptr_fun( lcl_modified ), xSource ) ); |
| ::std::for_each( maListEntryListeners.begin(), |
| maListEntryListeners.end(), |
| ::std::bind2nd( ::std::ptr_fun( lcl_listentry ), xSource ) ); |
| ::std::for_each( maValidityListeners.begin(), |
| maValidityListeners.end(), |
| ::std::bind2nd( ::std::ptr_fun( lcl_validate ), xSource ) ); |
| |
| // now distribute MIPs to childs |
| if( xNode.is() ) |
| distributeMIP( xNode->getFirstChild() ); |
| } |
| |
| void Binding::distributeMIP( const XNode_t & rxNode ) { |
| |
| typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t; |
| OUString sEventName( RTL_CONSTASCII_USTRINGPARAM("xforms-generic") ); |
| XFormsEvent_t *pEvent = new XFormsEvent_t; |
| pEvent->initXFormsEvent(sEventName, sal_True, sal_False); |
| Reference<XEvent> xEvent(pEvent); |
| |
| // naive depth-first traversal |
| XNode_t xNode( rxNode ); |
| while(xNode.is()) { |
| |
| // notifications should be triggered at the |
| // leaf nodes first, bubbling upwards the hierarchy. |
| XNode_t child(xNode->getFirstChild()); |
| if(child.is()) |
| distributeMIP(child); |
| |
| // we're standing at a particular node somewhere |
| // below the one which changed a property (MIP). |
| // bindings which are listening at this node will receive |
| // a notification message about what exactly happened. |
| Reference< XEventTarget > target(xNode,UNO_QUERY); |
| target->dispatchEvent(xEvent); |
| |
| xNode = xNode->getNextSibling(); |
| }; |
| } |
| |
| void Binding::bindingModified() |
| { |
| // defer notifications, if so desired |
| if( mnDeferModifyNotifications > 0 ) |
| { |
| mbBindingModified = true; |
| return; |
| } |
| mbBindingModified = false; |
| |
| // rebind (if live); then call valueModified |
| // A binding should be inert until its model is fully constructed. |
| if( isLive() ) |
| { |
| bind( true ); |
| valueModified(); |
| } |
| } |
| |
| |
| MIP Binding::getLocalMIP() const |
| { |
| MIP aMIP; |
| |
| if( maReadonly.hasValue() ) |
| aMIP.setReadonly( maReadonly.getBool( false ) ); |
| if( maRelevant.hasValue() ) |
| aMIP.setRelevant( maRelevant.getBool( true ) ); |
| if( maRequired.hasValue() ) |
| aMIP.setRequired( maRequired.getBool( false ) ); |
| if( maConstraint.hasValue() ) |
| { |
| aMIP.setConstraint( maConstraint.getBool( true ) ); |
| if( ! aMIP.isConstraint() ) |
| aMIP.setConstraintExplanation( msExplainConstraint ); |
| } |
| if( msTypeName.getLength() > 0 ) |
| aMIP.setTypeName( msTypeName ); |
| |
| // calculate: only handle presence of calculate; value set elsewhere |
| aMIP.setHasCalculate( !maCalculate.isEmptyExpression() ); |
| |
| return aMIP; |
| } |
| |
| Binding::XDataType_t Binding::getDataType() |
| { |
| OSL_ENSURE( getModel().is(), "need model" ); |
| OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" ); |
| |
| Reference<XDataTypeRepository> xRepository( |
| getModel()->getDataTypeRepository(), UNO_QUERY ); |
| OUString sTypeName = maMIP.getTypeName(); |
| |
| return ( xRepository.is() && xRepository->hasByName( sTypeName ) ) |
| ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY) |
| : Reference<XDataType>( NULL ); |
| } |
| |
| bool Binding::isValid_DataType() |
| { |
| Reference<XDataType> xDataType = getDataType(); |
| return xDataType.is() |
| ? xDataType->validate( maBindingExpression.getString() ) |
| : true; |
| } |
| |
| rtl::OUString Binding::explainInvalid_DataType() |
| { |
| Reference<XDataType> xDataType = getDataType(); |
| return xDataType.is() |
| ? xDataType->explainInvalid( maBindingExpression.getString() ) |
| : OUString(); |
| } |
| |
| void Binding::clear() |
| { |
| // remove MIPs contributed by this binding |
| Model* pModel = getModelImpl(); |
| if( pModel != NULL ) |
| pModel->removeMIPs( this ); |
| |
| // remove all references |
| for( XNodes_t::iterator aIter = maEventNodes.begin(); |
| aIter != maEventNodes.end(); |
| aIter ++ ) |
| lcl_removeListenerFromNode( *aIter, this ); |
| maEventNodes.clear(); |
| |
| // clear expressions |
| maBindingExpression.clear(); |
| maReadonly.clear(); |
| maRelevant.clear(); |
| maRequired.clear(); |
| maConstraint.clear(); |
| maCalculate.clear(); |
| |
| // TODO: what about our listeners? |
| } |
| |
| |
| void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom, |
| Binding::XNameContainer_t& xTo ) |
| { |
| OSL_ENSURE( xFrom.is(), "no source" ); |
| OSL_ENSURE( xTo.is(), "no target" ); |
| |
| // iterate over name in source |
| Sequence<OUString> aNames = xTo->getElementNames(); |
| sal_Int32 nNames = aNames.getLength(); |
| const OUString* pNames = aNames.getConstArray(); |
| for( sal_Int32 i = 0; i < nNames; i++ ) |
| { |
| const OUString& rName = pNames[i]; |
| |
| if( ! xFrom->hasByName( rName ) ) |
| xTo->removeByName( rName ); |
| } |
| } |
| |
| /** copy namespaces from one namespace container into another |
| * @param bOverwrite true: overwrite namespaces in target |
| * false: do not overwrite namespaces in target |
| * @param bMove true: move namespaces (i.e., delete in source) |
| * false: copy namespaces (do not modify source) |
| * @param bFromSource true: use elements from source |
| * false: use only elements from target |
| */ |
| void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom, |
| Binding::XNameContainer_t& xTo, |
| bool bOverwrite ) |
| { |
| OSL_ENSURE( xFrom.is(), "no source" ); |
| OSL_ENSURE( xTo.is(), "no target" ); |
| |
| // iterate over name in source |
| Sequence<OUString> aNames = xFrom->getElementNames(); |
| sal_Int32 nNames = aNames.getLength(); |
| const OUString* pNames = aNames.getConstArray(); |
| for( sal_Int32 i = 0; i < nNames; i++ ) |
| { |
| const OUString& rName = pNames[i]; |
| |
| // determine whether to copy the value, and whether to delete |
| // it in the source: |
| |
| bool bInTarget = xTo->hasByName( rName ); |
| |
| // we copy: if property is in target, and |
| // if bOverwrite is set, or when the namespace prefix is free |
| bool bCopy = bOverwrite || ! bInTarget; |
| |
| // and now... ACTION! |
| if( bCopy ) |
| { |
| if( bInTarget ) |
| xTo->replaceByName( rName, xFrom->getByName( rName ) ); |
| else |
| xTo->insertByName( rName, xFrom->getByName( rName ) ); |
| } |
| } |
| } |
| |
| // implement get*Namespaces() |
| // (identical for both variants) |
| Binding::XNameContainer_t Binding::_getNamespaces() const |
| { |
| XNameContainer_t xNamespaces = new NameContainer<OUString>(); |
| lcl_copyNamespaces( mxNamespaces, xNamespaces, true ); |
| |
| // merge model's with binding's own namespaces |
| Model* pModel = getModelImpl(); |
| if( pModel != NULL ) |
| lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false ); |
| |
| return xNamespaces; |
| } |
| |
| // implement set*Namespaces() |
| // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces |
| void Binding::_setNamespaces( const XNameContainer_t& rNamespaces, |
| bool bBinding ) |
| { |
| Model* pModel = getModelImpl(); |
| XNameContainer_t xModelNamespaces = ( pModel != NULL ) |
| ? pModel->getNamespaces() |
| : NULL; |
| OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?"); |
| |
| // remove deleted namespaces |
| lcl_removeOtherNamespaces( rNamespaces, mxNamespaces ); |
| if( !bBinding && xModelNamespaces.is() ) |
| lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces ); |
| |
| // copy namespaces as appropriate |
| Sequence<OUString> aNames = rNamespaces->getElementNames(); |
| sal_Int32 nNames = aNames.getLength(); |
| const OUString* pNames = aNames.getConstArray(); |
| for( sal_Int32 i = 0; i < nNames; i++ ) |
| { |
| const OUString& rName = pNames[i]; |
| Any aValue = rNamespaces->getByName( rName ); |
| |
| // determine whether the namespace should go into model's or |
| // into binding's namespaces |
| bool bLocal = |
| ! xModelNamespaces.is() |
| || mxNamespaces->hasByName( rName ) |
| || ( bBinding |
| && xModelNamespaces.is() |
| && xModelNamespaces->hasByName( rName ) ); |
| |
| // write namespace into the appropriate namespace container |
| XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces; |
| OSL_ENSURE( rWhich.is(), "whoops" ); |
| if( rWhich->hasByName( rName ) ) |
| rWhich->replaceByName( rName, aValue ); |
| else |
| rWhich->insertByName( rName, aValue ); |
| |
| // always 'promote' namespaces from binding to model, if equal |
| if( xModelNamespaces.is() |
| && xModelNamespaces->hasByName( rName ) |
| && mxNamespaces->hasByName( rName ) |
| && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) ) |
| { |
| mxNamespaces->removeByName( rName ); |
| } |
| } |
| |
| // ... done. But we modified the binding! |
| bindingModified(); |
| } |
| |
| void Binding::_checkBindingID() |
| { |
| if( getModel().is() ) |
| { |
| Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW ); |
| if( msBindingID.getLength() == 0 ) |
| { |
| // no binding ID? then make one up! |
| OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ); |
| sIDPrefix += String::CreateFromAscii( " " ); |
| sal_Int32 nNumber = 0; |
| OUString sName; |
| do |
| { |
| nNumber++; |
| sName = sIDPrefix + OUString::valueOf( nNumber ); |
| } |
| while( xBindings->hasByName( sName ) ); |
| setBindingID( sName ); |
| } |
| } |
| } |
| |
| |
| |
| |
| // |
| // XValueBinding |
| // |
| |
| Binding::Sequence_Type_t Binding::getSupportedValueTypes() |
| throw( RuntimeException ) |
| { |
| return Convert::get().getTypes(); |
| } |
| |
| sal_Bool Binding::supportsType( const Type_t& rType ) |
| throw( RuntimeException ) |
| { |
| return Convert::get().hasType( rType ); |
| } |
| |
| Binding::Any_t Binding::getValue( const Type_t& rType ) |
| throw( IncompatibleTypesException, |
| RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // second, check for type |
| if( ! supportsType( rType ) ) |
| throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); |
| |
| // return string value (if present; else return empty Any) |
| Binding::Any_t result = Any(); |
| if(maBindingExpression.hasValue()) { |
| rtl::OUString pathExpr(maBindingExpression.getString()); |
| Convert &rConvert = Convert::get(); |
| result = rConvert.toAny(pathExpr,rType); |
| } |
| |
| // return maBindingExpression.hasValue() |
| // ? Convert::get().toAny( maBindingExpression.getString(), rType ) |
| // : Any(); |
| |
| return result; |
| } |
| |
| void Binding::setValue( const Any_t& aValue ) |
| throw( IncompatibleTypesException, |
| InvalidBindingStateException, |
| NoSupportException, |
| RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // check for supported type |
| if( ! supportsType( aValue.getValueType() ) ) |
| throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); |
| |
| if( maBindingExpression.hasValue() ) |
| { |
| Binding::XNode_t xNode = maBindingExpression.getNode(); |
| if( xNode.is() ) |
| { |
| OUString sValue = Convert::get().toXSD( aValue ); |
| bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue ); |
| if( ! bSuccess ) |
| throw InvalidBindingStateException( EXCEPT( "can't set value" ) ); |
| } |
| else |
| throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); |
| } |
| else |
| throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); |
| } |
| |
| |
| // |
| // XListEntry Source |
| // |
| |
| sal_Int32 Binding::getListEntryCount() |
| throw( RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // return size of node list |
| return maBindingExpression.getNodeList().size(); |
| } |
| |
| void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer ) |
| { |
| if( xNode->getNodeType() == NodeType_TEXT_NODE |
| || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE ) |
| { |
| rBuffer.append( xNode->getNodeValue() ); |
| } |
| else |
| { |
| for( Reference<XNode> xChild = xNode->getFirstChild(); |
| xChild.is(); |
| xChild = xChild->getNextSibling() ) |
| { |
| lcl_getString( xChild, rBuffer ); |
| } |
| } |
| } |
| |
| OUString lcl_getString( const Reference<XNode>& xNode ) |
| { |
| OUStringBuffer aBuffer; |
| lcl_getString( xNode, aBuffer ); |
| return aBuffer.makeStringAndClear(); |
| } |
| |
| OUString Binding::getListEntry( sal_Int32 nPosition ) |
| throw( IndexOutOfBoundsException, |
| RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // check bounds and return proper item |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); |
| if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) ) |
| throw IndexOutOfBoundsException( EXCEPT("") ); |
| return lcl_getString( aNodes[ nPosition ] ); |
| } |
| |
| Sequence<OUString> Binding::getAllListEntries() |
| throw( RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // create sequence of string values |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); |
| Sequence<OUString> aSequence( aNodes.size() ); |
| OUString* pSequence = aSequence.getArray(); |
| for( sal_Int32 n = 0; n < aSequence.getLength(); n++ ) |
| { |
| pSequence[n] = lcl_getString( aNodes[n] ); |
| } |
| |
| return aSequence; |
| } |
| |
| void Binding::addListEntryListener( const XListEntryListener_t& xListener ) |
| throw( NullPointerException, |
| RuntimeException ) |
| { |
| OSL_ENSURE( xListener.is(), "need listener!" ); |
| if( ::std::find( maListEntryListeners.begin(), |
| maListEntryListeners.end(), |
| xListener) |
| == maListEntryListeners.end() ) |
| maListEntryListeners.push_back( xListener ); |
| } |
| |
| void Binding::removeListEntryListener( const XListEntryListener_t& xListener ) |
| throw( NullPointerException, |
| RuntimeException ) |
| { |
| XListEntryListeners_t::iterator aIter = |
| ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(), |
| xListener ); |
| if( aIter != maListEntryListeners.end() ) |
| maListEntryListeners.erase( aIter ); |
| } |
| |
| |
| // |
| // XValidator |
| // |
| |
| sal_Bool Binding::isValid( const Any_t& ) |
| throw( RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // ignore value; determine validate only on current data |
| return isValid(); |
| } |
| |
| rtl::OUString Binding::explainInvalid( |
| const Any_t& /*Value*/ ) |
| throw( RuntimeException ) |
| { |
| // first, check for model |
| checkLive(); |
| |
| // ignore value; determine explanation only on current data |
| return explainInvalid(); |
| } |
| |
| void Binding::addValidityConstraintListener( |
| const XValidityConstraintListener_t& xListener ) |
| throw( NullPointerException, |
| RuntimeException ) |
| { |
| OSL_ENSURE( xListener.is(), "need listener!" ); |
| if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener) |
| == maValidityListeners.end() ) |
| maValidityListeners.push_back( xListener ); |
| } |
| |
| void Binding::removeValidityConstraintListener( |
| const XValidityConstraintListener_t& xListener ) |
| throw( NullPointerException, |
| RuntimeException ) |
| { |
| XValidityConstraintListeners_t::iterator aIter = |
| ::std::find( maValidityListeners.begin(), maValidityListeners.end(), |
| xListener ); |
| if( aIter != maValidityListeners.end() ) |
| maValidityListeners.erase( aIter ); |
| } |
| |
| |
| |
| // |
| // xml::dom::event::XEventListener |
| // |
| |
| void Binding::handleEvent( const XEvent_t& xEvent ) |
| throw( RuntimeException ) |
| { |
| OUString sType(xEvent->getType()); |
| //OUString sEventMIPChanged(RTL_CONSTASCII_USTRINGPARAM("xforms-generic")); |
| //if(sType.equals(sEventMIPChanged)) { |
| if(!sType.compareToAscii("xforms-generic")) { |
| |
| // the modification of the 'mnDeferModifyNotifications'-member |
| // is necessary to prevent infinite notication looping. |
| // This can happend in case the binding which caused |
| // the notification chain is listening to those events |
| // as well... |
| bool bPreserveValueModified = mbValueModified; |
| mnDeferModifyNotifications++; |
| valueModified(); |
| --mnDeferModifyNotifications; |
| mbValueModified = bPreserveValueModified; |
| return; |
| } |
| |
| // if we're a dynamic binding, we better re-bind, too! |
| bind( false ); |
| |
| // our value was maybe modified |
| valueModified(); |
| } |
| |
| |
| // |
| // lang::XUnoTunnel |
| // |
| |
| sal_Int64 Binding::getSomething( const IntSequence_t& xId ) |
| throw( RuntimeException ) |
| { |
| return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL ); |
| } |
| |
| // |
| // XCloneable |
| // |
| |
| Binding::XCloneable_t SAL_CALL Binding::createClone() |
| throw( RuntimeException ) |
| { |
| Reference< XPropertySet > xClone; |
| |
| Model* pModel = getModelImpl(); |
| if ( pModel ) |
| xClone = pModel->cloneBinding( this ); |
| else |
| { |
| xClone = new Binding; |
| copy( this, xClone ); |
| } |
| return XCloneable_t( xClone, UNO_QUERY ); |
| } |
| |
| // |
| // property set implementations |
| // |
| |
| #define REGISTER_PROPERTY( property, type ) \ |
| registerProperty( PROPERTY( property, type ), \ |
| new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) ); |
| |
| #define REGISTER_PROPERTY_RO( property, type ) \ |
| registerProperty( PROPERTY_RO( property, type ), \ |
| new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) ); |
| |
| #define REGISTER_BOOL_PROPERTY_RO( property ) \ |
| registerProperty( PROPERTY_RO( property, sal_Bool ), \ |
| new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) ); |
| |
| void Binding::initializePropertySet() |
| { |
| REGISTER_PROPERTY ( BindingID, OUString ); |
| REGISTER_PROPERTY ( BindingExpression, OUString ); |
| REGISTER_PROPERTY_RO ( Model, Model_t ); |
| REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t ); |
| REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t ); |
| REGISTER_PROPERTY_RO ( ModelID, OUString ); |
| REGISTER_PROPERTY ( ReadonlyExpression, OUString ); |
| REGISTER_PROPERTY ( RelevantExpression, OUString ); |
| REGISTER_PROPERTY ( RequiredExpression, OUString ); |
| REGISTER_PROPERTY ( ConstraintExpression, OUString ); |
| REGISTER_PROPERTY ( CalculateExpression, OUString ); |
| REGISTER_PROPERTY ( Type, OUString ); |
| REGISTER_PROPERTY_RO ( ReadOnly, bool ); |
| REGISTER_PROPERTY_RO ( Relevant, bool ); |
| REGISTER_BOOL_PROPERTY_RO( ExternalData ); |
| |
| initializePropertyValueCache( HANDLE_ReadOnly ); |
| initializePropertyValueCache( HANDLE_Relevant ); |
| initializePropertyValueCache( HANDLE_ExternalData ); |
| } |
| |
| void Binding::addModifyListener( |
| const XModifyListener_t& xListener ) |
| throw( RuntimeException ) |
| { |
| OSL_ENSURE( xListener.is(), "need listener!" ); |
| if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ) |
| == maModifyListeners.end() ) |
| maModifyListeners.push_back( xListener ); |
| |
| // HACK: currently, we have to 'push' some MIPs to the control |
| // (read-only, relevant, etc.) To enable this, we need to update |
| // the control at least once when it registers here. |
| valueModified(); |
| } |
| |
| void Binding::removeModifyListener( |
| const XModifyListener_t& xListener ) |
| throw( RuntimeException ) |
| { |
| ModifyListeners_t::iterator aIter = |
| ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ); |
| if( aIter != maModifyListeners.end() ) |
| maModifyListeners.erase( aIter ); |
| } |
| |
| |
| |
| |
| rtl::OUString Binding::getName() |
| throw( RuntimeException ) |
| { |
| return getBindingID(); |
| } |
| |
| void SAL_CALL Binding::setName( const rtl::OUString& rName ) |
| throw( RuntimeException ) |
| { |
| // use the XPropertySet methods, so the change in the name is notified to the |
| // property listeners |
| setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) ); |
| } |