bump product version to 4.1.6.2
[LibreOffice.git] / forms / source / xforms / model.cxx
blob60863373746e3be16564f7db990c0d31ac91c627
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/typeprovider.hxx>
41 #include <algorithm>
43 // UNO classes
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)
79 #else
80 #define DBG_INVARIANT()
81 #endif
86 // The Model
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 */
101 Model::Model() :
102 msID(),
103 mpBindings( NULL ),
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
124 DBG_INVARIANT();
127 Model::~Model() throw()
129 // give up bindings & submissions; the mxBindings/mxSubmissions
130 // references will then delete them
131 mpBindings = NULL;
132 mpSubmissions = NULL;
135 static Model* lcl_getModel( const Reference<XUnoTunnel>& xTunnel )
137 Model* pModel = NULL;
138 if( xTunnel.is() )
139 pModel = reinterpret_cast<Model*>(
140 xTunnel->getSomething( Model::getUnoTunnelID() ) );
141 return pModel;
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" ),
161 UNO_QUERY_THROW );
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
191 return msSchemaRef;
194 void Model::setSchemaRef( const OUString& rSchemaRef )
196 msSchemaRef = rSchemaRef;
199 Model::XNameContainer_t Model::getNamespaces() const
201 return mxNamespaces;
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" );
232 #endif
236 // MIP management
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 );
259 aIter = next;
261 else
262 ++aIter;
266 MIP Model::queryMIP( const XNode_t& xNode ) const
268 // travel up inheritance chain and inherit MIPs
269 MIP aRet;
270 for( XNode_t xCurrent = xNode;
271 xCurrent.is();
272 xCurrent = xCurrent->getParentNode() )
274 // iterate over all MIPs for this node, and join MIPs
275 MIP aMIP;
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 )
283 aRet = aMIP;
284 else
285 aRet.inherit( aMIP );
288 return aRet;
293 void Model::rebind()
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?" );
303 pBind->update();
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" );
327 bool bRet = false;
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
345 if( ! xChild.is() )
347 xChild = Reference<XNode>(
348 xNode->getOwnerDocument()->createTextNode( OUString() ),
349 UNO_QUERY_THROW );
350 xNode->appendChild( xChild );
352 xNode = 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 );
370 bRet = true;
372 break;
374 default:
376 OSL_FAIL( "bound to unknown node type?" );
378 break;
382 return bRet;
385 void Model::loadInstance( sal_Int32 nInstance )
387 Sequence<PropertyValue> aSequence = mpInstances->getItem( nInstance );
389 // find URL from instance
390 OUString sURL;
391 bool bOnce = false;
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 );
401 if( xInput.is() )
403 Reference<XDocument> xInstance =
404 getDocumentBuilder()->parse( xInput );
405 if( xInstance.is() )
407 OUString sEmpty;
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
438 bool bValid = true;
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();
446 return bValid;
452 // implement xforms::XModel
455 OUString Model::getID()
456 throw( RuntimeException )
458 DBG_INVARIANT();
459 return msID;
462 void Model::setID( const OUString& sID )
463 throw( RuntimeException )
465 DBG_INVARIANT();
466 msID = sID;
469 void Model::initialize()
470 throw( RuntimeException )
472 DBG_ASSERT( ! mbInitialized, "model already initialized" );
474 // load instances
475 loadInstances();
477 // let's pretend we're initialized and rebind all bindings
478 mbInitialized = true;
479 rebind();
482 void Model::rebuild()
483 throw( RuntimeException )
485 if( ! mbInitialized )
486 initialize();
487 else
488 rebind();
491 void Model::recalculate()
492 throw( RuntimeException )
494 rebind();
497 void Model::revalidate()
498 throw( RuntimeException )
500 // do nothing. We don't validate anyways!
503 void Model::refresh()
504 throw( RuntimeException )
506 rebind();
510 void SAL_CALL Model::submitWithInteraction(
511 const OUString& sID,
512 const XInteractionHandler_t& _rxHandler )
513 throw( VetoException,
514 WrappedTargetException,
515 RuntimeException )
517 DBG_INVARIANT();
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 ),
525 "wrong model" );
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;
544 return mxDataTypes;
548 // instance management
551 Model::XSet_t Model::getInstances()
552 throw( RuntimeException )
554 return mxInstances;
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 );
566 return aInstance;
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 );
576 return aInstance;
582 // bindings management
585 Model::XPropertySet_t SAL_CALL Model::createBinding()
586 throw( RuntimeException )
588 DBG_INVARIANT();
589 return new Binding();
592 Model::XPropertySet_t Model::cloneBinding( const XPropertySet_t& xBinding )
593 throw( RuntimeException )
595 DBG_INVARIANT();
596 XPropertySet_t xNewBinding = createBinding();
597 copy( xBinding, xNewBinding );
598 return xNewBinding;
601 Model::XPropertySet_t Model::getBinding( const OUString& sId )
602 throw( RuntimeException )
604 DBG_INVARIANT();
605 return mpBindings->hasItem( sId ) ? mpBindings->getItem( sId ) : NULL;
608 Model::XSet_t Model::getBindings()
609 throw( RuntimeException )
611 DBG_INVARIANT();
612 return mxBindings;
618 // submission management
621 Model::XSubmission_t Model::createSubmission()
622 throw( RuntimeException )
624 DBG_INVARIANT();
625 return new Submission();
628 Model::XSubmission_t Model::cloneSubmission(const XPropertySet_t& xSubmission)
629 throw( RuntimeException )
631 DBG_INVARIANT();
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 )
641 DBG_INVARIANT();
642 XSubmission_t xSubmission;
643 if ( mpSubmissions->hasItem( sId ) )
644 xSubmission = xSubmission.query( mpSubmissions->getItem( sId ) );
645 return xSubmission;
648 Model::XSet_t Model::getSubmissions()
649 throw( RuntimeException )
651 DBG_INVARIANT();
652 return mxSubmissions;
656 // implement XPropertySet & friends
659 #define HANDLE_ID 0
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 );
686 void Model::update()
687 throw( RuntimeException )
689 rebuild();
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: */