diff options
Diffstat (limited to 'forms/source/xforms/binding.cxx')
-rw-r--r-- | forms/source/xforms/binding.cxx | 1412 |
1 files changed, 0 insertions, 1412 deletions
diff --git a/forms/source/xforms/binding.cxx b/forms/source/xforms/binding.cxx deleted file mode 100644 index 4c08e36cf..000000000 --- a/forms/source/xforms/binding.cxx +++ /dev/null @@ -1,1412 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/************************************************************************* - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2000, 2010 Oracle and/or its affiliates. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 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 ) ); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |