bump product version to 5.0.4.1
[LibreOffice.git] / forms / source / xforms / model.cxx
blobc6774ddc0488a9a80f697a381def0ce360dcc15d
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/propertysetinfo.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <cppuhelper/supportsservice.hxx>
40 #include <cppuhelper/typeprovider.hxx>
42 #include <algorithm>
44 // UNO classes
45 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
46 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
47 #include <com/sun/star/lang/IllegalArgumentException.hpp>
48 #include <com/sun/star/xml/dom/XDocument.hpp>
49 #include <com/sun/star/xml/dom/XCharacterData.hpp>
50 #include <com/sun/star/xml/dom/NodeType.hpp>
51 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
52 #include <com/sun/star/uno/Sequence.hxx>
53 #include <com/sun/star/beans/PropertyValue.hpp>
54 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
55 #include <com/sun/star/io/XInputStream.hpp>
58 using com::sun::star::lang::XMultiServiceFactory;
59 using com::sun::star::lang::XUnoTunnel;
60 using com::sun::star::beans::XPropertySet;
61 using com::sun::star::beans::PropertyValue;
62 using com::sun::star::beans::PropertyVetoException;
63 using com::sun::star::beans::UnknownPropertyException;
64 using com::sun::star::util::VetoException;
65 using com::sun::star::lang::WrappedTargetException;
66 using com::sun::star::lang::IllegalArgumentException;
67 using com::sun::star::ucb::XSimpleFileAccess3;
68 using com::sun::star::ucb::SimpleFileAccess;
69 using com::sun::star::io::XInputStream;
71 using namespace com::sun::star::uno;
72 using namespace com::sun::star::xml::dom;
73 using namespace xforms;
76 #if OSL_DEBUG_LEVEL > 1
77 #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);
79 #define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
80 #else
81 #define DBG_INVARIANT()
82 #endif
87 // The Model
90 void Model::ensureAtLeastOneInstance()
92 if( ! mpInstances->hasItems() )
94 // create a default instance
95 newInstance( OUString(), OUString(), true );
101 /** Model default constructor; create empty model */
102 Model::Model() :
103 msID(),
104 mpBindings( NULL ),
105 mpSubmissions( NULL ),
106 mpInstances( new InstanceCollection ),
107 mxNamespaces( new NameContainer<OUString>() ),
108 mxBindings( mpBindings ),
109 mxSubmissions( mpSubmissions ),
110 mxInstances( mpInstances ),
111 mbInitialized( false ),
112 mbExternalData( true )
114 initializePropertySet();
116 // initialize bindings collections
117 // (not in initializer list to avoid use of incomplete 'this')
118 mpBindings = new BindingCollection( this );
119 mxBindings = mpBindings;
121 mpSubmissions = new SubmissionCollection( this );
122 mxSubmissions = mpSubmissions;
124 // invariant only holds after construction
125 DBG_INVARIANT();
128 Model::~Model() throw()
130 // give up bindings & submissions; the mxBindings/mxSubmissions
131 // references will then delete them
132 mpBindings = NULL;
133 mpSubmissions = NULL;
136 static Model* lcl_getModel( const Reference<XUnoTunnel>& xTunnel )
138 Model* pModel = NULL;
139 if( xTunnel.is() )
140 pModel = reinterpret_cast<Model*>(
141 xTunnel->getSomething( Model::getUnoTunnelID() ) );
142 return pModel;
145 Model* Model::getModel( const Reference<XModel>& xModel )
147 return lcl_getModel( Reference<XUnoTunnel>( xModel, UNO_QUERY ) );
150 EvaluationContext Model::getEvaluationContext()
152 // the default context is the top-level element node. A default
153 // node (instanceData' is inserted when there is no default node
154 Reference<XDocument> xInstance = getDefaultInstance();
155 Reference<XNode> xElement( xInstance->getDocumentElement(), UNO_QUERY );
157 // no element found? Then insert default element 'instanceData'
158 if( ! xElement.is() )
160 xElement = Reference<XNode>(
161 xInstance->createElement( "instanceData" ),
162 UNO_QUERY_THROW );
163 xInstance->appendChild( xElement );
166 OSL_ENSURE( xElement.is() &&
167 xElement->getNodeType() == NodeType_ELEMENT_NODE,
168 "no element in evaluation context" );
170 return EvaluationContext( xElement, this, mxNamespaces, 0, 1 );
174 Model::IntSequence_t Model::getUnoTunnelID()
176 static cppu::OImplementationId aImplementationId;
177 return aImplementationId.getImplementationId();
181 void Model::setForeignSchema( const XDocument_t& rDocument )
183 mxForeignSchema = rDocument;
187 void Model::setSchemaRef( const OUString& rSchemaRef )
189 msSchemaRef = rSchemaRef;
193 void Model::setNamespaces( const XNameContainer_t& rNamespaces )
195 if( rNamespaces.is() )
196 mxNamespaces = rNamespaces;
200 void Model::setExternalData( bool _bData )
202 mbExternalData = _bData;
205 #if OSL_DEBUG_LEVEL > 1
206 void Model::dbg_assertInvariant() const
208 OSL_ENSURE( mpInstances != NULL, "no instances found" );
209 OSL_ENSURE( mxInstances.is(), "No instance container!" );
211 OSL_ENSURE( mpBindings != NULL, "no bindings element" );
212 OSL_ENSURE( mxBindings.is(), "No Bindings container" );
214 OSL_ENSURE( mpSubmissions != NULL, "no submissions element" );
215 OSL_ENSURE( mxSubmissions.is(), "No Submission container" );
217 #endif
221 // MIP management
222 void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP )
224 OSL_ENSURE( pTag != NULL, "empty tag?" );
225 OSL_ENSURE( xNode.is(), "no node" );
227 MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) );
228 maMIPs.insert( aValue );
231 void Model::removeMIPs( void* pTag )
233 OSL_ENSURE( pTag != NULL, "empty tag?" );
235 for( MIPs_t::iterator aIter = maMIPs.begin();
236 aIter != maMIPs.end(); )
238 if( aIter->second.first == pTag )
240 MIPs_t::iterator next( aIter ); ++next;
241 maMIPs.erase( aIter );
242 aIter = next;
244 else
245 ++aIter;
249 MIP Model::queryMIP( const XNode_t& xNode ) const
251 // travel up inheritance chain and inherit MIPs
252 MIP aRet;
253 for( XNode_t xCurrent = xNode;
254 xCurrent.is();
255 xCurrent = xCurrent->getParentNode() )
257 // iterate over all MIPs for this node, and join MIPs
258 MIP aMIP;
259 MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent );
260 MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent );
261 for( ; aIter != aEnd; ++aIter )
262 aMIP.join( aIter->second.second );
264 // inherit from current node (or set if we are at the start node)
265 if( xCurrent == xNode )
266 aRet = aMIP;
267 else
268 aRet.inherit( aMIP );
271 return aRet;
276 void Model::rebind()
278 OSL_ENSURE( mpBindings != NULL, "bindings?" );
280 // iterate over all bindings and call update
281 sal_Int32 nCount = mpBindings->countItems();
282 for( sal_Int32 i = 0; i < nCount; i++ )
284 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
285 OSL_ENSURE( pBind != NULL, "binding?" );
286 pBind->update();
292 void Model::deferNotifications( bool bDefer )
294 // iterate over all bindings and defer notifications
295 sal_Int32 nCount = mpBindings->countItems();
296 for( sal_Int32 i = 0; i < nCount; i++ )
298 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
299 OSL_ENSURE( pBind != NULL, "binding?" );
300 pBind->deferNotifications( bDefer );
305 bool Model::setSimpleContent( const XNode_t& xConstNode,
306 const OUString& sValue )
308 OSL_ENSURE( xConstNode.is(), "need node to set data" );
310 bool bRet = false;
311 if( xConstNode.is() )
313 // non-const node reference so we can assign children (if necessary)
314 XNode_t xNode( xConstNode );
316 switch( xNode->getNodeType() )
318 case NodeType_ELEMENT_NODE:
320 // find first text node child
321 Reference<XNode> xChild;
322 for( xChild = xNode->getFirstChild();
323 xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE;
324 xChild = xChild->getNextSibling() )
325 ; // empty loop; only find first text node child
327 // create text node, if none is found
328 if( ! xChild.is() )
330 xChild = Reference<XNode>(
331 xNode->getOwnerDocument()->createTextNode( OUString() ),
332 UNO_QUERY_THROW );
333 xNode->appendChild( xChild );
335 xNode = xChild;
337 OSL_ENSURE( xNode.is() &&
338 xNode->getNodeType() == NodeType_TEXT_NODE,
339 "text node creation failed?" );
341 // no break; continue as with text node:
343 case NodeType_TEXT_NODE:
344 case NodeType_ATTRIBUTE_NODE:
346 // set the node value (defer notifications)
347 if( xNode->getNodeValue() != sValue )
349 deferNotifications( true );
350 xNode->setNodeValue( sValue );
351 deferNotifications( false );
353 bRet = true;
355 break;
357 default:
359 OSL_FAIL( "bound to unknown node type?" );
361 break;
365 return bRet;
368 void Model::loadInstance( sal_Int32 nInstance )
370 Sequence<PropertyValue> aSequence = mpInstances->getItem( nInstance );
372 // find URL from instance
373 OUString sURL;
374 bool bOnce = false;
375 getInstanceData( aSequence, NULL, NULL, &sURL, &bOnce );
377 // if we have a URL, load the document and set it into the instance
378 if( !sURL.isEmpty() )
382 Reference<XInputStream> xInput =
383 Reference<XSimpleFileAccess3>( SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) )->openFileRead( sURL );
384 if( xInput.is() )
386 Reference<XDocument> xInstance =
387 getDocumentBuilder()->parse( xInput );
388 if( xInstance.is() )
390 OUString sEmpty;
391 setInstanceData( aSequence, NULL, &xInstance,
392 bOnce ? &sEmpty : &sURL, NULL);
393 mpInstances->setItem( nInstance, aSequence );
397 catch( const Exception& )
399 // couldn't load the instance -> ignore!
404 void Model::loadInstances()
406 // iterate over instance array to get PropertyValue-Sequence
407 const sal_Int32 nInstances = mpInstances->countItems();
408 for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ )
410 loadInstance( nInstance );
415 bool Model::isValid() const
417 bool bValid = true;
418 sal_Int32 nCount = mpBindings->countItems();
419 for( sal_Int32 i = 0; bValid && i < nCount; i++ )
421 Binding* pBind = Binding::getBinding( mpBindings->Collection<XPropertySet_t>::getItem( i ) );
422 OSL_ENSURE( pBind != NULL, "binding?" );
423 bValid = pBind->isValid();
425 return bValid;
431 // implement xforms::XModel
434 OUString Model::getID()
435 throw( RuntimeException, std::exception )
437 DBG_INVARIANT();
438 return msID;
441 void Model::setID( const OUString& sID )
442 throw( RuntimeException, std::exception )
444 DBG_INVARIANT();
445 msID = sID;
448 void Model::initialize()
449 throw( RuntimeException, std::exception )
451 DBG_ASSERT( ! mbInitialized, "model already initialized" );
453 // load instances
454 loadInstances();
456 // let's pretend we're initialized and rebind all bindings
457 mbInitialized = true;
458 rebind();
461 void Model::rebuild()
462 throw( RuntimeException, std::exception )
464 if( ! mbInitialized )
465 initialize();
466 else
467 rebind();
470 void Model::recalculate()
471 throw( RuntimeException, std::exception )
473 rebind();
476 void Model::revalidate()
477 throw( RuntimeException, std::exception )
479 // do nothing. We don't validate anyways!
482 void Model::refresh()
483 throw( RuntimeException, std::exception )
485 rebind();
489 void SAL_CALL Model::submitWithInteraction(
490 const OUString& sID,
491 const XInteractionHandler_t& _rxHandler )
492 throw( VetoException,
493 WrappedTargetException,
494 RuntimeException, std::exception )
496 DBG_INVARIANT();
498 if( mpSubmissions->hasItem( sID ) )
500 Submission* pSubmission =
501 Submission::getSubmission( mpSubmissions->getItem( sID ) );
502 OSL_ENSURE( pSubmission != NULL, "no submission?" );
503 OSL_ENSURE( pSubmission->getModel() == Reference<XModel>( this ),
504 "wrong model" );
506 // submit. All exceptions are allowed to leave.
507 pSubmission->submitWithInteraction( _rxHandler );
511 void Model::submit( const OUString& sID )
512 throw( VetoException, WrappedTargetException, RuntimeException, std::exception )
514 submitWithInteraction( sID, NULL );
517 Model::XDataTypeRepository_t SAL_CALL Model::getDataTypeRepository( )
518 throw( RuntimeException, std::exception )
520 if ( !mxDataTypes.is() )
521 mxDataTypes = new ODataTypeRepository;
523 return mxDataTypes;
527 // instance management
530 Model::XSet_t Model::getInstances()
531 throw( RuntimeException, std::exception )
533 return mxInstances;
536 Model::XDocument_t Model::getInstanceDocument( const OUString& rName )
537 throw( RuntimeException, std::exception )
539 ensureAtLeastOneInstance();
540 Reference<XDocument> aInstance;
541 sal_Int32 nInstance = lcl_findInstance( mpInstances, rName );
542 if( nInstance != -1 )
543 getInstanceData( mpInstances->getItem( nInstance ),
544 NULL, &aInstance, NULL, NULL );
545 return aInstance;
548 Model::XDocument_t SAL_CALL Model::getDefaultInstance()
549 throw( RuntimeException, std::exception )
551 ensureAtLeastOneInstance();
552 DBG_ASSERT( mpInstances->countItems() > 0, "no instance?" );
553 Reference<XDocument> aInstance;
554 getInstanceData( mpInstances->getItem( 0 ), NULL, &aInstance, NULL, NULL );
555 return aInstance;
561 // bindings management
564 Model::XPropertySet_t SAL_CALL Model::createBinding()
565 throw( RuntimeException, std::exception )
567 DBG_INVARIANT();
568 return new Binding();
571 Model::XPropertySet_t Model::cloneBinding( const XPropertySet_t& xBinding )
572 throw( RuntimeException, std::exception )
574 DBG_INVARIANT();
575 XPropertySet_t xNewBinding = createBinding();
576 copy( xBinding, xNewBinding );
577 return xNewBinding;
580 Model::XPropertySet_t Model::getBinding( const OUString& sId )
581 throw( RuntimeException, std::exception )
583 DBG_INVARIANT();
584 return mpBindings->hasItem( sId ) ? mpBindings->getItem( sId ) : NULL;
587 Model::XSet_t Model::getBindings()
588 throw( RuntimeException, std::exception )
590 DBG_INVARIANT();
591 return mxBindings;
597 // submission management
600 Model::XSubmission_t Model::createSubmission()
601 throw( RuntimeException, std::exception )
603 DBG_INVARIANT();
604 return new Submission();
607 Model::XSubmission_t Model::cloneSubmission(const XPropertySet_t& xSubmission)
608 throw( RuntimeException, std::exception )
610 DBG_INVARIANT();
611 XSubmission_t xNewSubmission = createSubmission();
612 XPropertySet_t xAsPropertySet( xNewSubmission.get() );
613 copy( xSubmission.get(), xAsPropertySet );
614 return xNewSubmission;
617 Model::XSubmission_t Model::getSubmission( const OUString& sId )
618 throw( RuntimeException, std::exception )
620 DBG_INVARIANT();
621 XSubmission_t xSubmission;
622 if ( mpSubmissions->hasItem( sId ) )
623 xSubmission.set(mpSubmissions->getItem( sId ), css::uno::UNO_QUERY);
624 return xSubmission;
627 Model::XSet_t Model::getSubmissions()
628 throw( RuntimeException, std::exception )
630 DBG_INVARIANT();
631 return mxSubmissions;
635 // implement XPropertySet & friends
638 #define HANDLE_ID 0
639 #define HANDLE_ForeignSchema 3
640 #define HANDLE_SchemaRef 4
641 #define HANDLE_Namespaces 5
642 #define HANDLE_ExternalData 6
644 #define REGISTER_PROPERTY( property, type ) \
645 registerProperty( PROPERTY( property, type ), \
646 new DirectPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
648 #define REGISTER_PROPERTY_API( property, type ) \
649 registerProperty( PROPERTY( property, type ), \
650 new APIPropertyAccessor< Model, type >( this, &Model::set##property, &Model::get##property ) );
652 #define REGISTER_BOOL_PROPERTY( property ) \
653 registerProperty( PROPERTY( property, sal_Bool ), \
654 new BooleanPropertyAccessor< Model, bool >( this, &Model::set##property, &Model::get##property ) );
656 void Model::initializePropertySet()
658 REGISTER_PROPERTY_API ( ID, OUString );
659 REGISTER_PROPERTY ( ForeignSchema, XDocument_t );
660 REGISTER_PROPERTY ( SchemaRef, OUString );
661 REGISTER_PROPERTY ( Namespaces, XNameContainer_t );
662 REGISTER_BOOL_PROPERTY( ExternalData );
665 void Model::update()
666 throw( RuntimeException, std::exception )
668 rebuild();
672 sal_Int64 Model::getSomething( const IntSequence_t& xId )
673 throw( RuntimeException, std::exception )
675 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL );
678 Sequence<sal_Int8> Model::getImplementationId()
679 throw( RuntimeException )
681 return css::uno::Sequence<sal_Int8>();
684 OUString Model::getImplementationName()
685 throw (css::uno::RuntimeException, std::exception)
687 return OUString("com.sun.star.form.Model");
690 sal_Bool Model::supportsService(OUString const & ServiceName)
691 throw (css::uno::RuntimeException, std::exception)
693 return cppu::supportsService(this, ServiceName);
696 css::uno::Sequence<OUString> Model::getSupportedServiceNames()
697 throw (css::uno::RuntimeException, std::exception)
699 return css::uno::Sequence<OUString>{"com.sun.star.xforms.Model"};
702 extern "C" SAL_DLLPUBLIC_EXPORT ::com::sun::star::uno::XInterface* SAL_CALL
703 com_sun_star_form_Model_get_implementation(::com::sun::star::uno::XComponentContext*,
704 ::com::sun::star::uno::Sequence<css::uno::Any> const &)
706 return cppu::acquire(new xforms::Model());
710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */