1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: model.cxx,v $
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"
36 #include "model_helper.hxx"
37 #include "unohelper.hxx"
38 #include "binding.hxx"
39 #include "submission.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>
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
;
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)
94 #define DBG_INVARIANT_TYPE(TYPE)
95 #define DBG_INVARIANT()
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 */
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
142 Model::~Model() throw()
144 // give up bindings & submissions; the mxBindings/mxSubmissions
145 // references will then delete them
147 mpSubmissions
= NULL
;
150 Model
* lcl_getModel( const Reference
<XUnoTunnel
>& xTunnel
)
152 Model
* pModel
= NULL
;
154 pModel
= reinterpret_cast<Model
*>(
155 xTunnel
->getSomething( Model::getUnoTunnelID() ) );
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") ),
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
209 void Model::setSchemaRef( const rtl::OUString
& rSchemaRef
)
211 msSchemaRef
= rSchemaRef
;
214 Model::XNameContainer_t
Model::getNamespaces() const
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;
269 aAllMIPs.push_back( pMIP );
270 for( MIPs_t::const_iterator aIter = maMIPs.begin();
271 aIter != maMIPs.end();
274 if( pMIP == aIter->second )
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();
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!" );
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
);
329 MIP
Model::queryMIP( const XNode_t
& xNode
) const
331 // OSL_ENSURE( xNode.is(), "no node" );
333 // travel up inheritance chain and inherit MIPs
335 for( XNode_t xCurrent
= xNode
;
337 xCurrent
= xCurrent
->getParentNode() )
339 // iterate over all MIPs for this node, and join MIPs
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
)
350 aRet
.inherit( aMIP
);
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?" );
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" );
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
412 xChild
= Reference
<XNode
>(
413 xNode
->getOwnerDocument()->createTextNode( OUString() ),
415 xNode
->appendChild( 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 );
441 OSL_ENSURE( false, "bound to unknown node type?" );
450 void Model::loadInstance( sal_Int32 nInstance
)
452 Sequence
<PropertyValue
> aSequence
= mpInstances
->getItem( nInstance
);
454 // find URL from instance
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
>(
467 OUSTRING("com.sun.star.ucb.SimpleFileAccess") ),
468 UNO_QUERY_THROW
)->openFileRead( sURL
);
471 Reference
<XDocument
> xInstance
=
472 getDocumentBuilder()->parse( xInput
);
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
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();
520 // implement xforms::XModel
523 rtl::OUString
Model::getID()
524 throw( RuntimeException
)
530 void Model::setID( const rtl::OUString
& sID
)
531 throw( RuntimeException
)
537 void Model::initialize()
538 throw( RuntimeException
)
540 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
545 // let's pretend we're initialized and rebind all bindings
546 mbInitialized
= true;
550 void Model::rebuild()
551 throw( RuntimeException
)
553 if( ! mbInitialized
)
559 void Model::recalculate()
560 throw( RuntimeException
)
565 void Model::revalidate()
566 throw( RuntimeException
)
568 // do nothing. We don't validate anyways!
571 void Model::refresh()
572 throw( RuntimeException
)
578 void SAL_CALL
Model::submitWithInteraction(
579 const rtl::OUString
& sID
,
580 const XInteractionHandler_t
& _rxHandler
)
581 throw( VetoException
,
582 WrappedTargetException
,
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 ),
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
;
616 // instance management
619 Model::XSet_t
Model::getInstances()
620 throw( RuntimeException
)
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
);
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
);
650 // bindings management
653 Model::XPropertySet_t SAL_CALL
Model::createBinding()
654 throw( RuntimeException
)
657 return new Binding();
660 Model::XPropertySet_t
Model::cloneBinding( const XPropertySet_t
& xBinding
)
661 throw( RuntimeException
)
664 XPropertySet_t xNewBinding
= createBinding();
665 copy( xBinding
, xNewBinding
);
669 Model::XPropertySet_t
Model::getBinding( const rtl::OUString
& sId
)
670 throw( RuntimeException
)
673 return mpBindings
->hasItem( sId
) ? mpBindings
->getItem( sId
) : NULL
;
676 Model::XSet_t
Model::getBindings()
677 throw( RuntimeException
)
686 // submission management
689 Model::XSubmission_t
Model::createSubmission()
690 throw( RuntimeException
)
693 return new Submission();
696 Model::XSubmission_t
Model::cloneSubmission(const XPropertySet_t
& xSubmission
)
697 throw( RuntimeException
)
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
)
710 XSubmission_t xSubmission
;
711 if ( mpSubmissions
->hasItem( sId
) )
712 xSubmission
= xSubmission
.query( mpSubmissions
->getItem( sId
) );
716 Model::XSet_t
Model::getSubmissions()
717 throw( RuntimeException
)
720 return mxSubmissions
;
726 // implementation of XFormsUIHelper1 interface
727 // can be found in file model_ui.cxx
733 // implement XPropertySet & friends
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
);
766 throw( RuntimeException
)
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
) );
796 bool operator >>= ( xforms::Model
* pModel
,
797 com::sun::star::uno::Any
& rAny
)
801 // acquire model pointer through XUnoTunnel
802 Reference
<XUnoTunnel
> xTunnel( rAny
, UNO_QUERY
);
805 pModel
= reinterpret_cast<xforms::Model
*>(
806 xTunnel
->getSomething( xforms::Model::getUnoTunnelID() ) );