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/processfactory.hxx>
38 #include <cppuhelper/supportsservice.hxx>
39 #include <cppuhelper/typeprovider.hxx>
44 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
45 #include <com/sun/star/xml/dom/XDocument.hpp>
46 #include <com/sun/star/xml/dom/XCharacterData.hpp>
47 #include <com/sun/star/xml/dom/NodeType.hpp>
48 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
49 #include <com/sun/star/uno/Sequence.hxx>
50 #include <com/sun/star/beans/PropertyValue.hpp>
51 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
52 #include <com/sun/star/io/XInputStream.hpp>
55 using com::sun::star::lang::XUnoTunnel
;
56 using com::sun::star::beans::XPropertySet
;
57 using com::sun::star::beans::PropertyValue
;
58 using com::sun::star::ucb::SimpleFileAccess
;
59 using com::sun::star::io::XInputStream
;
61 using namespace com::sun::star::uno
;
62 using namespace com::sun::star::xml::dom
;
63 using namespace xforms
;
66 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
67 #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);
69 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
71 #define DBG_INVARIANT()
78 void Model::ensureAtLeastOneInstance()
80 if( ! mxInstances
->hasItems() )
82 // create a default instance
83 newInstance( OUString(), OUString(), true );
88 /** Model default constructor; create empty model */
91 mxInstances( new InstanceCollection
),
92 mxNamespaces( new NameContainer
<OUString
>() ),
93 mbInitialized( false ),
94 mbExternalData( true )
96 initializePropertySet();
98 // initialize bindings collections
99 // (not in initializer list to avoid use of incomplete 'this')
100 mxBindings
= new BindingCollection( this );
101 mxSubmissions
= new SubmissionCollection( this );
103 // invariant only holds after construction
107 Model::~Model() throw()
111 EvaluationContext
Model::getEvaluationContext()
113 // the default context is the top-level element node. A default
114 // node (instanceData' is inserted when there is no default node
115 Reference
<XDocument
> xInstance
= getDefaultInstance();
116 Reference
<XNode
> xElement
= xInstance
->getDocumentElement();
118 // no element found? Then insert default element 'instanceData'
119 if( ! xElement
.is() )
121 xElement
.set( xInstance
->createElement( "instanceData" ), UNO_QUERY_THROW
);
122 xInstance
->appendChild( xElement
);
125 OSL_ENSURE( xElement
.is() &&
126 xElement
->getNodeType() == NodeType_ELEMENT_NODE
,
127 "no element in evaluation context" );
129 return EvaluationContext( xElement
, this, mxNamespaces
);
133 css::uno::Sequence
<sal_Int8
> Model::getUnoTunnelId()
135 static cppu::OImplementationId aImplementationId
;
136 return aImplementationId
.getImplementationId();
140 void Model::setForeignSchema( const css::uno::Reference
<css::xml::dom::XDocument
>& rDocument
)
142 mxForeignSchema
= rDocument
;
146 void Model::setSchemaRef( const OUString
& rSchemaRef
)
148 msSchemaRef
= rSchemaRef
;
152 void Model::setNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
154 if( rNamespaces
.is() )
155 mxNamespaces
= rNamespaces
;
159 void Model::setExternalData( bool _bData
)
161 mbExternalData
= _bData
;
164 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
165 void Model::dbg_assertInvariant() const
167 assert(mxInstances
&& "no instances found");
168 assert(mxBindings
&& "no bindings element");
169 assert(mxSubmissions
&& "no submissions element");
175 void Model::addMIP( void* pTag
, const XNode_t
& xNode
, const MIP
& rMIP
)
177 OSL_ENSURE( pTag
!= nullptr, "empty tag?" );
178 OSL_ENSURE( xNode
.is(), "no node" );
180 MIPs_t::value_type
aValue( xNode
, ::std::pair
<void*,MIP
>( pTag
, rMIP
) );
181 maMIPs
.insert( aValue
);
184 void Model::removeMIPs( void const * pTag
)
186 OSL_ENSURE( pTag
!= nullptr, "empty tag?" );
188 for( MIPs_t::iterator aIter
= maMIPs
.begin();
189 aIter
!= maMIPs
.end(); )
191 if( aIter
->second
.first
== pTag
)
193 aIter
= maMIPs
.erase( aIter
);
200 MIP
Model::queryMIP( const XNode_t
& xNode
) const
202 // travel up inheritance chain and inherit MIPs
204 for( XNode_t xCurrent
= xNode
;
206 xCurrent
= xCurrent
->getParentNode() )
208 // iterate over all MIPs for this node, and join MIPs
210 MIPs_t::const_iterator aEnd
= maMIPs
.upper_bound( xCurrent
);
211 MIPs_t::const_iterator aIter
= maMIPs
.lower_bound( xCurrent
);
212 for( ; aIter
!= aEnd
; ++aIter
)
213 aMIP
.join( aIter
->second
.second
);
215 // inherit from current node (or set if we are at the start node)
216 if( xCurrent
== xNode
)
219 aRet
.inherit( aMIP
);
228 OSL_ENSURE( mxBindings
, "bindings?" );
230 // iterate over all bindings and call update
231 sal_Int32 nCount
= mxBindings
->countItems();
232 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
234 Binding
* pBind
= comphelper::getUnoTunnelImplementation
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
235 OSL_ENSURE( pBind
!= nullptr, "binding?" );
241 void Model::deferNotifications( bool bDefer
)
243 // iterate over all bindings and defer notifications
244 sal_Int32 nCount
= mxBindings
->countItems();
245 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
247 Binding
* pBind
= comphelper::getUnoTunnelImplementation
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
248 OSL_ENSURE( pBind
!= nullptr, "binding?" );
249 pBind
->deferNotifications( bDefer
);
254 bool Model::setSimpleContent( const XNode_t
& xConstNode
,
255 const OUString
& sValue
)
257 OSL_ENSURE( xConstNode
.is(), "need node to set data" );
260 if( xConstNode
.is() )
262 // non-const node reference so we can assign children (if necessary)
263 XNode_t
xNode( xConstNode
);
265 switch( xNode
->getNodeType() )
267 case NodeType_ELEMENT_NODE
:
269 // find first text node child
270 Reference
<XNode
> xChild
;
271 for( xChild
= xNode
->getFirstChild();
272 xChild
.is() && xChild
->getNodeType() != NodeType_TEXT_NODE
;
273 xChild
= xChild
->getNextSibling() )
274 ; // empty loop; only find first text node child
276 // create text node, if none is found
280 xNode
->getOwnerDocument()->createTextNode( OUString() ),
282 xNode
->appendChild( xChild
);
286 OSL_ENSURE( xNode
.is() &&
287 xNode
->getNodeType() == NodeType_TEXT_NODE
,
288 "text node creation failed?" );
289 [[fallthrough
]]; // continue as with text node:
292 case NodeType_TEXT_NODE
:
293 case NodeType_ATTRIBUTE_NODE
:
295 // set the node value (defer notifications)
296 if( xNode
->getNodeValue() != sValue
)
298 deferNotifications( true );
299 xNode
->setNodeValue( sValue
);
300 deferNotifications( false );
308 OSL_FAIL( "bound to unknown node type?" );
317 void Model::loadInstance( sal_Int32 nInstance
)
319 Sequence
<PropertyValue
> aSequence
= mxInstances
->getItem( nInstance
);
321 // find URL from instance
324 getInstanceData( aSequence
, nullptr, nullptr, &sURL
, &bOnce
);
326 // if we have a URL, load the document and set it into the instance
332 Reference
<XInputStream
> xInput
=
333 SimpleFileAccess::create( ::comphelper::getProcessComponentContext() )->openFileRead( sURL
);
336 Reference
<XDocument
> xInstance
=
337 getDocumentBuilder()->parse( xInput
);
341 setInstanceData( aSequence
, nullptr, &xInstance
,
342 bOnce
? &sEmpty
: &sURL
, nullptr);
343 mxInstances
->setItem( nInstance
, aSequence
);
347 catch( const Exception
& )
349 // couldn't load the instance -> ignore!
353 void Model::loadInstances()
355 // iterate over instance array to get PropertyValue-Sequence
356 const sal_Int32 nInstances
= mxInstances
->countItems();
357 for( sal_Int32 nInstance
= 0; nInstance
< nInstances
; nInstance
++ )
359 loadInstance( nInstance
);
364 bool Model::isValid() const
367 sal_Int32 nCount
= mxBindings
->countItems();
368 for( sal_Int32 i
= 0; bValid
&& i
< nCount
; i
++ )
370 Binding
* pBind
= comphelper::getUnoTunnelImplementation
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
371 OSL_ENSURE( pBind
!= nullptr, "binding?" );
372 bValid
= pBind
->isValid();
378 // implement xforms::XModel
381 OUString
Model::getID()
387 void Model::setID( const OUString
& sID
)
393 void Model::initialize()
395 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
400 // let's pretend we're initialized and rebind all bindings
401 mbInitialized
= true;
405 void Model::rebuild()
407 if( ! mbInitialized
)
413 void Model::recalculate()
418 void Model::revalidate()
420 // do nothing. We don't validate anyways!
423 void Model::refresh()
429 void SAL_CALL
Model::submitWithInteraction(
431 const css::uno::Reference
<css::task::XInteractionHandler
>& _rxHandler
)
435 if( mxSubmissions
->hasItem( sID
) )
437 Submission
* pSubmission
=
438 comphelper::getUnoTunnelImplementation
<Submission
>( mxSubmissions
->getItem( sID
) );
439 OSL_ENSURE( pSubmission
!= nullptr, "no submission?" );
440 OSL_ENSURE( pSubmission
->getModel() == Reference
<XModel
>( this ),
443 // submit. All exceptions are allowed to leave.
444 pSubmission
->submitWithInteraction( _rxHandler
);
448 void Model::submit( const OUString
& sID
)
450 submitWithInteraction( sID
, nullptr );
453 css::uno::Reference
<css::xforms::XDataTypeRepository
> SAL_CALL
Model::getDataTypeRepository( )
455 if ( !mxDataTypes
.is() )
456 mxDataTypes
= new ODataTypeRepository
;
462 // instance management
465 css::uno::Reference
<css::container::XSet
> Model::getInstances()
467 return mxInstances
.get();
470 css::uno::Reference
<css::xml::dom::XDocument
> Model::getInstanceDocument( const OUString
& rName
)
472 ensureAtLeastOneInstance();
473 Reference
<XDocument
> aInstance
;
474 sal_Int32 nInstance
= lcl_findInstance( mxInstances
.get(), rName
);
475 if( nInstance
!= -1 )
476 getInstanceData( mxInstances
->getItem( nInstance
),
477 nullptr, &aInstance
, nullptr, nullptr );
481 css::uno::Reference
<css::xml::dom::XDocument
> SAL_CALL
Model::getDefaultInstance()
483 ensureAtLeastOneInstance();
484 DBG_ASSERT( mxInstances
->countItems() > 0, "no instance?" );
485 Reference
<XDocument
> aInstance
;
486 getInstanceData( mxInstances
->getItem( 0 ), nullptr, &aInstance
, nullptr, nullptr );
491 // bindings management
494 css::uno::Reference
<css::beans::XPropertySet
> SAL_CALL
Model::createBinding()
497 return new Binding();
500 css::uno::Reference
<css::beans::XPropertySet
> Model::cloneBinding( const css::uno::Reference
<css::beans::XPropertySet
>& xBinding
)
503 XPropertySet_t xNewBinding
= createBinding();
504 copy( xBinding
, xNewBinding
);
508 css::uno::Reference
<css::beans::XPropertySet
> Model::getBinding( const OUString
& sId
)
511 return mxBindings
->hasItem( sId
) ? mxBindings
->getItem( sId
) : nullptr;
514 css::uno::Reference
<css::container::XSet
> Model::getBindings()
517 return mxBindings
.get();
521 // submission management
524 css::uno::Reference
<css::xforms::XSubmission
> Model::createSubmission()
527 return new Submission();
530 css::uno::Reference
<css::xforms::XSubmission
> Model::cloneSubmission(const css::uno::Reference
<css::beans::XPropertySet
>& xSubmission
)
533 css::uno::Reference
<css::xforms::XSubmission
> xNewSubmission
= createSubmission();
534 XPropertySet_t
xAsPropertySet( xNewSubmission
.get() );
535 copy( xSubmission
.get(), xAsPropertySet
);
536 return xNewSubmission
;
539 css::uno::Reference
<css::xforms::XSubmission
> Model::getSubmission( const OUString
& sId
)
542 css::uno::Reference
<css::xforms::XSubmission
> xSubmission
;
543 if ( mxSubmissions
->hasItem( sId
) )
544 xSubmission
.set(mxSubmissions
->getItem( sId
), css::uno::UNO_QUERY
);
548 css::uno::Reference
<css::container::XSet
> Model::getSubmissions()
551 return mxSubmissions
.get();
555 // implement XPropertySet & friends
559 #define HANDLE_ForeignSchema 3
560 #define HANDLE_SchemaRef 4
561 #define HANDLE_Namespaces 5
562 #define HANDLE_ExternalData 6
564 #define REGISTER_PROPERTY( property, type ) \
565 registerProperty( PROPERTY( property, type ), \
566 new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
568 #define REGISTER_PROPERTY_API( property, type ) \
569 registerProperty( PROPERTY( property, type ), \
570 new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
572 #define REGISTER_BOOL_PROPERTY( property ) \
573 registerProperty( PROPERTY( property, sal_Bool ), \
574 new BooleanPropertyAccessor< Model >( this, &Model::set##property, &Model::get##property ) );
576 void Model::initializePropertySet()
578 REGISTER_PROPERTY_API ( ID
, OUString
);
579 REGISTER_PROPERTY ( ForeignSchema
, css::uno::Reference
<css::xml::dom::XDocument
> );
580 REGISTER_PROPERTY ( SchemaRef
, OUString
);
581 REGISTER_PROPERTY ( Namespaces
, css::uno::Reference
<css::container::XNameContainer
> );
582 REGISTER_BOOL_PROPERTY( ExternalData
);
591 sal_Int64
Model::getSomething( const css::uno::Sequence
<sal_Int8
>& xId
)
593 return reinterpret_cast<sal_Int64
>( ( xId
== getUnoTunnelId() ) ? this : nullptr );
596 Sequence
<sal_Int8
> Model::getImplementationId()
598 return css::uno::Sequence
<sal_Int8
>();
601 OUString
Model::getImplementationName()
603 return "com.sun.star.form.Model";
606 sal_Bool
Model::supportsService(OUString
const & ServiceName
)
608 return cppu::supportsService(this, ServiceName
);
611 css::uno::Sequence
<OUString
> Model::getSupportedServiceNames()
613 return css::uno::Sequence
<OUString
>{"com.sun.star.xforms.Model"};
616 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
617 com_sun_star_form_Model_get_implementation(css::uno::XComponentContext
*,
618 css::uno::Sequence
<css::uno::Any
> const &)
620 return cppu::acquire(new xforms::Model());
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */