1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include "model_helper.hxx"
24 #include "unohelper.hxx"
25 #include "binding.hxx"
26 #include "submission.hxx"
28 #include "evaluationcontext.hxx"
29 #include "xmlhelper.hxx"
30 #include "datatyperepository.hxx"
31 #include "NameContainer.hxx"
33 #include <rtl/ustring.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <tools/debug.hxx>
37 #include <comphelper/propertysetinfo.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <cppuhelper/typeprovider.hxx>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
46 #include <com/sun/star/lang/IllegalArgumentException.hpp>
47 #include <com/sun/star/xml/dom/XDocument.hpp>
48 #include <com/sun/star/xml/dom/XCharacterData.hpp>
49 #include <com/sun/star/xml/dom/NodeType.hpp>
50 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
51 #include <com/sun/star/uno/Sequence.hxx>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
54 #include <com/sun/star/io/XInputStream.hpp>
57 using com::sun::star::lang::XMultiServiceFactory
;
58 using com::sun::star::lang::XUnoTunnel
;
59 using com::sun::star::beans::XPropertySet
;
60 using com::sun::star::beans::PropertyValue
;
61 using com::sun::star::beans::PropertyVetoException
;
62 using com::sun::star::beans::UnknownPropertyException
;
63 using com::sun::star::util::VetoException
;
64 using com::sun::star::lang::WrappedTargetException
;
65 using com::sun::star::lang::IllegalArgumentException
;
66 using com::sun::star::ucb::XSimpleFileAccess3
;
67 using com::sun::star::ucb::SimpleFileAccess
;
68 using com::sun::star::io::XInputStream
;
70 using namespace com::sun::star::uno
;
71 using namespace com::sun::star::xml::dom
;
72 using namespace xforms
;
75 #if OSL_DEBUG_LEVEL > 1
76 #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);
78 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
80 #define DBG_INVARIANT()
89 void Model::ensureAtLeastOneInstance()
91 if( ! mpInstances
->hasItems() )
93 // create a default instance
94 newInstance( OUString(), OUString(), true );
100 /** Model default constructor; create empty model */
104 mpSubmissions( NULL
),
105 mpInstances( new InstanceCollection
),
106 mxNamespaces( new NameContainer
<OUString
>() ),
107 mxBindings( mpBindings
),
108 mxSubmissions( mpSubmissions
),
109 mxInstances( mpInstances
),
110 mbInitialized( false ),
111 mbExternalData( true )
113 initializePropertySet();
115 // initialize bindings collections
116 // (not in initializer list to avoid use of incomplete 'this')
117 mpBindings
= new BindingCollection( this );
118 mxBindings
= mpBindings
;
120 mpSubmissions
= new SubmissionCollection( this );
121 mxSubmissions
= mpSubmissions
;
123 // invariant only holds after construction
127 Model::~Model() throw()
129 // give up bindings & submissions; the mxBindings/mxSubmissions
130 // references will then delete them
132 mpSubmissions
= NULL
;
135 static Model
* lcl_getModel( const Reference
<XUnoTunnel
>& xTunnel
)
137 Model
* pModel
= NULL
;
139 pModel
= reinterpret_cast<Model
*>(
140 xTunnel
->getSomething( Model::getUnoTunnelID() ) );
144 Model
* Model::getModel( const Reference
<XModel
>& xModel
)
146 return lcl_getModel( Reference
<XUnoTunnel
>( xModel
, UNO_QUERY
) );
149 EvaluationContext
Model::getEvaluationContext()
151 // the default context is the top-level element node. A default
152 // node (instanceData' is inserted when there is no default node
153 Reference
<XDocument
> xInstance
= getDefaultInstance();
154 Reference
<XNode
> xElement( xInstance
->getDocumentElement(), UNO_QUERY
);
156 // no element found? Then insert default element 'instanceData'
157 if( ! xElement
.is() )
159 xElement
= Reference
<XNode
>(
160 xInstance
->createElement( "instanceData" ),
162 Reference
<XNode
>( xInstance
, UNO_QUERY_THROW
)->appendChild( xElement
);
165 OSL_ENSURE( xElement
.is() &&
166 xElement
->getNodeType() == NodeType_ELEMENT_NODE
,
167 "no element in evaluation context" );
169 return EvaluationContext( xElement
, this, mxNamespaces
, 0, 1 );
173 Model::IntSequence_t
Model::getUnoTunnelID()
175 static cppu::OImplementationId aImplementationId
;
176 return aImplementationId
.getImplementationId();
179 Model::XDocument_t
Model::getForeignSchema() const
181 return mxForeignSchema
;
184 void Model::setForeignSchema( const XDocument_t
& rDocument
)
186 mxForeignSchema
= rDocument
;
189 OUString
Model::getSchemaRef() const
194 void Model::setSchemaRef( const OUString
& rSchemaRef
)
196 msSchemaRef
= rSchemaRef
;
199 Model::XNameContainer_t
Model::getNamespaces() const
204 void Model::setNamespaces( const XNameContainer_t
& rNamespaces
)
206 if( rNamespaces
.is() )
207 mxNamespaces
= rNamespaces
;
210 bool Model::getExternalData() const
212 return mbExternalData
;
215 void Model::setExternalData( bool _bData
)
217 mbExternalData
= _bData
;
220 #if OSL_DEBUG_LEVEL > 1
221 void Model::dbg_assertInvariant() const
223 OSL_ENSURE( mpInstances
!= NULL
, "no instances found" );
224 OSL_ENSURE( mxInstances
.is(), "No instance container!" );
226 OSL_ENSURE( mpBindings
!= NULL
, "no bindings element" );
227 OSL_ENSURE( mxBindings
.is(), "No Bindings container" );
229 OSL_ENSURE( mpSubmissions
!= NULL
, "no submissions element" );
230 OSL_ENSURE( mxSubmissions
.is(), "No Submission container" );
239 void Model::addMIP( void* pTag
, const XNode_t
& xNode
, const MIP
& rMIP
)
241 OSL_ENSURE( pTag
!= NULL
, "empty tag?" );
242 OSL_ENSURE( xNode
.is(), "no node" );
244 MIPs_t::value_type
aValue( xNode
, ::std::pair
<void*,MIP
>( pTag
, rMIP
) );
245 maMIPs
.insert( aValue
);
248 void Model::removeMIPs( void* pTag
)
250 OSL_ENSURE( pTag
!= NULL
, "empty tag?" );
252 for( MIPs_t::iterator aIter
= maMIPs
.begin();
253 aIter
!= maMIPs
.end(); )
255 if( aIter
->second
.first
== pTag
)
257 MIPs_t::iterator
next( aIter
); ++next
;
258 maMIPs
.erase( aIter
);
266 MIP
Model::queryMIP( const XNode_t
& xNode
) const
268 // travel up inheritance chain and inherit MIPs
270 for( XNode_t xCurrent
= xNode
;
272 xCurrent
= xCurrent
->getParentNode() )
274 // iterate over all MIPs for this node, and join MIPs
276 MIPs_t::const_iterator aEnd
= maMIPs
.upper_bound( xCurrent
);
277 MIPs_t::const_iterator aIter
= maMIPs
.lower_bound( xCurrent
);
278 for( ; aIter
!= aEnd
; ++aIter
)
279 aMIP
.join( aIter
->second
.second
);
281 // inherit from current node (or set if we are at the start node)
282 if( xCurrent
== xNode
)
285 aRet
.inherit( aMIP
);
295 OSL_ENSURE( mpBindings
!= NULL
, "bindings?" );
297 // iterate over all bindings and call update
298 sal_Int32 nCount
= mpBindings
->countItems();
299 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
301 Binding
* pBind
= Binding::getBinding( mpBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
302 OSL_ENSURE( pBind
!= NULL
, "binding?" );
309 void Model::deferNotifications( bool bDefer
)
311 // iterate over all bindings and defer notifications
312 sal_Int32 nCount
= mpBindings
->countItems();
313 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
315 Binding
* pBind
= Binding::getBinding( mpBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
316 OSL_ENSURE( pBind
!= NULL
, "binding?" );
317 pBind
->deferNotifications( bDefer
);
322 bool Model::setSimpleContent( const XNode_t
& xConstNode
,
323 const OUString
& sValue
)
325 OSL_ENSURE( xConstNode
.is(), "need node to set data" );
328 if( xConstNode
.is() )
330 // non-const node reference so we can assign children (if necessary)
331 XNode_t
xNode( xConstNode
);
333 switch( xNode
->getNodeType() )
335 case NodeType_ELEMENT_NODE
:
337 // find first text node child
338 Reference
<XNode
> xChild
;
339 for( xChild
= xNode
->getFirstChild();
340 xChild
.is() && xChild
->getNodeType() != NodeType_TEXT_NODE
;
341 xChild
= xChild
->getNextSibling() )
342 ; // empty loop; only find first text node child
344 // create text node, if none is found
347 xChild
= Reference
<XNode
>(
348 xNode
->getOwnerDocument()->createTextNode( OUString() ),
350 xNode
->appendChild( xChild
);
354 OSL_ENSURE( xNode
.is() &&
355 xNode
->getNodeType() == NodeType_TEXT_NODE
,
356 "text node creation failed?" );
358 // no break; continue as with text node:
360 case NodeType_TEXT_NODE
:
361 case NodeType_ATTRIBUTE_NODE
:
363 // set the node value (defer notifications)
364 if( xNode
->getNodeValue() != sValue
)
366 deferNotifications( true );
367 xNode
->setNodeValue( sValue
);
368 deferNotifications( false );
376 OSL_FAIL( "bound to unknown node type?" );
385 void Model::loadInstance( sal_Int32 nInstance
)
387 Sequence
<PropertyValue
> aSequence
= mpInstances
->getItem( nInstance
);
389 // find URL from instance
392 getInstanceData( aSequence
, NULL
, NULL
, &sURL
, &bOnce
);
394 // if we have a URL, load the document and set it into the instance
395 if( !sURL
.isEmpty() )
399 Reference
<XInputStream
> xInput
=
400 Reference
<XSimpleFileAccess3
>( SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) )->openFileRead( sURL
);
403 Reference
<XDocument
> xInstance
=
404 getDocumentBuilder()->parse( xInput
);
408 setInstanceData( aSequence
, NULL
, &xInstance
,
409 bOnce
? &sEmpty
: &sURL
, NULL
);
410 mpInstances
->setItem( nInstance
, aSequence
);
414 catch( const Exception
& )
416 // couldn't load the instance -> ignore!
421 void Model::loadInstances()
423 // iterate over instance array to get PropertyValue-Sequence
424 const sal_Int32 nInstances
= mpInstances
->countItems();
425 for( sal_Int32 nInstance
= 0; nInstance
< nInstances
; nInstance
++ )
427 loadInstance( nInstance
);
431 bool Model::isInitialized() const
433 return mbInitialized
;
436 bool Model::isValid() const
439 sal_Int32 nCount
= mpBindings
->countItems();
440 for( sal_Int32 i
= 0; bValid
&& i
< nCount
; i
++ )
442 Binding
* pBind
= Binding::getBinding( mpBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
443 OSL_ENSURE( pBind
!= NULL
, "binding?" );
444 bValid
= pBind
->isValid();
452 // implement xforms::XModel
455 OUString
Model::getID()
456 throw( RuntimeException
)
462 void Model::setID( const OUString
& sID
)
463 throw( RuntimeException
)
469 void Model::initialize()
470 throw( RuntimeException
)
472 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
477 // let's pretend we're initialized and rebind all bindings
478 mbInitialized
= true;
482 void Model::rebuild()
483 throw( RuntimeException
)
485 if( ! mbInitialized
)
491 void Model::recalculate()
492 throw( RuntimeException
)
497 void Model::revalidate()
498 throw( RuntimeException
)
500 // do nothing. We don't validate anyways!
503 void Model::refresh()
504 throw( RuntimeException
)
510 void SAL_CALL
Model::submitWithInteraction(
512 const XInteractionHandler_t
& _rxHandler
)
513 throw( VetoException
,
514 WrappedTargetException
,
519 if( mpSubmissions
->hasItem( sID
) )
521 Submission
* pSubmission
=
522 Submission::getSubmission( mpSubmissions
->getItem( sID
) );
523 OSL_ENSURE( pSubmission
!= NULL
, "no submission?" );
524 OSL_ENSURE( pSubmission
->getModel() == Reference
<XModel
>( this ),
527 // submit. All exceptions are allowed to leave.
528 pSubmission
->submitWithInteraction( _rxHandler
);
532 void Model::submit( const OUString
& sID
)
533 throw( VetoException
, WrappedTargetException
, RuntimeException
)
535 submitWithInteraction( sID
, NULL
);
538 Model::XDataTypeRepository_t SAL_CALL
Model::getDataTypeRepository( )
539 throw( RuntimeException
)
541 if ( !mxDataTypes
.is() )
542 mxDataTypes
= new ODataTypeRepository
;
548 // instance management
551 Model::XSet_t
Model::getInstances()
552 throw( RuntimeException
)
557 Model::XDocument_t
Model::getInstanceDocument( const OUString
& rName
)
558 throw( RuntimeException
)
560 ensureAtLeastOneInstance();
561 Reference
<XDocument
> aInstance
;
562 sal_Int32 nInstance
= lcl_findInstance( mpInstances
, rName
);
563 if( nInstance
!= -1 )
564 getInstanceData( mpInstances
->getItem( nInstance
),
565 NULL
, &aInstance
, NULL
, NULL
);
569 Model::XDocument_t SAL_CALL
Model::getDefaultInstance()
570 throw( RuntimeException
)
572 ensureAtLeastOneInstance();
573 DBG_ASSERT( mpInstances
->countItems() > 0, "no instance?" );
574 Reference
<XDocument
> aInstance
;
575 getInstanceData( mpInstances
->getItem( 0 ), NULL
, &aInstance
, NULL
, NULL
);
582 // bindings management
585 Model::XPropertySet_t SAL_CALL
Model::createBinding()
586 throw( RuntimeException
)
589 return new Binding();
592 Model::XPropertySet_t
Model::cloneBinding( const XPropertySet_t
& xBinding
)
593 throw( RuntimeException
)
596 XPropertySet_t xNewBinding
= createBinding();
597 copy( xBinding
, xNewBinding
);
601 Model::XPropertySet_t
Model::getBinding( const OUString
& sId
)
602 throw( RuntimeException
)
605 return mpBindings
->hasItem( sId
) ? mpBindings
->getItem( sId
) : NULL
;
608 Model::XSet_t
Model::getBindings()
609 throw( RuntimeException
)
618 // submission management
621 Model::XSubmission_t
Model::createSubmission()
622 throw( RuntimeException
)
625 return new Submission();
628 Model::XSubmission_t
Model::cloneSubmission(const XPropertySet_t
& xSubmission
)
629 throw( RuntimeException
)
632 XSubmission_t xNewSubmission
= createSubmission();
633 XPropertySet_t
xAsPropertySet( xNewSubmission
.get() );
634 copy( xSubmission
.get(), xAsPropertySet
);
635 return xNewSubmission
;
638 Model::XSubmission_t
Model::getSubmission( const OUString
& sId
)
639 throw( RuntimeException
)
642 XSubmission_t xSubmission
;
643 if ( mpSubmissions
->hasItem( sId
) )
644 xSubmission
= xSubmission
.query( mpSubmissions
->getItem( sId
) );
648 Model::XSet_t
Model::getSubmissions()
649 throw( RuntimeException
)
652 return mxSubmissions
;
656 // implement XPropertySet & friends
660 #define HANDLE_ForeignSchema 3
661 #define HANDLE_SchemaRef 4
662 #define HANDLE_Namespaces 5
663 #define HANDLE_ExternalData 6
665 #define REGISTER_PROPERTY( property, type ) \
666 registerProperty( PROPERTY( property, type ), \
667 new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
669 #define REGISTER_PROPERTY_API( property, type ) \
670 registerProperty( PROPERTY( property, type ), \
671 new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
673 #define REGISTER_BOOL_PROPERTY( property ) \
674 registerProperty( PROPERTY( property, sal_Bool ), \
675 new BooleanPropertyAccessor< Model, bool >( this, &Model::set##property, &Model::get##property ) );
677 void Model::initializePropertySet()
679 REGISTER_PROPERTY_API ( ID
, OUString
);
680 REGISTER_PROPERTY ( ForeignSchema
, XDocument_t
);
681 REGISTER_PROPERTY ( SchemaRef
, OUString
);
682 REGISTER_PROPERTY ( Namespaces
, XNameContainer_t
);
683 REGISTER_BOOL_PROPERTY( ExternalData
);
687 throw( RuntimeException
)
693 sal_Int64
Model::getSomething( const IntSequence_t
& xId
)
694 throw( RuntimeException
)
696 return reinterpret_cast<sal_Int64
>( ( xId
== getUnoTunnelID() ) ? this : NULL
);
699 Sequence
<sal_Int8
> Model::getImplementationId()
700 throw( RuntimeException
)
702 return getUnoTunnelID();
705 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */