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 <comphelper/servicehelper.hxx>
39 #include <cppuhelper/supportsservice.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::beans::XPropertySet
;
56 using com::sun::star::beans::PropertyValue
;
57 using com::sun::star::ucb::SimpleFileAccess
;
58 using com::sun::star::io::XInputStream
;
60 using namespace com::sun::star::uno
;
61 using namespace com::sun::star::xml::dom
;
62 using namespace xforms
;
65 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
66 #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);
68 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
70 #define DBG_INVARIANT()
77 void Model::ensureAtLeastOneInstance()
79 if( ! mxInstances
->hasItems() )
81 // create a default instance
82 newInstance( OUString(), OUString(), true );
87 /** Model default constructor; create empty model */
89 mxInstances( new InstanceCollection
),
90 mxNamespaces( new NameContainer
<OUString
>() ),
91 mbInitialized( false ),
92 mbExternalData( true )
94 initializePropertySet();
96 // initialize bindings collections
97 // (not in initializer list to avoid use of incomplete 'this')
98 mxBindings
= new BindingCollection( this );
99 mxSubmissions
= new SubmissionCollection( this );
101 // invariant only holds after construction
105 Model::~Model() noexcept
109 EvaluationContext
Model::getEvaluationContext()
111 // the default context is the top-level element node. A default
112 // node (instanceData' is inserted when there is no default node
113 Reference
<XDocument
> xInstance
= getDefaultInstance();
114 Reference
<XNode
> xElement
= xInstance
->getDocumentElement();
116 // no element found? Then insert default element 'instanceData'
117 if( ! xElement
.is() )
119 xElement
.set( xInstance
->createElement( u
"instanceData"_ustr
), UNO_QUERY_THROW
);
120 xInstance
->appendChild( xElement
);
123 OSL_ENSURE( xElement
.is() &&
124 xElement
->getNodeType() == NodeType_ELEMENT_NODE
,
125 "no element in evaluation context" );
127 return EvaluationContext( xElement
, this, mxNamespaces
);
131 void Model::setForeignSchema( const css::uno::Reference
<css::xml::dom::XDocument
>& rDocument
)
133 mxForeignSchema
= rDocument
;
137 void Model::setSchemaRef( const OUString
& rSchemaRef
)
139 msSchemaRef
= rSchemaRef
;
143 void Model::setNamespaces( const css::uno::Reference
<css::container::XNameContainer
>& rNamespaces
)
145 if( rNamespaces
.is() )
146 mxNamespaces
= rNamespaces
;
150 void Model::setExternalData( bool _bData
)
152 mbExternalData
= _bData
;
155 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
156 void Model::dbg_assertInvariant() const
158 assert(mxInstances
&& "no instances found");
159 assert(mxBindings
&& "no bindings element");
160 assert(mxSubmissions
&& "no submissions element");
166 void Model::addMIP( void* pTag
, const XNode_t
& xNode
, const MIP
& rMIP
)
168 OSL_ENSURE( pTag
!= nullptr, "empty tag?" );
169 OSL_ENSURE( xNode
.is(), "no node" );
171 MIPs_t::value_type
aValue( xNode
, ::std::pair
<void*,MIP
>( pTag
, rMIP
) );
172 maMIPs
.insert( aValue
);
175 void Model::removeMIPs( void const * pTag
)
177 OSL_ENSURE( pTag
!= nullptr, "empty tag?" );
179 for( MIPs_t::iterator aIter
= maMIPs
.begin();
180 aIter
!= maMIPs
.end(); )
182 if( aIter
->second
.first
== pTag
)
184 aIter
= maMIPs
.erase( aIter
);
191 MIP
Model::queryMIP( const XNode_t
& xNode
) const
193 // travel up inheritance chain and inherit MIPs
195 for( XNode_t xCurrent
= xNode
;
197 xCurrent
= xCurrent
->getParentNode() )
199 // iterate over all MIPs for this node, and join MIPs
201 MIPs_t::const_iterator aEnd
= maMIPs
.upper_bound( xCurrent
);
202 MIPs_t::const_iterator aIter
= maMIPs
.lower_bound( xCurrent
);
203 for( ; aIter
!= aEnd
; ++aIter
)
204 aMIP
.join( aIter
->second
.second
);
206 // inherit from current node (or set if we are at the start node)
207 if( xCurrent
== xNode
)
208 aRet
= std::move(aMIP
);
210 aRet
.inherit( aMIP
);
219 OSL_ENSURE( mxBindings
, "bindings?" );
221 // iterate over all bindings and call update
222 sal_Int32 nCount
= mxBindings
->countItems();
223 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
225 Binding
* pBind
= comphelper::getFromUnoTunnel
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
226 OSL_ENSURE( pBind
!= nullptr, "binding?" );
232 void Model::deferNotifications( bool bDefer
)
234 // iterate over all bindings and defer notifications
235 sal_Int32 nCount
= mxBindings
->countItems();
236 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
238 Binding
* pBind
= comphelper::getFromUnoTunnel
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
239 OSL_ENSURE( pBind
!= nullptr, "binding?" );
240 pBind
->deferNotifications( bDefer
);
245 bool Model::setSimpleContent( const XNode_t
& xConstNode
,
246 const OUString
& sValue
)
248 OSL_ENSURE( xConstNode
.is(), "need node to set data" );
251 if( xConstNode
.is() )
253 // non-const node reference so we can assign children (if necessary)
254 XNode_t
xNode( xConstNode
);
256 switch( xNode
->getNodeType() )
258 case NodeType_ELEMENT_NODE
:
260 // find first text node child
261 Reference
<XNode
> xChild
;
262 for( xChild
= xNode
->getFirstChild();
263 xChild
.is() && xChild
->getNodeType() != NodeType_TEXT_NODE
;
264 xChild
= xChild
->getNextSibling() )
265 ; // empty loop; only find first text node child
267 // create text node, if none is found
271 xNode
->getOwnerDocument()->createTextNode( OUString() ),
273 xNode
->appendChild( xChild
);
275 xNode
= std::move(xChild
);
277 OSL_ENSURE( xNode
.is() &&
278 xNode
->getNodeType() == NodeType_TEXT_NODE
,
279 "text node creation failed?" );
280 [[fallthrough
]]; // continue as with text node:
283 case NodeType_TEXT_NODE
:
284 case NodeType_ATTRIBUTE_NODE
:
286 // set the node value (defer notifications)
287 if( xNode
->getNodeValue() != sValue
)
289 deferNotifications( true );
290 xNode
->setNodeValue( sValue
);
291 deferNotifications( false );
299 OSL_FAIL( "bound to unknown node type?" );
308 void Model::loadInstance( sal_Int32 nInstance
)
310 Sequence
<PropertyValue
> aSequence
= mxInstances
->getItem( nInstance
);
312 // find URL from instance
315 getInstanceData( aSequence
, nullptr, nullptr, &sURL
, &bOnce
);
317 // if we have a URL, load the document and set it into the instance
323 Reference
<XInputStream
> xInput
=
324 SimpleFileAccess::create( ::comphelper::getProcessComponentContext() )->openFileRead( sURL
);
327 Reference
<XDocument
> xInstance
=
328 getDocumentBuilder()->parse( xInput
);
332 setInstanceData( aSequence
, nullptr, &xInstance
,
333 bOnce
? &sEmpty
: &sURL
, nullptr);
334 mxInstances
->setItem( nInstance
, aSequence
);
338 catch( const Exception
& )
340 // couldn't load the instance -> ignore!
344 void Model::loadInstances()
346 // iterate over instance array to get PropertyValue-Sequence
347 const sal_Int32 nInstances
= mxInstances
->countItems();
348 for( sal_Int32 nInstance
= 0; nInstance
< nInstances
; nInstance
++ )
350 loadInstance( nInstance
);
355 bool Model::isValid() const
358 sal_Int32 nCount
= mxBindings
->countItems();
359 for( sal_Int32 i
= 0; bValid
&& i
< nCount
; i
++ )
361 Binding
* pBind
= comphelper::getFromUnoTunnel
<Binding
>( mxBindings
->Collection
<XPropertySet_t
>::getItem( i
) );
362 assert(pBind
!= nullptr && "binding?");
363 bValid
= pBind
->isValid();
369 // implement xforms::XModel
372 OUString
Model::getID()
378 void Model::setID( const OUString
& sID
)
384 void Model::initialize()
386 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
391 // let's pretend we're initialized and rebind all bindings
392 mbInitialized
= true;
396 void Model::rebuild()
398 if( ! mbInitialized
)
404 void Model::recalculate()
409 void Model::revalidate()
411 // do nothing. We don't validate anyways!
414 void Model::refresh()
420 void SAL_CALL
Model::submitWithInteraction(
422 const css::uno::Reference
<css::task::XInteractionHandler
>& _rxHandler
)
426 if( mxSubmissions
->hasItem( sID
) )
428 Submission
* pSubmission
=
429 dynamic_cast<Submission
*>( mxSubmissions
->getItem( sID
).get() );
430 assert(pSubmission
&& "no submission?");
431 OSL_ENSURE( pSubmission
->getModelImpl() == this,
434 // submit. All exceptions are allowed to leave.
435 pSubmission
->submitWithInteraction( _rxHandler
);
439 void Model::submit( const OUString
& sID
)
441 submitWithInteraction( sID
, nullptr );
444 css::uno::Reference
<css::xforms::XDataTypeRepository
> SAL_CALL
Model::getDataTypeRepository( )
446 if ( !mxDataTypes
.is() )
447 mxDataTypes
= new ODataTypeRepository
;
453 // instance management
456 css::uno::Reference
<css::container::XSet
> Model::getInstances()
461 css::uno::Reference
<css::xml::dom::XDocument
> Model::getInstanceDocument( const OUString
& rName
)
463 ensureAtLeastOneInstance();
464 Reference
<XDocument
> aInstance
;
465 sal_Int32 nInstance
= lcl_findInstance( mxInstances
.get(), rName
);
466 if( nInstance
!= -1 )
467 getInstanceData( mxInstances
->getItem( nInstance
),
468 nullptr, &aInstance
, nullptr, nullptr );
472 css::uno::Reference
<css::xml::dom::XDocument
> SAL_CALL
Model::getDefaultInstance()
474 ensureAtLeastOneInstance();
475 DBG_ASSERT( mxInstances
->countItems() > 0, "no instance?" );
476 Reference
<XDocument
> aInstance
;
477 getInstanceData( mxInstances
->getItem( 0 ), nullptr, &aInstance
, nullptr, nullptr );
482 // bindings management
485 css::uno::Reference
<css::beans::XPropertySet
> SAL_CALL
Model::createBinding()
488 return new Binding();
491 css::uno::Reference
<css::beans::XPropertySet
> Model::cloneBinding( const css::uno::Reference
<css::beans::XPropertySet
>& xBinding
)
494 XPropertySet_t xNewBinding
= createBinding();
495 copy( xBinding
, xNewBinding
);
499 css::uno::Reference
<css::beans::XPropertySet
> Model::getBinding( const OUString
& sId
)
502 return mxBindings
->hasItem( sId
) ? mxBindings
->getItem( sId
) : nullptr;
505 css::uno::Reference
<css::container::XSet
> Model::getBindings()
512 // submission management
515 css::uno::Reference
<css::xforms::XSubmission
> Model::createSubmission()
518 return new Submission();
521 css::uno::Reference
<css::xforms::XSubmission
> Model::cloneSubmission(const css::uno::Reference
<css::beans::XPropertySet
>& xSubmission
)
524 css::uno::Reference
<css::xforms::XSubmission
> xNewSubmission
= createSubmission();
525 XPropertySet_t
xAsPropertySet( xNewSubmission
);
526 copy( xSubmission
, xAsPropertySet
);
527 return xNewSubmission
;
530 css::uno::Reference
<css::xforms::XSubmission
> Model::getSubmission( const OUString
& sId
)
533 css::uno::Reference
<css::xforms::XSubmission
> xSubmission
;
534 if ( mxSubmissions
->hasItem( sId
) )
535 xSubmission
.set(mxSubmissions
->getItem( sId
), css::uno::UNO_QUERY
);
539 css::uno::Reference
<css::container::XSet
> Model::getSubmissions()
542 return mxSubmissions
;
546 // implement XPropertySet & friends
550 #define HANDLE_ForeignSchema 3
551 #define HANDLE_SchemaRef 4
552 #define HANDLE_Namespaces 5
553 #define HANDLE_ExternalData 6
555 void Model::initializePropertySet()
557 registerProperty( css::beans::Property(u
"ID"_ustr
, HANDLE_ID
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
558 new APIPropertyAccessor
< Model
, OUString
>(this, &Model::setID
, &Model::getID
) );
559 registerProperty( css::beans::Property(u
"ForeignSchema"_ustr
, HANDLE_ForeignSchema
, cppu::UnoType
<css::uno::Reference
<css::xml::dom::XDocument
>>::get(), css::beans::PropertyAttribute::BOUND
),
560 new DirectPropertyAccessor
< Model
, css::uno::Reference
<css::xml::dom::XDocument
> >( this, &Model::setForeignSchema
, &Model::getForeignSchema
) );
562 registerProperty( css::beans::Property(u
"SchemaRef"_ustr
, HANDLE_SchemaRef
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
563 new DirectPropertyAccessor
< Model
, OUString
>( this, &Model::setSchemaRef
, &Model::getSchemaRef
) );
565 registerProperty( css::beans::Property(u
"Namespaces"_ustr
, HANDLE_Namespaces
, cppu::UnoType
<css::uno::Reference
<css::container::XNameContainer
>>::get(), css::beans::PropertyAttribute::BOUND
),
566 new DirectPropertyAccessor
< Model
, css::uno::Reference
<css::container::XNameContainer
> >( this, &Model::setNamespaces
, &Model::getNamespaces
) );
568 registerProperty( css::beans::Property(u
"ExternalData"_ustr
, HANDLE_ExternalData
, cppu::UnoType
<sal_Bool
>::get(), css::beans::PropertyAttribute::BOUND
),
569 new BooleanPropertyAccessor
< Model
>( this, &Model::setExternalData
, &Model::getExternalData
) );
578 Sequence
<sal_Int8
> Model::getImplementationId()
580 return css::uno::Sequence
<sal_Int8
>();
583 OUString
Model::getImplementationName()
585 return u
"com.sun.star.form.Model"_ustr
;
588 sal_Bool
Model::supportsService(OUString
const & ServiceName
)
590 return cppu::supportsService(this, ServiceName
);
593 css::uno::Sequence
<OUString
> Model::getSupportedServiceNames()
595 return {u
"com.sun.star.xforms.Model"_ustr
};
598 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
599 com_sun_star_form_Model_get_implementation(css::uno::XComponentContext
*,
600 css::uno::Sequence
<css::uno::Any
> const &)
602 return cppu::acquire(new xforms::Model());
606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */