diff options
Diffstat (limited to 'forms/source/xforms/model.cxx')
-rw-r--r-- | forms/source/xforms/model.cxx | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/forms/source/xforms/model.cxx b/forms/source/xforms/model.cxx new file mode 100644 index 000000000000..0d81c7056bec --- /dev/null +++ b/forms/source/xforms/model.cxx @@ -0,0 +1,808 @@ +/************************************************************************* + * + * 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 "model.hxx" + +#include "model_helper.hxx" +#include "unohelper.hxx" +#include "binding.hxx" +#include "submission.hxx" +#include "mip.hxx" +#include "evaluationcontext.hxx" +#include "xmlhelper.hxx" +#include "datatyperepository.hxx" +#include "NameContainer.hxx" + +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> + +#include <comphelper/propertysetinfo.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <algorithm> + +// UNO classes +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/dom/XCharacterData.hpp> +#include <com/sun/star/xml/dom/NodeType.hpp> +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/io/XInputStream.hpp> + + +using com::sun::star::lang::XMultiServiceFactory; +using com::sun::star::lang::XUnoTunnel; +using com::sun::star::beans::XPropertySet; +using com::sun::star::beans::PropertyValue; +using rtl::OUString; +using rtl::OUStringBuffer; +using com::sun::star::beans::PropertyVetoException; +using com::sun::star::beans::UnknownPropertyException; +using com::sun::star::util::VetoException; +using com::sun::star::lang::WrappedTargetException; +using com::sun::star::lang::IllegalArgumentException; +using com::sun::star::ucb::XSimpleFileAccess; +using com::sun::star::io::XInputStream; + +using namespace com::sun::star::uno; +using namespace com::sun::star::xml::dom; +using namespace xforms; + + +#if OSL_DEBUG_LEVEL > 1 +#define DBG_INVARIANT_TYPE(TYPE) class DBG_##TYPE { const TYPE* mpT; void check() { mpT->dbg_assertInvariant(); } public: DBG_##TYPE(const TYPE* pT) : mpT(pT) { check(); } ~DBG_##TYPE() { check(); } } _DBG_##TYPE(this); + +#define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model) +#else +#define DBG_INVARIANT_TYPE(TYPE) +#define DBG_INVARIANT() +#endif + + + +// +// The Model +// + +void Model::ensureAtLeastOneInstance() +{ + if( ! mpInstances->hasItems() ) + { + // create a default instance + newInstance( OUString(), OUString(), true ); + } +} + + + +/** Model default constructor; create empty model */ +Model::Model() : + msID(), + mpBindings( NULL ), + mpSubmissions( NULL ), + mpInstances( new InstanceCollection ), + mxNamespaces( new NameContainer<OUString>() ), + mxBindings( mpBindings ), + mxSubmissions( mpSubmissions ), + mxInstances( mpInstances ), + mbInitialized( false ), + mbExternalData( true ) +{ + initializePropertySet(); + + // initialize bindings collections + // (not in initializer list to avoid use of incomplete 'this') + mpBindings = new BindingCollection( this ); + mxBindings = mpBindings; + + mpSubmissions = new SubmissionCollection( this ); + mxSubmissions = mpSubmissions; + + // invariant only holds after construction + DBG_INVARIANT(); +} + +Model::~Model() throw() +{ + // give up bindings & submissions; the mxBindings/mxSubmissions + // references will then delete them + mpBindings = NULL; + mpSubmissions = NULL; +} + +Model* lcl_getModel( const Reference<XUnoTunnel>& xTunnel ) +{ + Model* pModel = NULL; + if( xTunnel.is() ) + pModel = reinterpret_cast<Model*>( + xTunnel->getSomething( Model::getUnoTunnelID() ) ); + return pModel; +} + +Model* Model::getModel( const Reference<XModel>& xModel ) +{ + return lcl_getModel( Reference<XUnoTunnel>( xModel, UNO_QUERY ) ); +} + +EvaluationContext Model::getEvaluationContext() +{ + // the default context is the top-level element node. A default + // node (instanceData' is inserted when there is no default node + Reference<XDocument> xInstance = getDefaultInstance(); + Reference<XNode> xElement( xInstance->getDocumentElement(), UNO_QUERY ); + + // no element found? Then insert default element 'instanceData' + if( ! xElement.is() ) + { + xElement = Reference<XNode>( + xInstance->createElement( OUSTRING("instanceData") ), + UNO_QUERY_THROW ); + Reference<XNode>( xInstance, UNO_QUERY_THROW)->appendChild( xElement ); + } + + OSL_ENSURE( xElement.is() && + xElement->getNodeType() == NodeType_ELEMENT_NODE, + "no element in evaluation context" ); + + return EvaluationContext( xElement, this, mxNamespaces, 0, 1 ); +} + + +Model::IntSequence_t Model::getUnoTunnelID() +{ + static cppu::OImplementationId aImplementationId; + return aImplementationId.getImplementationId(); +} + +Model::XDocument_t Model::getForeignSchema() const +{ + return mxForeignSchema; +} + +void Model::setForeignSchema( const XDocument_t& rDocument ) +{ + mxForeignSchema = rDocument; +} + +rtl::OUString Model::getSchemaRef() const +{ + return msSchemaRef; +} + +void Model::setSchemaRef( const rtl::OUString& rSchemaRef ) +{ + msSchemaRef = rSchemaRef; +} + +Model::XNameContainer_t Model::getNamespaces() const +{ + return mxNamespaces; +} + +void Model::setNamespaces( const XNameContainer_t& rNamespaces ) +{ + if( rNamespaces.is() ) + mxNamespaces = rNamespaces; +} + +bool Model::getExternalData() const +{ + return mbExternalData; +} + +void Model::setExternalData( bool _bData ) +{ + mbExternalData = _bData; +} + +#if OSL_DEBUG_LEVEL > 1 +void Model::dbg_assertInvariant() const +{ + OSL_ENSURE( mpInstances != NULL, "no instances found" ); + OSL_ENSURE( mxInstances.is(), "No instance container!" ); + // OSL_ENSURE( mxInstances->hasElements(), "no instance!" ); + + OSL_ENSURE( mpBindings != NULL, "no bindings element" ); + OSL_ENSURE( mxBindings.is(), "No Bindings container" ); + + OSL_ENSURE( mpSubmissions != NULL, "no submissions element" ); + OSL_ENSURE( mxSubmissions.is(), "No Submission container" ); + + + + /* + // check bindings, and things that have to do with our binding + std::vector<MIP*> aAllMIPs; // check MIP map + sal_Int32 nCount = mpBindings->countItems(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + Binding* pBind = Binding::getBinding( + mpBindings->Collection<XPropertySet_t>::getItem( i ) ); + + // examine and check binding + OSL_ENSURE( pBind != NULL, "invalid binding found" ); + + OSL_ENSURE( Model::getModel( pBind->getModel() ) == this, + "our binding doesn't know us."); + // check this binding's MIP against MIP map + MIP* pMIP = const_cast<MIP*>( pBind->_getMIP() ); + sal_Int32 nFound = 0; + if( pMIP != NULL ) + { + aAllMIPs.push_back( pMIP ); + for( MIPs_t::const_iterator aIter = maMIPs.begin(); + aIter != maMIPs.end(); + aIter++ ) + { + if( pMIP == aIter->second ) + nFound++; + } + } + OSL_ENSURE( ( pMIP == NULL ) == ( nFound == 0 ), "MIP-map wrong" ); + } + + // check MIP map for left-over MIPs + for( MIPs_t::const_iterator aIter = maMIPs.begin(); + aIter != maMIPs.end(); + aIter++ ) + { + MIP* pMIP = aIter->second; + std::vector<MIP*>::iterator aFound = + std::find( aAllMIPs.begin(), aAllMIPs.end(), pMIP ); + if( aFound != aAllMIPs.end() ) + aAllMIPs.erase( aFound ); + } + OSL_ENSURE( aAllMIPs.empty(), "lonely MIPs found!" ); + */ +} +#endif + + +// +// MIP managment +// + +void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP ) +{ + OSL_ENSURE( pTag != NULL, "empty tag?" ); + OSL_ENSURE( xNode.is(), "no node" ); + + MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) ); + maMIPs.insert( aValue ); +} + +void Model::removeMIPs( void* pTag ) +{ + OSL_ENSURE( pTag != NULL, "empty tag?" ); + + for( MIPs_t::iterator aIter = maMIPs.begin(); + aIter != maMIPs.end(); ) + { + if( aIter->second.first == pTag ) + { + MIPs_t::iterator next( aIter ); ++next; + maMIPs.erase( aIter ); + aIter = next; + } + else + ++aIter; + } +} + +MIP Model::queryMIP( const XNode_t& xNode ) const +{ + // OSL_ENSURE( xNode.is(), "no node" ); + + // travel up inheritance chain and inherit MIPs + MIP aRet; + for( XNode_t xCurrent = xNode; + xCurrent.is(); + xCurrent = xCurrent->getParentNode() ) + { + // iterate over all MIPs for this node, and join MIPs + MIP aMIP; + MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent ); + MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent ); + for( ; aIter != aEnd; aIter++ ) + aMIP.join( aIter->second.second ); + + // inherit from current node (or set if we are at the start node) + if( xCurrent == xNode ) + aRet = aMIP; + else + aRet.inherit( aMIP ); + } + + return aRet; +} + + + +void Model::rebind() +{ + OSL_ENSURE( mpBindings != NULL, "bindings?" ); + + // iterate over all bindings and call update + sal_Int32 nCount = mpBindings->countItems(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) ); + OSL_ENSURE( pBind != NULL, "binding?" ); + pBind->update(); + } +} + + + +void Model::deferNotifications( bool bDefer ) +{ + // iterate over all bindings and defer notifications + sal_Int32 nCount = mpBindings->countItems(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) ); + OSL_ENSURE( pBind != NULL, "binding?" ); + pBind->deferNotifications( bDefer ); + } +} + + +bool Model::setSimpleContent( const XNode_t& xConstNode, + const rtl::OUString& sValue ) +{ + OSL_ENSURE( xConstNode.is(), "need node to set data" ); + + bool bRet = false; + if( xConstNode.is() ) + { + // non-const node reference so we can assign children (if necessary) + XNode_t xNode( xConstNode ); + + switch( xNode->getNodeType() ) + { + case NodeType_ELEMENT_NODE: + { + // find first text node child + Reference<XNode> xChild; + for( xChild = xNode->getFirstChild(); + xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE; + xChild = xChild->getNextSibling() ) + ; // empty loop; only find first text node child + + // create text node, if none is found + if( ! xChild.is() ) + { + xChild = Reference<XNode>( + xNode->getOwnerDocument()->createTextNode( OUString() ), + UNO_QUERY_THROW ); + xNode->appendChild( xChild ); + } + xNode = xChild; + + OSL_ENSURE( xNode.is() && + xNode->getNodeType() == NodeType_TEXT_NODE, + "text node creation failed?" ); + } + // no break; continue as with text node: + + case NodeType_TEXT_NODE: + case NodeType_ATTRIBUTE_NODE: + { + // set the node value (defer notifications) + if( xNode->getNodeValue() != sValue ) + { + deferNotifications( true ); + xNode->setNodeValue( sValue ); + deferNotifications( false ); + } + bRet = true; + } + break; + + default: + { + OSL_ENSURE( false, "bound to unknown node type?" ); + } + break; + + } + } + return bRet; +} + +void Model::loadInstance( sal_Int32 nInstance ) +{ + Sequence<PropertyValue> aSequence = mpInstances->getItem( nInstance ); + + // find URL from instance + OUString sURL; + bool bOnce = false; + getInstanceData( aSequence, NULL, NULL, &sURL, &bOnce ); + + // if we have a URL, load the document and set it into the instance + if( sURL.getLength() > 0 ) + { + try + { + Reference<XInputStream> xInput = + Reference<XSimpleFileAccess>( + createInstance( + OUSTRING("com.sun.star.ucb.SimpleFileAccess") ), + UNO_QUERY_THROW )->openFileRead( sURL ); + if( xInput.is() ) + { + Reference<XDocument> xInstance = + getDocumentBuilder()->parse( xInput ); + if( xInstance.is() ) + { + OUString sEmpty; + setInstanceData( aSequence, NULL, &xInstance, + bOnce ? &sEmpty : &sURL, NULL); + mpInstances->setItem( nInstance, aSequence ); + } + } + } + catch( const Exception& ) + { + // couldn't load the instance -> ignore! + } + } +} + +void Model::loadInstances() +{ + // iterate over instance array to get PropertyValue-Sequence + const sal_Int32 nInstances = mpInstances->countItems(); + for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ ) + { + loadInstance( nInstance ); + } +} + +bool Model::isInitialized() const +{ + return mbInitialized; +} + +bool Model::isValid() const +{ + bool bValid = true; + sal_Int32 nCount = mpBindings->countItems(); + for( sal_Int32 i = 0; bValid && i < nCount; i++ ) + { + Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) ); + OSL_ENSURE( pBind != NULL, "binding?" ); + bValid = pBind->isValid(); + } + return bValid; +} + + + +// +// implement xforms::XModel +// + +rtl::OUString Model::getID() + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return msID; +} + +void Model::setID( const rtl::OUString& sID ) + throw( RuntimeException ) +{ + DBG_INVARIANT(); + msID = sID; +} + +void Model::initialize() + throw( RuntimeException ) +{ + DBG_ASSERT( ! mbInitialized, "model already initialized" ); + + // load instances + loadInstances(); + + // let's pretend we're initialized and rebind all bindings + mbInitialized = true; + rebind(); +} + +void Model::rebuild() + throw( RuntimeException ) +{ + if( ! mbInitialized ) + initialize(); + else + rebind(); +} + +void Model::recalculate() + throw( RuntimeException ) +{ + rebind(); +} + +void Model::revalidate() + throw( RuntimeException ) +{ + // do nothing. We don't validate anyways! +} + +void Model::refresh() + throw( RuntimeException ) +{ + rebind(); +} + + +void SAL_CALL Model::submitWithInteraction( + const rtl::OUString& sID, + const XInteractionHandler_t& _rxHandler ) + throw( VetoException, + WrappedTargetException, + RuntimeException ) +{ + DBG_INVARIANT(); + + if( mpSubmissions->hasItem( sID ) ) + { + Submission* pSubmission = + Submission::getSubmission( mpSubmissions->getItem( sID ) ); + OSL_ENSURE( pSubmission != NULL, "no submission?" ); + OSL_ENSURE( pSubmission->getModel() == Reference<XModel>( this ), + "wrong model" ); + + // submit. All exceptions are allowed to leave. + pSubmission->submitWithInteraction( _rxHandler ); + } +} + +void Model::submit( const rtl::OUString& sID ) + throw( VetoException, WrappedTargetException, RuntimeException ) +{ + submitWithInteraction( sID, NULL ); +} + +Model::XDataTypeRepository_t SAL_CALL Model::getDataTypeRepository( ) + throw( RuntimeException ) +{ + if ( !mxDataTypes.is() ) + mxDataTypes = new ODataTypeRepository; + + return mxDataTypes; +} + +// +// instance management +// + +Model::XSet_t Model::getInstances() + throw( RuntimeException ) +{ + return mxInstances; +} + +Model::XDocument_t Model::getInstanceDocument( const rtl::OUString& rName ) + throw( RuntimeException ) +{ + ensureAtLeastOneInstance(); + Reference<XDocument> aInstance; + sal_Int32 nInstance = lcl_findInstance( mpInstances, rName ); + if( nInstance != -1 ) + getInstanceData( mpInstances->getItem( nInstance ), + NULL, &aInstance, NULL, NULL ); + return aInstance; +} + +Model::XDocument_t SAL_CALL Model::getDefaultInstance() + throw( RuntimeException ) +{ + ensureAtLeastOneInstance(); + DBG_ASSERT( mpInstances->countItems() > 0, "no instance?" ); + Reference<XDocument> aInstance; + getInstanceData( mpInstances->getItem( 0 ), NULL, &aInstance, NULL, NULL ); + return aInstance; +} + + + +// +// bindings management +// + +Model::XPropertySet_t SAL_CALL Model::createBinding() + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return new Binding(); +} + +Model::XPropertySet_t Model::cloneBinding( const XPropertySet_t& xBinding ) + throw( RuntimeException ) +{ + DBG_INVARIANT(); + XPropertySet_t xNewBinding = createBinding(); + copy( xBinding, xNewBinding ); + return xNewBinding; +} + +Model::XPropertySet_t Model::getBinding( const rtl::OUString& sId ) + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return mpBindings->hasItem( sId ) ? mpBindings->getItem( sId ) : NULL; +} + +Model::XSet_t Model::getBindings() + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return mxBindings; +} + + + +// +// submission management +// + +Model::XSubmission_t Model::createSubmission() + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return new Submission(); +} + +Model::XSubmission_t Model::cloneSubmission(const XPropertySet_t& xSubmission) + throw( RuntimeException ) +{ + DBG_INVARIANT(); + XSubmission_t xNewSubmission = createSubmission(); + XPropertySet_t xAsPropertySet( xNewSubmission.get() ); + copy( xSubmission.get(), xAsPropertySet ); + return xNewSubmission; +} + +Model::XSubmission_t Model::getSubmission( const rtl::OUString& sId ) + throw( RuntimeException ) +{ + DBG_INVARIANT(); + XSubmission_t xSubmission; + if ( mpSubmissions->hasItem( sId ) ) + xSubmission = xSubmission.query( mpSubmissions->getItem( sId ) ); + return xSubmission; +} + +Model::XSet_t Model::getSubmissions() + throw( RuntimeException ) +{ + DBG_INVARIANT(); + return mxSubmissions; +} + + + +// +// implementation of XFormsUIHelper1 interface +// can be found in file model_ui.cxx +// + + + +// +// implement XPropertySet & friends +// + +#define HANDLE_ID 0 +#define HANDLE_Instance 1 +#define HANDLE_InstanceURL 2 +#define HANDLE_ForeignSchema 3 +#define HANDLE_SchemaRef 4 +#define HANDLE_Namespaces 5 +#define HANDLE_ExternalData 6 + +#define REGISTER_PROPERTY( property, type ) \ + registerProperty( PROPERTY( property, type ), \ + new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) ); + +#define REGISTER_PROPERTY_API( property, type ) \ + registerProperty( PROPERTY( property, type ), \ + new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) ); + +#define REGISTER_BOOL_PROPERTY( property ) \ + registerProperty( PROPERTY( property, sal_Bool ), \ + new BooleanPropertyAccessor< Model, bool >( this, &Model::set##property, &Model::get##property ) ); + +void Model::initializePropertySet() +{ + REGISTER_PROPERTY_API ( ID, OUString ); + REGISTER_PROPERTY ( ForeignSchema, XDocument_t ); + REGISTER_PROPERTY ( SchemaRef, OUString ); + REGISTER_PROPERTY ( Namespaces, XNameContainer_t ); + REGISTER_BOOL_PROPERTY( ExternalData ); +} + +void Model::update() + throw( RuntimeException ) +{ + rebuild(); +} + + +sal_Int64 Model::getSomething( const IntSequence_t& xId ) + throw( RuntimeException ) +{ + return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL ); +} + +Sequence<sal_Int8> Model::getImplementationId() + throw( RuntimeException ) +{ + return getUnoTunnelID(); +} + + +// +// 'shift' operators for getting data into and out of Anys +// + +void operator <<= ( com::sun::star::uno::Any& rAny, + xforms::Model* pModel) +{ + Reference<XPropertySet> xPropSet( static_cast<XPropertySet*>( pModel ) ); + rAny <<= xPropSet; +} + +bool operator >>= ( xforms::Model* pModel, + com::sun::star::uno::Any& rAny ) +{ + bool bRet = false; + + // acquire model pointer through XUnoTunnel + Reference<XUnoTunnel> xTunnel( rAny, UNO_QUERY ); + if( xTunnel.is() ) + { + pModel = reinterpret_cast<xforms::Model*>( + xTunnel->getSomething( xforms::Model::getUnoTunnelID() ) ); + bRet = true; + } + + return bRet; +} |