update dev300-m58
[ooovba.git] / forms / source / xforms / model.cxx
blobba011a4e0d8088b1cfc8e69862804ed64705dd7b
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: model.cxx,v $
10 * $Revision: 1.10 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_forms.hxx"
34 #include "model.hxx"
36 #include "model_helper.hxx"
37 #include "unohelper.hxx"
38 #include "binding.hxx"
39 #include "submission.hxx"
40 #include "mip.hxx"
41 #include "evaluationcontext.hxx"
42 #include "xmlhelper.hxx"
43 #include "datatyperepository.hxx"
44 #include "NameContainer.hxx"
46 #include <rtl/ustring.hxx>
47 #include <rtl/ustrbuf.hxx>
48 #include <tools/debug.hxx>
50 #include <comphelper/propertysetinfo.hxx>
51 #include <cppuhelper/typeprovider.hxx>
53 #include <algorithm>
55 // UNO classes
56 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
57 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
58 #include <com/sun/star/lang/IllegalArgumentException.hpp>
59 #include <com/sun/star/xml/dom/XDocument.hpp>
60 #include <com/sun/star/xml/dom/XCharacterData.hpp>
61 #include <com/sun/star/xml/dom/NodeType.hpp>
62 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
63 #include <com/sun/star/uno/Sequence.hxx>
64 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
65 #include <com/sun/star/beans/PropertyValue.hpp>
66 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
67 #include <com/sun/star/io/XInputStream.hpp>
70 using com::sun::star::lang::XMultiServiceFactory;
71 using com::sun::star::lang::XUnoTunnel;
72 using com::sun::star::beans::XPropertySet;
73 using com::sun::star::beans::PropertyValue;
74 using rtl::OUString;
75 using rtl::OUStringBuffer;
76 using com::sun::star::beans::PropertyVetoException;
77 using com::sun::star::beans::UnknownPropertyException;
78 using com::sun::star::util::VetoException;
79 using com::sun::star::lang::WrappedTargetException;
80 using com::sun::star::lang::IllegalArgumentException;
81 using com::sun::star::ucb::XSimpleFileAccess;
82 using com::sun::star::io::XInputStream;
84 using namespace com::sun::star::uno;
85 using namespace com::sun::star::xml::dom;
86 using namespace xforms;
89 #if OSL_DEBUG_LEVEL > 1
90 #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);
92 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
93 #else
94 #define DBG_INVARIANT_TYPE(TYPE)
95 #define DBG_INVARIANT()
96 #endif
101 // The Model
104 void Model::ensureAtLeastOneInstance()
106 if( ! mpInstances->hasItems() )
108 // create a default instance
109 newInstance( OUString(), OUString(), true );
115 /** Model default constructor; create empty model */
116 Model::Model() :
117 msID(),
118 mpBindings( NULL ),
119 mpSubmissions( NULL ),
120 mpInstances( new InstanceCollection ),
121 mxNamespaces( new NameContainer<OUString>() ),
122 mxBindings( mpBindings ),
123 mxSubmissions( mpSubmissions ),
124 mxInstances( mpInstances ),
125 mbInitialized( false ),
126 mbExternalData( true )
128 initializePropertySet();
130 // initialize bindings collections
131 // (not in initializer list to avoid use of incomplete 'this')
132 mpBindings = new BindingCollection( this );
133 mxBindings = mpBindings;
135 mpSubmissions = new SubmissionCollection( this );
136 mxSubmissions = mpSubmissions;
138 // invariant only holds after construction
139 DBG_INVARIANT();
142 Model::~Model() throw()
144 // give up bindings & submissions; the mxBindings/mxSubmissions
145 // references will then delete them
146 mpBindings = NULL;
147 mpSubmissions = NULL;
150 Model* lcl_getModel( const Reference<XUnoTunnel>& xTunnel )
152 Model* pModel = NULL;
153 if( xTunnel.is() )
154 pModel = reinterpret_cast<Model*>(
155 xTunnel->getSomething( Model::getUnoTunnelID() ) );
156 return pModel;
159 Model* Model::getModel( const Reference<XModel>& xModel )
161 return lcl_getModel( Reference<XUnoTunnel>( xModel, UNO_QUERY ) );
164 EvaluationContext Model::getEvaluationContext()
166 // the default context is the top-level element node. A default
167 // node (instanceData' is inserted when there is no default node
168 Reference<XDocument> xInstance = getDefaultInstance();
169 Reference<XNode> xElement( xInstance->getDocumentElement(), UNO_QUERY );
171 // no element found? Then insert default element 'instanceData'
172 if( ! xElement.is() )
174 xElement = Reference<XNode>(
175 xInstance->createElement( OUSTRING("instanceData") ),
176 UNO_QUERY_THROW );
177 Reference<XNode>( xInstance, UNO_QUERY_THROW)->appendChild( xElement );
180 OSL_ENSURE( xElement.is() &&
181 xElement->getNodeType() == NodeType_ELEMENT_NODE,
182 "no element in evaluation context" );
184 return EvaluationContext( xElement, this, mxNamespaces, 0, 1 );
188 Model::IntSequence_t Model::getUnoTunnelID()
190 static cppu::OImplementationId aImplementationId;
191 return aImplementationId.getImplementationId();
194 Model::XDocument_t Model::getForeignSchema() const
196 return mxForeignSchema;
199 void Model::setForeignSchema( const XDocument_t& rDocument )
201 mxForeignSchema = rDocument;
204 rtl::OUString Model::getSchemaRef() const
206 return msSchemaRef;
209 void Model::setSchemaRef( const rtl::OUString& rSchemaRef )
211 msSchemaRef = rSchemaRef;
214 Model::XNameContainer_t Model::getNamespaces() const
216 return mxNamespaces;
219 void Model::setNamespaces( const XNameContainer_t& rNamespaces )
221 if( rNamespaces.is() )
222 mxNamespaces = rNamespaces;
225 bool Model::getExternalData() const
227 return mbExternalData;
230 void Model::setExternalData( bool _bData )
232 mbExternalData = _bData;
235 #if OSL_DEBUG_LEVEL > 1
236 void Model::dbg_assertInvariant() const
238 OSL_ENSURE( mpInstances != NULL, "no instances found" );
239 OSL_ENSURE( mxInstances.is(), "No instance container!" );
240 // OSL_ENSURE( mxInstances->hasElements(), "no instance!" );
242 OSL_ENSURE( mpBindings != NULL, "no bindings element" );
243 OSL_ENSURE( mxBindings.is(), "No Bindings container" );
245 OSL_ENSURE( mpSubmissions != NULL, "no submissions element" );
246 OSL_ENSURE( mxSubmissions.is(), "No Submission container" );
251 // check bindings, and things that have to do with our binding
252 std::vector<MIP*> aAllMIPs; // check MIP map
253 sal_Int32 nCount = mpBindings->countItems();
254 for( sal_Int32 i = 0; i < nCount; i++ )
256 Binding* pBind = Binding::getBinding(
257 mpBindings->Collection<XPropertySet_t>::getItem( i ) );
259 // examine and check binding
260 OSL_ENSURE( pBind != NULL, "invalid binding found" );
262 OSL_ENSURE( Model::getModel( pBind->getModel() ) == this,
263 "our binding doesn't know us.");
264 // check this binding's MIP against MIP map
265 MIP* pMIP = const_cast<MIP*>( pBind->_getMIP() );
266 sal_Int32 nFound = 0;
267 if( pMIP != NULL )
269 aAllMIPs.push_back( pMIP );
270 for( MIPs_t::const_iterator aIter = maMIPs.begin();
271 aIter != maMIPs.end();
272 aIter++ )
274 if( pMIP == aIter->second )
275 nFound++;
278 OSL_ENSURE( ( pMIP == NULL ) == ( nFound == 0 ), "MIP-map wrong" );
281 // check MIP map for left-over MIPs
282 for( MIPs_t::const_iterator aIter = maMIPs.begin();
283 aIter != maMIPs.end();
284 aIter++ )
286 MIP* pMIP = aIter->second;
287 std::vector<MIP*>::iterator aFound =
288 std::find( aAllMIPs.begin(), aAllMIPs.end(), pMIP );
289 if( aFound != aAllMIPs.end() )
290 aAllMIPs.erase( aFound );
292 OSL_ENSURE( aAllMIPs.empty(), "lonely MIPs found!" );
295 #endif
299 // MIP managment
302 void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP )
304 OSL_ENSURE( pTag != NULL, "empty tag?" );
305 OSL_ENSURE( xNode.is(), "no node" );
307 MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) );
308 maMIPs.insert( aValue );
311 void Model::removeMIPs( void* pTag )
313 OSL_ENSURE( pTag != NULL, "empty tag?" );
315 for( MIPs_t::iterator aIter = maMIPs.begin();
316 aIter != maMIPs.end(); )
318 if( aIter->second.first == pTag )
320 MIPs_t::iterator next( aIter ); ++next;
321 maMIPs.erase( aIter );
322 aIter = next;
324 else
325 ++aIter;
329 MIP Model::queryMIP( const XNode_t& xNode ) const
331 // OSL_ENSURE( xNode.is(), "no node" );
333 // travel up inheritance chain and inherit MIPs
334 MIP aRet;
335 for( XNode_t xCurrent = xNode;
336 xCurrent.is();
337 xCurrent = xCurrent->getParentNode() )
339 // iterate over all MIPs for this node, and join MIPs
340 MIP aMIP;
341 MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent );
342 MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent );
343 for( ; aIter != aEnd; aIter++ )
344 aMIP.join( aIter->second.second );
346 // inherit from current node (or set if we are at the start node)
347 if( xCurrent == xNode )
348 aRet = aMIP;
349 else
350 aRet.inherit( aMIP );
353 return aRet;
358 void Model::rebind()
360 OSL_ENSURE( mpBindings != NULL, "bindings?" );
362 // iterate over all bindings and call update
363 sal_Int32 nCount = mpBindings->countItems();
364 for( sal_Int32 i = 0; i < nCount; i++ )
366 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
367 OSL_ENSURE( pBind != NULL, "binding?" );
368 pBind->update();
374 void Model::deferNotifications( bool bDefer )
376 // iterate over all bindings and defer notifications
377 sal_Int32 nCount = mpBindings->countItems();
378 for( sal_Int32 i = 0; i < nCount; i++ )
380 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
381 OSL_ENSURE( pBind != NULL, "binding?" );
382 pBind->deferNotifications( bDefer );
387 bool Model::setSimpleContent( const XNode_t& xConstNode,
388 const rtl::OUString& sValue )
390 OSL_ENSURE( xConstNode.is(), "need node to set data" );
392 bool bRet = false;
393 if( xConstNode.is() )
395 // non-const node reference so we can assign children (if necessary)
396 XNode_t xNode( xConstNode );
398 switch( xNode->getNodeType() )
400 case NodeType_ELEMENT_NODE:
402 // find first text node child
403 Reference<XNode> xChild;
404 for( xChild = xNode->getFirstChild();
405 xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE;
406 xChild = xChild->getNextSibling() )
407 ; // empty loop; only find first text node child
409 // create text node, if none is found
410 if( ! xChild.is() )
412 xChild = Reference<XNode>(
413 xNode->getOwnerDocument()->createTextNode( OUString() ),
414 UNO_QUERY_THROW );
415 xNode->appendChild( xChild );
417 xNode = xChild;
419 OSL_ENSURE( xNode.is() &&
420 xNode->getNodeType() == NodeType_TEXT_NODE,
421 "text node creation failed?" );
423 // no break; continue as with text node:
425 case NodeType_TEXT_NODE:
426 case NodeType_ATTRIBUTE_NODE:
428 // set the node value (defer notifications)
429 if( xNode->getNodeValue() != sValue )
431 deferNotifications( true );
432 xNode->setNodeValue( sValue );
433 deferNotifications( false );
435 bRet = true;
437 break;
439 default:
441 OSL_ENSURE( false, "bound to unknown node type?" );
443 break;
447 return bRet;
450 void Model::loadInstance( sal_Int32 nInstance )
452 Sequence<PropertyValue> aSequence = mpInstances->getItem( nInstance );
454 // find URL from instance
455 OUString sURL;
456 bool bOnce = false;
457 getInstanceData( aSequence, NULL, NULL, &sURL, &bOnce );
459 // if we have a URL, load the document and set it into the instance
460 if( sURL.getLength() > 0 )
464 Reference<XInputStream> xInput =
465 Reference<XSimpleFileAccess>(
466 createInstance(
467 OUSTRING("com.sun.star.ucb.SimpleFileAccess") ),
468 UNO_QUERY_THROW )->openFileRead( sURL );
469 if( xInput.is() )
471 Reference<XDocument> xInstance =
472 getDocumentBuilder()->parse( xInput );
473 if( xInstance.is() )
475 OUString sEmpty;
476 setInstanceData( aSequence, NULL, &xInstance,
477 bOnce ? &sEmpty : &sURL, NULL);
478 mpInstances->setItem( nInstance, aSequence );
482 catch( const Exception& )
484 // couldn't load the instance -> ignore!
489 void Model::loadInstances()
491 // iterate over instance array to get PropertyValue-Sequence
492 const sal_Int32 nInstances = mpInstances->countItems();
493 for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ )
495 loadInstance( nInstance );
499 bool Model::isInitialized() const
501 return mbInitialized;
504 bool Model::isValid() const
506 bool bValid = true;
507 sal_Int32 nCount = mpBindings->countItems();
508 for( sal_Int32 i = 0; bValid && i < nCount; i++ )
510 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
511 OSL_ENSURE( pBind != NULL, "binding?" );
512 bValid = pBind->isValid();
514 return bValid;
520 // implement xforms::XModel
523 rtl::OUString Model::getID()
524 throw( RuntimeException )
526 DBG_INVARIANT();
527 return msID;
530 void Model::setID( const rtl::OUString& sID )
531 throw( RuntimeException )
533 DBG_INVARIANT();
534 msID = sID;
537 void Model::initialize()
538 throw( RuntimeException )
540 DBG_ASSERT( ! mbInitialized, "model already initialized" );
542 // load instances
543 loadInstances();
545 // let's pretend we're initialized and rebind all bindings
546 mbInitialized = true;
547 rebind();
550 void Model::rebuild()
551 throw( RuntimeException )
553 if( ! mbInitialized )
554 initialize();
555 else
556 rebind();
559 void Model::recalculate()
560 throw( RuntimeException )
562 rebind();
565 void Model::revalidate()
566 throw( RuntimeException )
568 // do nothing. We don't validate anyways!
571 void Model::refresh()
572 throw( RuntimeException )
574 rebind();
578 void SAL_CALL Model::submitWithInteraction(
579 const rtl::OUString& sID,
580 const XInteractionHandler_t& _rxHandler )
581 throw( VetoException,
582 WrappedTargetException,
583 RuntimeException )
585 DBG_INVARIANT();
587 if( mpSubmissions->hasItem( sID ) )
589 Submission* pSubmission =
590 Submission::getSubmission( mpSubmissions->getItem( sID ) );
591 OSL_ENSURE( pSubmission != NULL, "no submission?" );
592 OSL_ENSURE( pSubmission->getModel() == Reference<XModel>( this ),
593 "wrong model" );
595 // submit. All exceptions are allowed to leave.
596 pSubmission->submitWithInteraction( _rxHandler );
600 void Model::submit( const rtl::OUString& sID )
601 throw( VetoException, WrappedTargetException, RuntimeException )
603 submitWithInteraction( sID, NULL );
606 Model::XDataTypeRepository_t SAL_CALL Model::getDataTypeRepository( )
607 throw( RuntimeException )
609 if ( !mxDataTypes.is() )
610 mxDataTypes = new ODataTypeRepository;
612 return mxDataTypes;
616 // instance management
619 Model::XSet_t Model::getInstances()
620 throw( RuntimeException )
622 return mxInstances;
625 Model::XDocument_t Model::getInstanceDocument( const rtl::OUString& rName )
626 throw( RuntimeException )
628 ensureAtLeastOneInstance();
629 Reference<XDocument> aInstance;
630 sal_Int32 nInstance = lcl_findInstance( mpInstances, rName );
631 if( nInstance != -1 )
632 getInstanceData( mpInstances->getItem( nInstance ),
633 NULL, &aInstance, NULL, NULL );
634 return aInstance;
637 Model::XDocument_t SAL_CALL Model::getDefaultInstance()
638 throw( RuntimeException )
640 ensureAtLeastOneInstance();
641 DBG_ASSERT( mpInstances->countItems() > 0, "no instance?" );
642 Reference<XDocument> aInstance;
643 getInstanceData( mpInstances->getItem( 0 ), NULL, &aInstance, NULL, NULL );
644 return aInstance;
650 // bindings management
653 Model::XPropertySet_t SAL_CALL Model::createBinding()
654 throw( RuntimeException )
656 DBG_INVARIANT();
657 return new Binding();
660 Model::XPropertySet_t Model::cloneBinding( const XPropertySet_t& xBinding )
661 throw( RuntimeException )
663 DBG_INVARIANT();
664 XPropertySet_t xNewBinding = createBinding();
665 copy( xBinding, xNewBinding );
666 return xNewBinding;
669 Model::XPropertySet_t Model::getBinding( const rtl::OUString& sId )
670 throw( RuntimeException )
672 DBG_INVARIANT();
673 return mpBindings->hasItem( sId ) ? mpBindings->getItem( sId ) : NULL;
676 Model::XSet_t Model::getBindings()
677 throw( RuntimeException )
679 DBG_INVARIANT();
680 return mxBindings;
686 // submission management
689 Model::XSubmission_t Model::createSubmission()
690 throw( RuntimeException )
692 DBG_INVARIANT();
693 return new Submission();
696 Model::XSubmission_t Model::cloneSubmission(const XPropertySet_t& xSubmission)
697 throw( RuntimeException )
699 DBG_INVARIANT();
700 XSubmission_t xNewSubmission = createSubmission();
701 XPropertySet_t xAsPropertySet( xNewSubmission.get() );
702 copy( xSubmission.get(), xAsPropertySet );
703 return xNewSubmission;
706 Model::XSubmission_t Model::getSubmission( const rtl::OUString& sId )
707 throw( RuntimeException )
709 DBG_INVARIANT();
710 XSubmission_t xSubmission;
711 if ( mpSubmissions->hasItem( sId ) )
712 xSubmission = xSubmission.query( mpSubmissions->getItem( sId ) );
713 return xSubmission;
716 Model::XSet_t Model::getSubmissions()
717 throw( RuntimeException )
719 DBG_INVARIANT();
720 return mxSubmissions;
726 // implementation of XFormsUIHelper1 interface
727 // can be found in file model_ui.cxx
733 // implement XPropertySet & friends
736 #define HANDLE_ID 0
737 #define HANDLE_Instance 1
738 #define HANDLE_InstanceURL 2
739 #define HANDLE_ForeignSchema 3
740 #define HANDLE_SchemaRef 4
741 #define HANDLE_Namespaces 5
742 #define HANDLE_ExternalData 6
744 #define REGISTER_PROPERTY( property, type ) \
745 registerProperty( PROPERTY( property, type ), \
746 new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
748 #define REGISTER_PROPERTY_API( property, type ) \
749 registerProperty( PROPERTY( property, type ), \
750 new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
752 #define REGISTER_BOOL_PROPERTY( property ) \
753 registerProperty( PROPERTY( property, sal_Bool ), \
754 new BooleanPropertyAccessor< Model, bool >( this, &Model::set##property, &Model::get##property ) );
756 void Model::initializePropertySet()
758 REGISTER_PROPERTY_API ( ID, OUString );
759 REGISTER_PROPERTY ( ForeignSchema, XDocument_t );
760 REGISTER_PROPERTY ( SchemaRef, OUString );
761 REGISTER_PROPERTY ( Namespaces, XNameContainer_t );
762 REGISTER_BOOL_PROPERTY( ExternalData );
765 void Model::update()
766 throw( RuntimeException )
768 rebuild();
772 sal_Int64 Model::getSomething( const IntSequence_t& xId )
773 throw( RuntimeException )
775 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
778 Sequence<sal_Int8> Model::getImplementationId()
779 throw( RuntimeException )
781 return getUnoTunnelID();
786 // 'shift' operators for getting data into and out of Anys
789 void operator <<= ( com::sun::star::uno::Any& rAny,
790 xforms::Model* pModel)
792 Reference<XPropertySet> xPropSet( static_cast<XPropertySet*>( pModel ) );
793 rAny <<= xPropSet;
796 bool operator >>= ( xforms::Model* pModel,
797 com::sun::star::uno::Any& rAny )
799 bool bRet = false;
801 // acquire model pointer through XUnoTunnel
802 Reference<XUnoTunnel> xTunnel( rAny, UNO_QUERY );
803 if( xTunnel.is() )
805 pModel = reinterpret_cast<xforms::Model*>(
806 xTunnel->getSomething( xforms::Model::getUnoTunnelID() ) );
807 bRet = true;
810 return bRet;