masterfix DEV300: #i10000# build fix
[LibreOffice.git] / forms / source / xforms / model.cxx
blob0d81c7056bec0c19880a2b3ed750ddaf24338fb3
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_forms.hxx"
31 #include "model.hxx"
33 #include "model_helper.hxx"
34 #include "unohelper.hxx"
35 #include "binding.hxx"
36 #include "submission.hxx"
37 #include "mip.hxx"
38 #include "evaluationcontext.hxx"
39 #include "xmlhelper.hxx"
40 #include "datatyperepository.hxx"
41 #include "NameContainer.hxx"
43 #include <rtl/ustring.hxx>
44 #include <rtl/ustrbuf.hxx>
45 #include <tools/debug.hxx>
47 #include <comphelper/propertysetinfo.hxx>
48 #include <cppuhelper/typeprovider.hxx>
50 #include <algorithm>
52 // UNO classes
53 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
54 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
55 #include <com/sun/star/lang/IllegalArgumentException.hpp>
56 #include <com/sun/star/xml/dom/XDocument.hpp>
57 #include <com/sun/star/xml/dom/XCharacterData.hpp>
58 #include <com/sun/star/xml/dom/NodeType.hpp>
59 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
60 #include <com/sun/star/uno/Sequence.hxx>
61 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
62 #include <com/sun/star/beans/PropertyValue.hpp>
63 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
64 #include <com/sun/star/io/XInputStream.hpp>
67 using com::sun::star::lang::XMultiServiceFactory;
68 using com::sun::star::lang::XUnoTunnel;
69 using com::sun::star::beans::XPropertySet;
70 using com::sun::star::beans::PropertyValue;
71 using rtl::OUString;
72 using rtl::OUStringBuffer;
73 using com::sun::star::beans::PropertyVetoException;
74 using com::sun::star::beans::UnknownPropertyException;
75 using com::sun::star::util::VetoException;
76 using com::sun::star::lang::WrappedTargetException;
77 using com::sun::star::lang::IllegalArgumentException;
78 using com::sun::star::ucb::XSimpleFileAccess;
79 using com::sun::star::io::XInputStream;
81 using namespace com::sun::star::uno;
82 using namespace com::sun::star::xml::dom;
83 using namespace xforms;
86 #if OSL_DEBUG_LEVEL > 1
87 #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);
89 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
90 #else
91 #define DBG_INVARIANT_TYPE(TYPE)
92 #define DBG_INVARIANT()
93 #endif
98 // The Model
101 void Model::ensureAtLeastOneInstance()
103 if( ! mpInstances->hasItems() )
105 // create a default instance
106 newInstance( OUString(), OUString(), true );
112 /** Model default constructor; create empty model */
113 Model::Model() :
114 msID(),
115 mpBindings( NULL ),
116 mpSubmissions( NULL ),
117 mpInstances( new InstanceCollection ),
118 mxNamespaces( new NameContainer<OUString>() ),
119 mxBindings( mpBindings ),
120 mxSubmissions( mpSubmissions ),
121 mxInstances( mpInstances ),
122 mbInitialized( false ),
123 mbExternalData( true )
125 initializePropertySet();
127 // initialize bindings collections
128 // (not in initializer list to avoid use of incomplete 'this')
129 mpBindings = new BindingCollection( this );
130 mxBindings = mpBindings;
132 mpSubmissions = new SubmissionCollection( this );
133 mxSubmissions = mpSubmissions;
135 // invariant only holds after construction
136 DBG_INVARIANT();
139 Model::~Model() throw()
141 // give up bindings & submissions; the mxBindings/mxSubmissions
142 // references will then delete them
143 mpBindings = NULL;
144 mpSubmissions = NULL;
147 Model* lcl_getModel( const Reference<XUnoTunnel>& xTunnel )
149 Model* pModel = NULL;
150 if( xTunnel.is() )
151 pModel = reinterpret_cast<Model*>(
152 xTunnel->getSomething( Model::getUnoTunnelID() ) );
153 return pModel;
156 Model* Model::getModel( const Reference<XModel>& xModel )
158 return lcl_getModel( Reference<XUnoTunnel>( xModel, UNO_QUERY ) );
161 EvaluationContext Model::getEvaluationContext()
163 // the default context is the top-level element node. A default
164 // node (instanceData' is inserted when there is no default node
165 Reference<XDocument> xInstance = getDefaultInstance();
166 Reference<XNode> xElement( xInstance->getDocumentElement(), UNO_QUERY );
168 // no element found? Then insert default element 'instanceData'
169 if( ! xElement.is() )
171 xElement = Reference<XNode>(
172 xInstance->createElement( OUSTRING("instanceData") ),
173 UNO_QUERY_THROW );
174 Reference<XNode>( xInstance, UNO_QUERY_THROW)->appendChild( xElement );
177 OSL_ENSURE( xElement.is() &&
178 xElement->getNodeType() == NodeType_ELEMENT_NODE,
179 "no element in evaluation context" );
181 return EvaluationContext( xElement, this, mxNamespaces, 0, 1 );
185 Model::IntSequence_t Model::getUnoTunnelID()
187 static cppu::OImplementationId aImplementationId;
188 return aImplementationId.getImplementationId();
191 Model::XDocument_t Model::getForeignSchema() const
193 return mxForeignSchema;
196 void Model::setForeignSchema( const XDocument_t& rDocument )
198 mxForeignSchema = rDocument;
201 rtl::OUString Model::getSchemaRef() const
203 return msSchemaRef;
206 void Model::setSchemaRef( const rtl::OUString& rSchemaRef )
208 msSchemaRef = rSchemaRef;
211 Model::XNameContainer_t Model::getNamespaces() const
213 return mxNamespaces;
216 void Model::setNamespaces( const XNameContainer_t& rNamespaces )
218 if( rNamespaces.is() )
219 mxNamespaces = rNamespaces;
222 bool Model::getExternalData() const
224 return mbExternalData;
227 void Model::setExternalData( bool _bData )
229 mbExternalData = _bData;
232 #if OSL_DEBUG_LEVEL > 1
233 void Model::dbg_assertInvariant() const
235 OSL_ENSURE( mpInstances != NULL, "no instances found" );
236 OSL_ENSURE( mxInstances.is(), "No instance container!" );
237 // OSL_ENSURE( mxInstances->hasElements(), "no instance!" );
239 OSL_ENSURE( mpBindings != NULL, "no bindings element" );
240 OSL_ENSURE( mxBindings.is(), "No Bindings container" );
242 OSL_ENSURE( mpSubmissions != NULL, "no submissions element" );
243 OSL_ENSURE( mxSubmissions.is(), "No Submission container" );
248 // check bindings, and things that have to do with our binding
249 std::vector<MIP*> aAllMIPs; // check MIP map
250 sal_Int32 nCount = mpBindings->countItems();
251 for( sal_Int32 i = 0; i < nCount; i++ )
253 Binding* pBind = Binding::getBinding(
254 mpBindings->Collection<XPropertySet_t>::getItem( i ) );
256 // examine and check binding
257 OSL_ENSURE( pBind != NULL, "invalid binding found" );
259 OSL_ENSURE( Model::getModel( pBind->getModel() ) == this,
260 "our binding doesn't know us.");
261 // check this binding's MIP against MIP map
262 MIP* pMIP = const_cast<MIP*>( pBind->_getMIP() );
263 sal_Int32 nFound = 0;
264 if( pMIP != NULL )
266 aAllMIPs.push_back( pMIP );
267 for( MIPs_t::const_iterator aIter = maMIPs.begin();
268 aIter != maMIPs.end();
269 aIter++ )
271 if( pMIP == aIter->second )
272 nFound++;
275 OSL_ENSURE( ( pMIP == NULL ) == ( nFound == 0 ), "MIP-map wrong" );
278 // check MIP map for left-over MIPs
279 for( MIPs_t::const_iterator aIter = maMIPs.begin();
280 aIter != maMIPs.end();
281 aIter++ )
283 MIP* pMIP = aIter->second;
284 std::vector<MIP*>::iterator aFound =
285 std::find( aAllMIPs.begin(), aAllMIPs.end(), pMIP );
286 if( aFound != aAllMIPs.end() )
287 aAllMIPs.erase( aFound );
289 OSL_ENSURE( aAllMIPs.empty(), "lonely MIPs found!" );
292 #endif
296 // MIP managment
299 void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP )
301 OSL_ENSURE( pTag != NULL, "empty tag?" );
302 OSL_ENSURE( xNode.is(), "no node" );
304 MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) );
305 maMIPs.insert( aValue );
308 void Model::removeMIPs( void* pTag )
310 OSL_ENSURE( pTag != NULL, "empty tag?" );
312 for( MIPs_t::iterator aIter = maMIPs.begin();
313 aIter != maMIPs.end(); )
315 if( aIter->second.first == pTag )
317 MIPs_t::iterator next( aIter ); ++next;
318 maMIPs.erase( aIter );
319 aIter = next;
321 else
322 ++aIter;
326 MIP Model::queryMIP( const XNode_t& xNode ) const
328 // OSL_ENSURE( xNode.is(), "no node" );
330 // travel up inheritance chain and inherit MIPs
331 MIP aRet;
332 for( XNode_t xCurrent = xNode;
333 xCurrent.is();
334 xCurrent = xCurrent->getParentNode() )
336 // iterate over all MIPs for this node, and join MIPs
337 MIP aMIP;
338 MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent );
339 MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent );
340 for( ; aIter != aEnd; aIter++ )
341 aMIP.join( aIter->second.second );
343 // inherit from current node (or set if we are at the start node)
344 if( xCurrent == xNode )
345 aRet = aMIP;
346 else
347 aRet.inherit( aMIP );
350 return aRet;
355 void Model::rebind()
357 OSL_ENSURE( mpBindings != NULL, "bindings?" );
359 // iterate over all bindings and call update
360 sal_Int32 nCount = mpBindings->countItems();
361 for( sal_Int32 i = 0; i < nCount; i++ )
363 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
364 OSL_ENSURE( pBind != NULL, "binding?" );
365 pBind->update();
371 void Model::deferNotifications( bool bDefer )
373 // iterate over all bindings and defer notifications
374 sal_Int32 nCount = mpBindings->countItems();
375 for( sal_Int32 i = 0; i < nCount; i++ )
377 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
378 OSL_ENSURE( pBind != NULL, "binding?" );
379 pBind->deferNotifications( bDefer );
384 bool Model::setSimpleContent( const XNode_t& xConstNode,
385 const rtl::OUString& sValue )
387 OSL_ENSURE( xConstNode.is(), "need node to set data" );
389 bool bRet = false;
390 if( xConstNode.is() )
392 // non-const node reference so we can assign children (if necessary)
393 XNode_t xNode( xConstNode );
395 switch( xNode->getNodeType() )
397 case NodeType_ELEMENT_NODE:
399 // find first text node child
400 Reference<XNode> xChild;
401 for( xChild = xNode->getFirstChild();
402 xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE;
403 xChild = xChild->getNextSibling() )
404 ; // empty loop; only find first text node child
406 // create text node, if none is found
407 if( ! xChild.is() )
409 xChild = Reference<XNode>(
410 xNode->getOwnerDocument()->createTextNode( OUString() ),
411 UNO_QUERY_THROW );
412 xNode->appendChild( xChild );
414 xNode = xChild;
416 OSL_ENSURE( xNode.is() &&
417 xNode->getNodeType() == NodeType_TEXT_NODE,
418 "text node creation failed?" );
420 // no break; continue as with text node:
422 case NodeType_TEXT_NODE:
423 case NodeType_ATTRIBUTE_NODE:
425 // set the node value (defer notifications)
426 if( xNode->getNodeValue() != sValue )
428 deferNotifications( true );
429 xNode->setNodeValue( sValue );
430 deferNotifications( false );
432 bRet = true;
434 break;
436 default:
438 OSL_ENSURE( false, "bound to unknown node type?" );
440 break;
444 return bRet;
447 void Model::loadInstance( sal_Int32 nInstance )
449 Sequence<PropertyValue> aSequence = mpInstances->getItem( nInstance );
451 // find URL from instance
452 OUString sURL;
453 bool bOnce = false;
454 getInstanceData( aSequence, NULL, NULL, &sURL, &bOnce );
456 // if we have a URL, load the document and set it into the instance
457 if( sURL.getLength() > 0 )
461 Reference<XInputStream> xInput =
462 Reference<XSimpleFileAccess>(
463 createInstance(
464 OUSTRING("com.sun.star.ucb.SimpleFileAccess") ),
465 UNO_QUERY_THROW )->openFileRead( sURL );
466 if( xInput.is() )
468 Reference<XDocument> xInstance =
469 getDocumentBuilder()->parse( xInput );
470 if( xInstance.is() )
472 OUString sEmpty;
473 setInstanceData( aSequence, NULL, &xInstance,
474 bOnce ? &sEmpty : &sURL, NULL);
475 mpInstances->setItem( nInstance, aSequence );
479 catch( const Exception& )
481 // couldn't load the instance -> ignore!
486 void Model::loadInstances()
488 // iterate over instance array to get PropertyValue-Sequence
489 const sal_Int32 nInstances = mpInstances->countItems();
490 for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ )
492 loadInstance( nInstance );
496 bool Model::isInitialized() const
498 return mbInitialized;
501 bool Model::isValid() const
503 bool bValid = true;
504 sal_Int32 nCount = mpBindings->countItems();
505 for( sal_Int32 i = 0; bValid && i < nCount; i++ )
507 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
508 OSL_ENSURE( pBind != NULL, "binding?" );
509 bValid = pBind->isValid();
511 return bValid;
517 // implement xforms::XModel
520 rtl::OUString Model::getID()
521 throw( RuntimeException )
523 DBG_INVARIANT();
524 return msID;
527 void Model::setID( const rtl::OUString& sID )
528 throw( RuntimeException )
530 DBG_INVARIANT();
531 msID = sID;
534 void Model::initialize()
535 throw( RuntimeException )
537 DBG_ASSERT( ! mbInitialized, "model already initialized" );
539 // load instances
540 loadInstances();
542 // let's pretend we're initialized and rebind all bindings
543 mbInitialized = true;
544 rebind();
547 void Model::rebuild()
548 throw( RuntimeException )
550 if( ! mbInitialized )
551 initialize();
552 else
553 rebind();
556 void Model::recalculate()
557 throw( RuntimeException )
559 rebind();
562 void Model::revalidate()
563 throw( RuntimeException )
565 // do nothing. We don't validate anyways!
568 void Model::refresh()
569 throw( RuntimeException )
571 rebind();
575 void SAL_CALL Model::submitWithInteraction(
576 const rtl::OUString& sID,
577 const XInteractionHandler_t& _rxHandler )
578 throw( VetoException,
579 WrappedTargetException,
580 RuntimeException )
582 DBG_INVARIANT();
584 if( mpSubmissions->hasItem( sID ) )
586 Submission* pSubmission =
587 Submission::getSubmission( mpSubmissions->getItem( sID ) );
588 OSL_ENSURE( pSubmission != NULL, "no submission?" );
589 OSL_ENSURE( pSubmission->getModel() == Reference<XModel>( this ),
590 "wrong model" );
592 // submit. All exceptions are allowed to leave.
593 pSubmission->submitWithInteraction( _rxHandler );
597 void Model::submit( const rtl::OUString& sID )
598 throw( VetoException, WrappedTargetException, RuntimeException )
600 submitWithInteraction( sID, NULL );
603 Model::XDataTypeRepository_t SAL_CALL Model::getDataTypeRepository( )
604 throw( RuntimeException )
606 if ( !mxDataTypes.is() )
607 mxDataTypes = new ODataTypeRepository;
609 return mxDataTypes;
613 // instance management
616 Model::XSet_t Model::getInstances()
617 throw( RuntimeException )
619 return mxInstances;
622 Model::XDocument_t Model::getInstanceDocument( const rtl::OUString& rName )
623 throw( RuntimeException )
625 ensureAtLeastOneInstance();
626 Reference<XDocument> aInstance;
627 sal_Int32 nInstance = lcl_findInstance( mpInstances, rName );
628 if( nInstance != -1 )
629 getInstanceData( mpInstances->getItem( nInstance ),
630 NULL, &aInstance, NULL, NULL );
631 return aInstance;
634 Model::XDocument_t SAL_CALL Model::getDefaultInstance()
635 throw( RuntimeException )
637 ensureAtLeastOneInstance();
638 DBG_ASSERT( mpInstances->countItems() > 0, "no instance?" );
639 Reference<XDocument> aInstance;
640 getInstanceData( mpInstances->getItem( 0 ), NULL, &aInstance, NULL, NULL );
641 return aInstance;
647 // bindings management
650 Model::XPropertySet_t SAL_CALL Model::createBinding()
651 throw( RuntimeException )
653 DBG_INVARIANT();
654 return new Binding();
657 Model::XPropertySet_t Model::cloneBinding( const XPropertySet_t& xBinding )
658 throw( RuntimeException )
660 DBG_INVARIANT();
661 XPropertySet_t xNewBinding = createBinding();
662 copy( xBinding, xNewBinding );
663 return xNewBinding;
666 Model::XPropertySet_t Model::getBinding( const rtl::OUString& sId )
667 throw( RuntimeException )
669 DBG_INVARIANT();
670 return mpBindings->hasItem( sId ) ? mpBindings->getItem( sId ) : NULL;
673 Model::XSet_t Model::getBindings()
674 throw( RuntimeException )
676 DBG_INVARIANT();
677 return mxBindings;
683 // submission management
686 Model::XSubmission_t Model::createSubmission()
687 throw( RuntimeException )
689 DBG_INVARIANT();
690 return new Submission();
693 Model::XSubmission_t Model::cloneSubmission(const XPropertySet_t& xSubmission)
694 throw( RuntimeException )
696 DBG_INVARIANT();
697 XSubmission_t xNewSubmission = createSubmission();
698 XPropertySet_t xAsPropertySet( xNewSubmission.get() );
699 copy( xSubmission.get(), xAsPropertySet );
700 return xNewSubmission;
703 Model::XSubmission_t Model::getSubmission( const rtl::OUString& sId )
704 throw( RuntimeException )
706 DBG_INVARIANT();
707 XSubmission_t xSubmission;
708 if ( mpSubmissions->hasItem( sId ) )
709 xSubmission = xSubmission.query( mpSubmissions->getItem( sId ) );
710 return xSubmission;
713 Model::XSet_t Model::getSubmissions()
714 throw( RuntimeException )
716 DBG_INVARIANT();
717 return mxSubmissions;
723 // implementation of XFormsUIHelper1 interface
724 // can be found in file model_ui.cxx
730 // implement XPropertySet & friends
733 #define HANDLE_ID 0
734 #define HANDLE_Instance 1
735 #define HANDLE_InstanceURL 2
736 #define HANDLE_ForeignSchema 3
737 #define HANDLE_SchemaRef 4
738 #define HANDLE_Namespaces 5
739 #define HANDLE_ExternalData 6
741 #define REGISTER_PROPERTY( property, type ) \
742 registerProperty( PROPERTY( property, type ), \
743 new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
745 #define REGISTER_PROPERTY_API( property, type ) \
746 registerProperty( PROPERTY( property, type ), \
747 new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
749 #define REGISTER_BOOL_PROPERTY( property ) \
750 registerProperty( PROPERTY( property, sal_Bool ), \
751 new BooleanPropertyAccessor< Model, bool >( this, &Model::set##property, &Model::get##property ) );
753 void Model::initializePropertySet()
755 REGISTER_PROPERTY_API ( ID, OUString );
756 REGISTER_PROPERTY ( ForeignSchema, XDocument_t );
757 REGISTER_PROPERTY ( SchemaRef, OUString );
758 REGISTER_PROPERTY ( Namespaces, XNameContainer_t );
759 REGISTER_BOOL_PROPERTY( ExternalData );
762 void Model::update()
763 throw( RuntimeException )
765 rebuild();
769 sal_Int64 Model::getSomething( const IntSequence_t& xId )
770 throw( RuntimeException )
772 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
775 Sequence<sal_Int8> Model::getImplementationId()
776 throw( RuntimeException )
778 return getUnoTunnelID();
783 // 'shift' operators for getting data into and out of Anys
786 void operator <<= ( com::sun::star::uno::Any& rAny,
787 xforms::Model* pModel)
789 Reference<XPropertySet> xPropSet( static_cast<XPropertySet*>( pModel ) );
790 rAny <<= xPropSet;
793 bool operator >>= ( xforms::Model* pModel,
794 com::sun::star::uno::Any& rAny )
796 bool bRet = false;
798 // acquire model pointer through XUnoTunnel
799 Reference<XUnoTunnel> xTunnel( rAny, UNO_QUERY );
800 if( xTunnel.is() )
802 pModel = reinterpret_cast<xforms::Model*>(
803 xTunnel->getSomething( xforms::Model::getUnoTunnelID() ) );
804 bRet = true;
807 return bRet;