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"
33 #include "model_helper.hxx"
34 #include "unohelper.hxx"
35 #include "binding.hxx"
36 #include "submission.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>
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
;
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)
91 #define DBG_INVARIANT_TYPE(TYPE)
92 #define DBG_INVARIANT()
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 */
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
139 Model::~Model() throw()
141 // give up bindings & submissions; the mxBindings/mxSubmissions
142 // references will then delete them
144 mpSubmissions
= NULL
;
147 Model
* lcl_getModel( const Reference
<XUnoTunnel
>& xTunnel
)
149 Model
* pModel
= NULL
;
151 pModel
= reinterpret_cast<Model
*>(
152 xTunnel
->getSomething( Model::getUnoTunnelID() ) );
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") ),
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
206 void Model::setSchemaRef( const rtl::OUString
& rSchemaRef
)
208 msSchemaRef
= rSchemaRef
;
211 Model::XNameContainer_t
Model::getNamespaces() const
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;
266 aAllMIPs.push_back( pMIP );
267 for( MIPs_t::const_iterator aIter = maMIPs.begin();
268 aIter != maMIPs.end();
271 if( pMIP == aIter->second )
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();
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!" );
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
);
326 MIP
Model::queryMIP( const XNode_t
& xNode
) const
328 // OSL_ENSURE( xNode.is(), "no node" );
330 // travel up inheritance chain and inherit MIPs
332 for( XNode_t xCurrent
= xNode
;
334 xCurrent
= xCurrent
->getParentNode() )
336 // iterate over all MIPs for this node, and join MIPs
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
)
347 aRet
.inherit( aMIP
);
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?" );
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" );
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
409 xChild
= Reference
<XNode
>(
410 xNode
->getOwnerDocument()->createTextNode( OUString() ),
412 xNode
->appendChild( 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 );
438 OSL_ENSURE( false, "bound to unknown node type?" );
447 void Model::loadInstance( sal_Int32 nInstance
)
449 Sequence
<PropertyValue
> aSequence
= mpInstances
->getItem( nInstance
);
451 // find URL from instance
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
>(
464 OUSTRING("com.sun.star.ucb.SimpleFileAccess") ),
465 UNO_QUERY_THROW
)->openFileRead( sURL
);
468 Reference
<XDocument
> xInstance
=
469 getDocumentBuilder()->parse( xInput
);
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
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();
517 // implement xforms::XModel
520 rtl::OUString
Model::getID()
521 throw( RuntimeException
)
527 void Model::setID( const rtl::OUString
& sID
)
528 throw( RuntimeException
)
534 void Model::initialize()
535 throw( RuntimeException
)
537 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
542 // let's pretend we're initialized and rebind all bindings
543 mbInitialized
= true;
547 void Model::rebuild()
548 throw( RuntimeException
)
550 if( ! mbInitialized
)
556 void Model::recalculate()
557 throw( RuntimeException
)
562 void Model::revalidate()
563 throw( RuntimeException
)
565 // do nothing. We don't validate anyways!
568 void Model::refresh()
569 throw( RuntimeException
)
575 void SAL_CALL
Model::submitWithInteraction(
576 const rtl::OUString
& sID
,
577 const XInteractionHandler_t
& _rxHandler
)
578 throw( VetoException
,
579 WrappedTargetException
,
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 ),
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
;
613 // instance management
616 Model::XSet_t
Model::getInstances()
617 throw( RuntimeException
)
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
);
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
);
647 // bindings management
650 Model::XPropertySet_t SAL_CALL
Model::createBinding()
651 throw( RuntimeException
)
654 return new Binding();
657 Model::XPropertySet_t
Model::cloneBinding( const XPropertySet_t
& xBinding
)
658 throw( RuntimeException
)
661 XPropertySet_t xNewBinding
= createBinding();
662 copy( xBinding
, xNewBinding
);
666 Model::XPropertySet_t
Model::getBinding( const rtl::OUString
& sId
)
667 throw( RuntimeException
)
670 return mpBindings
->hasItem( sId
) ? mpBindings
->getItem( sId
) : NULL
;
673 Model::XSet_t
Model::getBindings()
674 throw( RuntimeException
)
683 // submission management
686 Model::XSubmission_t
Model::createSubmission()
687 throw( RuntimeException
)
690 return new Submission();
693 Model::XSubmission_t
Model::cloneSubmission(const XPropertySet_t
& xSubmission
)
694 throw( RuntimeException
)
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
)
707 XSubmission_t xSubmission
;
708 if ( mpSubmissions
->hasItem( sId
) )
709 xSubmission
= xSubmission
.query( mpSubmissions
->getItem( sId
) );
713 Model::XSet_t
Model::getSubmissions()
714 throw( RuntimeException
)
717 return mxSubmissions
;
723 // implementation of XFormsUIHelper1 interface
724 // can be found in file model_ui.cxx
730 // implement XPropertySet & friends
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
);
763 throw( RuntimeException
)
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
) );
793 bool operator >>= ( xforms::Model
* pModel
,
794 com::sun::star::uno::Any
& rAny
)
798 // acquire model pointer through XUnoTunnel
799 Reference
<XUnoTunnel
> xTunnel( rAny
, UNO_QUERY
);
802 pModel
= reinterpret_cast<xforms::Model
*>(
803 xTunnel
->getSomething( xforms::Model::getUnoTunnelID() ) );