nss: upgrade to release 3.73
[LibreOffice.git] / forms / source / xforms / model.cxx
blob5075222e14597d147a3be13ab6c40befe3ae978a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include "model.hxx"
23 #include "model_helper.hxx"
24 #include "unohelper.hxx"
25 #include "binding.hxx"
26 #include "submission.hxx"
27 #include "mip.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>
41 #include <algorithm>
43 // UNO classes
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)
70 #else
71 #define DBG_INVARIANT()
72 #endif
75 // The Model
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 */
89 Model::Model() :
90 msID(),
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
104 DBG_INVARIANT();
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");
171 #endif
174 // MIP management
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 );
195 else
196 ++aIter;
200 MIP Model::queryMIP( const XNode_t& xNode ) const
202 // travel up inheritance chain and inherit MIPs
203 MIP aRet;
204 for( XNode_t xCurrent = xNode;
205 xCurrent.is();
206 xCurrent = xCurrent->getParentNode() )
208 // iterate over all MIPs for this node, and join MIPs
209 MIP aMIP;
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 )
217 aRet = aMIP;
218 else
219 aRet.inherit( aMIP );
222 return aRet;
226 void Model::rebind()
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?" );
236 pBind->update();
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" );
259 bool bRet = false;
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
277 if( ! xChild.is() )
279 xChild.set(
280 xNode->getOwnerDocument()->createTextNode( OUString() ),
281 UNO_QUERY_THROW );
282 xNode->appendChild( xChild );
284 xNode = 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 );
302 bRet = true;
304 break;
306 default:
308 OSL_FAIL( "bound to unknown node type?" );
310 break;
314 return bRet;
317 void Model::loadInstance( sal_Int32 nInstance )
319 Sequence<PropertyValue> aSequence = mxInstances->getItem( nInstance );
321 // find URL from instance
322 OUString sURL;
323 bool bOnce = false;
324 getInstanceData( aSequence, nullptr, nullptr, &sURL, &bOnce );
326 // if we have a URL, load the document and set it into the instance
327 if( sURL.isEmpty() )
328 return;
332 Reference<XInputStream> xInput =
333 SimpleFileAccess::create( ::comphelper::getProcessComponentContext() )->openFileRead( sURL );
334 if( xInput.is() )
336 Reference<XDocument> xInstance =
337 getDocumentBuilder()->parse( xInput );
338 if( xInstance.is() )
340 OUString sEmpty;
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
366 bool bValid = true;
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();
374 return bValid;
378 // implement xforms::XModel
381 OUString Model::getID()
383 DBG_INVARIANT();
384 return msID;
387 void Model::setID( const OUString& sID )
389 DBG_INVARIANT();
390 msID = sID;
393 void Model::initialize()
395 DBG_ASSERT( ! mbInitialized, "model already initialized" );
397 // load instances
398 loadInstances();
400 // let's pretend we're initialized and rebind all bindings
401 mbInitialized = true;
402 rebind();
405 void Model::rebuild()
407 if( ! mbInitialized )
408 initialize();
409 else
410 rebind();
413 void Model::recalculate()
415 rebind();
418 void Model::revalidate()
420 // do nothing. We don't validate anyways!
423 void Model::refresh()
425 rebind();
429 void SAL_CALL Model::submitWithInteraction(
430 const OUString& sID,
431 const css::uno::Reference<css::task::XInteractionHandler>& _rxHandler )
433 DBG_INVARIANT();
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 ),
441 "wrong model" );
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;
458 return mxDataTypes;
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 );
478 return aInstance;
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 );
487 return aInstance;
491 // bindings management
494 css::uno::Reference<css::beans::XPropertySet> SAL_CALL Model::createBinding()
496 DBG_INVARIANT();
497 return new Binding();
500 css::uno::Reference<css::beans::XPropertySet> Model::cloneBinding( const css::uno::Reference<css::beans::XPropertySet>& xBinding )
502 DBG_INVARIANT();
503 XPropertySet_t xNewBinding = createBinding();
504 copy( xBinding, xNewBinding );
505 return xNewBinding;
508 css::uno::Reference<css::beans::XPropertySet> Model::getBinding( const OUString& sId )
510 DBG_INVARIANT();
511 return mxBindings->hasItem( sId ) ? mxBindings->getItem( sId ) : nullptr;
514 css::uno::Reference<css::container::XSet> Model::getBindings()
516 DBG_INVARIANT();
517 return mxBindings.get();
521 // submission management
524 css::uno::Reference<css::xforms::XSubmission> Model::createSubmission()
526 DBG_INVARIANT();
527 return new Submission();
530 css::uno::Reference<css::xforms::XSubmission> Model::cloneSubmission(const css::uno::Reference<css::beans::XPropertySet>& xSubmission)
532 DBG_INVARIANT();
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 )
541 DBG_INVARIANT();
542 css::uno::Reference<css::xforms::XSubmission> xSubmission;
543 if ( mxSubmissions->hasItem( sId ) )
544 xSubmission.set(mxSubmissions->getItem( sId ), css::uno::UNO_QUERY);
545 return xSubmission;
548 css::uno::Reference<css::container::XSet> Model::getSubmissions()
550 DBG_INVARIANT();
551 return mxSubmissions.get();
555 // implement XPropertySet & friends
558 #define HANDLE_ID 0
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 );
585 void Model::update()
587 rebuild();
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: */