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/propertysetinfo.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <cppuhelper/supportsservice.hxx>
40 #include <cppuhelper/typeprovider.hxx>
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)
81 #define DBG_INVARIANT()
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 */
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
128 Model::~Model() throw()
130 // give up bindings & submissions; the mxBindings/mxSubmissions
131 // references will then delete them
133 mpSubmissions
= NULL
;
136 static Model
* lcl_getModel( const Reference
<XUnoTunnel
>& xTunnel
)
138 Model
* pModel
= NULL
;
140 pModel
= reinterpret_cast<Model
*>(
141 xTunnel
->getSomething( Model::getUnoTunnelID() ) );
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" ),
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" );
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
);
249 MIP
Model::queryMIP( const XNode_t
& xNode
) const
251 // travel up inheritance chain and inherit MIPs
253 for( XNode_t xCurrent
= xNode
;
255 xCurrent
= xCurrent
->getParentNode() )
257 // iterate over all MIPs for this node, and join MIPs
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
)
268 aRet
.inherit( aMIP
);
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?" );
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" );
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
330 xChild
= Reference
<XNode
>(
331 xNode
->getOwnerDocument()->createTextNode( OUString() ),
333 xNode
->appendChild( 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 );
359 OSL_FAIL( "bound to unknown node type?" );
368 void Model::loadInstance( sal_Int32 nInstance
)
370 Sequence
<PropertyValue
> aSequence
= mpInstances
->getItem( nInstance
);
372 // find URL from instance
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
);
386 Reference
<XDocument
> xInstance
=
387 getDocumentBuilder()->parse( xInput
);
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
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();
431 // implement xforms::XModel
434 OUString
Model::getID()
435 throw( RuntimeException
, std::exception
)
441 void Model::setID( const OUString
& sID
)
442 throw( RuntimeException
, std::exception
)
448 void Model::initialize()
449 throw( RuntimeException
, std::exception
)
451 DBG_ASSERT( ! mbInitialized
, "model already initialized" );
456 // let's pretend we're initialized and rebind all bindings
457 mbInitialized
= true;
461 void Model::rebuild()
462 throw( RuntimeException
, std::exception
)
464 if( ! mbInitialized
)
470 void Model::recalculate()
471 throw( RuntimeException
, std::exception
)
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
)
489 void SAL_CALL
Model::submitWithInteraction(
491 const XInteractionHandler_t
& _rxHandler
)
492 throw( VetoException
,
493 WrappedTargetException
,
494 RuntimeException
, std::exception
)
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 ),
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
;
527 // instance management
530 Model::XSet_t
Model::getInstances()
531 throw( RuntimeException
, std::exception
)
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
);
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
);
561 // bindings management
564 Model::XPropertySet_t SAL_CALL
Model::createBinding()
565 throw( RuntimeException
, std::exception
)
568 return new Binding();
571 Model::XPropertySet_t
Model::cloneBinding( const XPropertySet_t
& xBinding
)
572 throw( RuntimeException
, std::exception
)
575 XPropertySet_t xNewBinding
= createBinding();
576 copy( xBinding
, xNewBinding
);
580 Model::XPropertySet_t
Model::getBinding( const OUString
& sId
)
581 throw( RuntimeException
, std::exception
)
584 return mpBindings
->hasItem( sId
) ? mpBindings
->getItem( sId
) : NULL
;
587 Model::XSet_t
Model::getBindings()
588 throw( RuntimeException
, std::exception
)
597 // submission management
600 Model::XSubmission_t
Model::createSubmission()
601 throw( RuntimeException
, std::exception
)
604 return new Submission();
607 Model::XSubmission_t
Model::cloneSubmission(const XPropertySet_t
& xSubmission
)
608 throw( RuntimeException
, std::exception
)
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
)
621 XSubmission_t xSubmission
;
622 if ( mpSubmissions
->hasItem( sId
) )
623 xSubmission
.set(mpSubmissions
->getItem( sId
), css::uno::UNO_QUERY
);
627 Model::XSet_t
Model::getSubmissions()
628 throw( RuntimeException
, std::exception
)
631 return mxSubmissions
;
635 // implement XPropertySet & friends
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
);
666 throw( RuntimeException
, std::exception
)
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: */