update dev300-m58
[ooovba.git] / forms / source / xforms / model_ui.cxx
blobd2f6bc6e95f9bf8cb29cf59a617b1673c89debb7
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: model_ui.cxx,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_forms.hxx"
34 #include "model.hxx"
35 #include "model_helper.hxx"
36 #include "mip.hxx"
37 #include "evaluationcontext.hxx"
38 #include "unohelper.hxx"
39 #include "submission/serialization_app_xml.hxx"
40 #include "resourcehelper.hxx"
41 #include "xmlhelper.hxx"
42 #include "convert.hxx"
44 #include <rtl/ustring.hxx>
45 #include <rtl/ustrbuf.hxx>
46 #include <tools/debug.hxx>
48 // UNO classes
49 #include <com/sun/star/xml/dom/XNode.hpp>
50 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
51 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
52 #include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
53 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
54 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
55 #include <com/sun/star/beans/PropertyValue.hpp>
56 #include <com/sun/star/io/XInputStream.hpp>
57 #include <com/sun/star/io/XActiveDataSink.hpp>
58 #include <com/sun/star/io/XTextInputStream.hpp>
59 #include <com/sun/star/container/XEnumeration.hpp>
60 #include <com/sun/star/container/XNameContainer.hpp>
61 #include <com/sun/star/frame/XModel.hpp>
62 #include <com/sun/star/xforms/XFormsSupplier.hpp>
63 #include <com/sun/star/xforms/XDataTypeRepository.hpp>
64 #include <com/sun/star/xsd/XDataType.hpp>
65 #include <com/sun/star/xsd/DataTypeClass.hpp>
68 using rtl::OUString;
69 using rtl::OUStringBuffer;
70 using com::sun::star::beans::PropertyValue;
71 using com::sun::star::io::XInputStream;
72 using com::sun::star::io::XActiveDataSink;
73 using com::sun::star::io::XTextInputStream;
74 using com::sun::star::container::XEnumeration;
75 using com::sun::star::container::XNameContainer;
76 using com::sun::star::xforms::XFormsSupplier;
78 using namespace xforms;
79 using namespace com::sun::star::uno;
80 using namespace com::sun::star::xml::dom;
81 using namespace com::sun::star::xml::xpath;
86 // implement XFormsUIHelper1
89 OUString Model::getDefaultServiceNameForNode( const XNode_t& xNode )
90 throw( RuntimeException )
92 // determine service for control. string/text field is default.
93 OUString sService = OUSTRING("com.sun.star.form.component.TextField");
95 // query repository for suitable type
96 OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
97 OUString sTypeName = queryMIP( xNode ).getTypeName();
98 if( mxDataTypes->hasByName( sTypeName ) )
100 OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
101 "has or has not?" );
103 switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
105 case com::sun::star::xsd::DataTypeClass::BOOLEAN:
106 sService = OUSTRING("com.sun.star.form.component.CheckBox");
107 break;
108 case com::sun::star::xsd::DataTypeClass::DOUBLE:
109 case com::sun::star::xsd::DataTypeClass::DECIMAL:
110 case com::sun::star::xsd::DataTypeClass::FLOAT:
111 sService = OUSTRING("com.sun.star.form.component.NumericField");
112 break;
114 case com::sun::star::xsd::DataTypeClass::STRING:
115 case com::sun::star::xsd::DataTypeClass::DURATION:
116 case com::sun::star::xsd::DataTypeClass::DATETIME:
117 case com::sun::star::xsd::DataTypeClass::TIME:
118 case com::sun::star::xsd::DataTypeClass::DATE:
119 case com::sun::star::xsd::DataTypeClass::gYearMonth:
120 case com::sun::star::xsd::DataTypeClass::gYear:
121 case com::sun::star::xsd::DataTypeClass::gMonthDay:
122 case com::sun::star::xsd::DataTypeClass::gDay:
123 case com::sun::star::xsd::DataTypeClass::gMonth:
124 case com::sun::star::xsd::DataTypeClass::hexBinary:
125 case com::sun::star::xsd::DataTypeClass::base64Binary:
126 case com::sun::star::xsd::DataTypeClass::anyURI:
127 case com::sun::star::xsd::DataTypeClass::QName:
128 case com::sun::star::xsd::DataTypeClass::NOTATION:
129 default:
130 // keep default
131 break;
135 return sService;
139 void lcl_OutPosition( OUStringBuffer& rBuffer,
140 const Reference<XNode>& xNode )
142 OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
144 // count # of occurences of this node
145 sal_Int32 nFound = 0;
146 sal_Int32 nPosition = -1;
147 if( xNode->getParentNode().is() )
149 for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
150 xIter != NULL;
151 xIter = xIter->getNextSibling() )
153 if( xIter->getNodeType() == xNode->getNodeType() &&
154 xIter->getNodeName() == xNode->getNodeName() &&
155 xIter->getNamespaceURI() == xNode->getNamespaceURI() )
157 nFound++;
158 if( xIter == xNode )
159 nPosition = nFound;
163 OSL_ENSURE( nFound > 0 && nPosition > 0, "node not found???" );
165 // output position (if necessary)
166 if( nFound > 1 )
168 rBuffer.insert( 0, sal_Unicode(']') );
169 rBuffer.insert( 0, nPosition );
170 rBuffer.insert( 0, sal_Unicode('[') );
174 void lcl_OutName( OUStringBuffer& rBuffer,
175 const Reference<XNode>& xNode )
177 rBuffer.insert( 0, xNode->getNodeName() );
178 OUString sPrefix = xNode->getPrefix();
179 if( sPrefix.getLength() > 0 )
181 rBuffer.insert( 0, sal_Unicode(':') );
182 rBuffer.insert( 0, sPrefix );
186 void lcl_OutInstance( OUStringBuffer& rBuffer,
187 const Reference<XNode>& xNode,
188 Model* pModel )
190 Reference<XDocument> xDoc = xNode->getOwnerDocument();
192 if( xDoc != pModel->getDefaultInstance() )
194 rBuffer.insert( 0, OUSTRING("')") );
196 // iterate over instances, and find the right one
197 OUString sInstanceName;
198 Reference<XEnumeration> xEnum =
199 pModel->getInstances()->createEnumeration();
200 while( ( sInstanceName.getLength() == 0 ) && xEnum->hasMoreElements() )
202 Sequence<PropertyValue> aValues;
203 xEnum->nextElement() >>= aValues;
205 // get ID and instance
206 OUString sId;
207 Reference<XDocument> xInstance;
208 getInstanceData( aValues, &sId, &xInstance, NULL, NULL );
210 // now check whether this was our instance:
211 if( xInstance == xDoc )
212 sInstanceName = sId;
215 rBuffer.insert( 0, sInstanceName );
216 rBuffer.insert( 0, OUSTRING("instance('") );
220 OUString Model::getDefaultBindingExpressionForNode(
221 const XNode_t& xNode,
222 const EvaluationContext& rContext)
224 OSL_ENSURE( xNode.is(), "need node" );
226 // iterate upwards and put sections into the expression buffer.
227 // Stop iteration either at context node (relative expression) or
228 // at document root, whichever occurs first.
229 OUStringBuffer aBuffer;
230 for( Reference<XNode> xCurrent = xNode;
231 xCurrent.is() && xCurrent != rContext.mxContextNode;
232 xCurrent = xCurrent->getParentNode() )
234 // insert a '/' for every step except the first
235 if( aBuffer.getLength() > 0 )
236 aBuffer.insert( 0, sal_Unicode('/') );
238 switch( xCurrent->getNodeType() )
240 case NodeType_ELEMENT_NODE:
241 lcl_OutPosition( aBuffer, xCurrent );
242 lcl_OutName( aBuffer, xCurrent );
243 break;
245 case NodeType_TEXT_NODE:
246 lcl_OutPosition( aBuffer, xCurrent );
247 aBuffer.insert( 0, OUSTRING("text()") );
248 break;
250 case NodeType_ATTRIBUTE_NODE:
251 lcl_OutName( aBuffer, xCurrent );
252 aBuffer.insert( 0, sal_Unicode('@') );
253 break;
255 case NodeType_DOCUMENT_NODE:
256 // check for which instance we have
257 lcl_OutInstance( aBuffer, xCurrent, this );
258 break;
260 default:
261 // unknown type? fail!
262 OSL_ENSURE( false, "unknown node type!" );
263 xCurrent.set( NULL );
264 aBuffer.makeStringAndClear();
265 // we'll remove the slash below
266 aBuffer.insert( 0, sal_Unicode('/') );
267 break;
271 return aBuffer.makeStringAndClear();
276 OUString Model::getDefaultBindingExpressionForNode( const XNode_t& xNode )
277 throw( RuntimeException )
279 return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
282 bool lcl_isWhitespace( const OUString& rString )
284 sal_Int32 nLength = rString.getLength();
285 const sal_Unicode* pStr = rString.getStr();
287 bool bWhitespace = true;
288 for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
290 sal_Unicode c = pStr[i];
291 bWhitespace = ( c == sal_Unicode(0x09) ||
292 c == sal_Unicode(0x0A) ||
293 c == sal_Unicode(0x0D) ||
294 c == sal_Unicode(0x20) );
296 return bWhitespace;
299 OUString Model::getNodeDisplayName( const XNode_t& xNode,
300 sal_Bool bDetail )
301 throw( RuntimeException )
303 OUStringBuffer aBuffer;
305 switch( xNode->getNodeType() )
307 case NodeType_ELEMENT_NODE:
308 lcl_OutName( aBuffer, xNode );
309 break;
311 case NodeType_TEXT_NODE:
313 OUString sContent = xNode->getNodeValue();
314 if( bDetail || ! lcl_isWhitespace( sContent ) )
316 aBuffer.append( sal_Unicode('"') );
317 aBuffer.append( Convert::collapseWhitespace( sContent ) );
318 aBuffer.append( sal_Unicode('"') );
321 break;
323 case NodeType_ATTRIBUTE_NODE:
324 lcl_OutName( aBuffer, xNode );
325 aBuffer.insert( 0, sal_Unicode('@') );
326 break;
328 case NodeType_DOCUMENT_NODE:
329 if( xNode == getDefaultInstance() )
330 aBuffer.append( sal_Unicode('/') );
331 else
332 lcl_OutInstance( aBuffer, xNode, this );
333 break;
335 default:
336 // unknown type? fail!
337 OSL_ENSURE( false, "unknown node type!" );
338 break;
341 return aBuffer.makeStringAndClear();
344 OUString Model::getNodeName( const XNode_t& xNode )
345 throw( RuntimeException )
347 OUStringBuffer aBuffer;
349 switch( xNode->getNodeType() )
351 case NodeType_ELEMENT_NODE:
352 case NodeType_ATTRIBUTE_NODE:
353 lcl_OutName( aBuffer, xNode );
354 break;
356 case NodeType_TEXT_NODE:
357 case NodeType_DOCUMENT_NODE:
358 default:
359 // unknown type? fail!
360 OSL_ENSURE( false, "no name for this node type!" );
361 break;
364 return aBuffer.makeStringAndClear();
367 OUString Model::getBindingName( const XPropertySet_t& xBinding,
368 sal_Bool /*bDetail*/ )
369 throw( RuntimeException )
371 OUString sID;
372 xBinding->getPropertyValue( OUSTRING("BindingID" ) ) >>= sID;
373 OUString sExpression;
374 xBinding->getPropertyValue( OUSTRING("BindingExpression" ) ) >>= sExpression;
376 OUStringBuffer aBuffer;
377 if( sID.getLength() > 0 )
379 aBuffer.append( sID );
380 aBuffer.append( OUSTRING(" (" ));
381 aBuffer.append( sExpression );
382 aBuffer.append( OUSTRING(")" ));
384 else
385 aBuffer.append( sExpression );
387 return aBuffer.makeStringAndClear();
390 OUString Model::getSubmissionName( const XPropertySet_t& xSubmission,
391 sal_Bool /*bDetail*/ )
392 throw( RuntimeException )
394 OUString sID;
395 xSubmission->getPropertyValue( OUSTRING("ID") ) >>= sID;
396 return sID;
399 Model::XPropertySet_t Model::cloneBindingAsGhost( const XPropertySet_t &xBinding )
400 throw( RuntimeException )
402 // Create a new binding instance first...
403 Binding *pBinding = new Binding();
405 // ...and bump up the "defered notification counter"
406 // to prevent this binding from contributing to the
407 // MIPs table...
408 pBinding->deferNotifications(true);
410 // Copy the propertyset and return result...
411 XPropertySet_t xNewBinding(pBinding);
412 copy( xBinding, xNewBinding );
413 return xNewBinding;
416 void Model::removeBindingIfUseless( const XPropertySet_t& xBinding )
417 throw( RuntimeException )
419 Binding* pBinding = Binding::getBinding( xBinding );
420 if( pBinding != NULL )
422 if( ! pBinding->isUseful() )
423 mpBindings->removeItem( pBinding );
427 Model::XDocument_t Model::newInstance( const rtl::OUString& sName,
428 const rtl::OUString& sURL,
429 sal_Bool bURLOnce )
430 throw( RuntimeException )
432 // create a default instance with <instanceData> element
433 XDocument_t xInstance = getDocumentBuilder()->newDocument();
434 DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
436 Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
437 Reference<XNode>( xInstance->createElement( OUSTRING("instanceData") ),
438 UNO_QUERY_THROW ) );
440 Sequence<PropertyValue> aSequence;
441 bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
442 setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
443 sal_Int32 nInstance = mpInstances->addItem( aSequence );
444 loadInstance( nInstance );
446 return xInstance;
449 sal_Int32 lcl_findProp( const PropertyValue* pValues,
450 sal_Int32 nLength,
451 const rtl::OUString& rName )
453 bool bFound = false;
454 sal_Int32 n = 0;
455 for( ; !bFound && n < nLength; n++ )
457 bFound = ( pValues[n].Name == rName );
459 return bFound ? ( n - 1) : -1;
462 sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
463 const rtl::OUString& rName )
465 sal_Int32 nLength = pInstances->countItems();
466 sal_Int32 n = 0;
467 bool bFound = false;
468 for( ; !bFound && n < nLength; n++ )
470 OUString sName;
471 getInstanceData( pInstances->getItem( n ), &sName, NULL, NULL, NULL );
472 bFound = ( sName == rName );
474 return bFound ? ( n - 1 ) : -1;
477 void Model::renameInstance( const rtl::OUString& sFrom,
478 const rtl::OUString& sTo,
479 const rtl::OUString& sURL,
480 sal_Bool bURLOnce )
481 throw( RuntimeException )
483 sal_Int32 nPos = lcl_findInstance( mpInstances, sFrom );
484 if( nPos != -1 )
486 Sequence<PropertyValue> aSeq = mpInstances->getItem( nPos );
487 PropertyValue* pSeq = aSeq.getArray();
488 sal_Int32 nLength = aSeq.getLength();
490 sal_Int32 nProp = lcl_findProp( pSeq, nLength, OUSTRING("ID") );
491 if( nProp == -1 )
493 // add name property
494 aSeq.realloc( nLength + 1 );
495 pSeq = aSeq.getArray();
496 pSeq[ nLength ].Name = OUSTRING("ID");
497 nProp = nLength;
500 // change name
501 pSeq[ nProp ].Value <<= sTo;
503 // change url
504 nProp = lcl_findProp( pSeq, nLength, OUSTRING("URL") );
505 if(nProp != -1)
506 pSeq[ nProp ].Value <<= sURL;
508 // change urlonce
509 nProp = lcl_findProp( pSeq, nLength, OUSTRING("URLOnce") );
510 if(nProp != -1)
511 pSeq[ nProp ].Value <<= bURLOnce;
513 // set instance
514 mpInstances->setItem( nPos, aSeq );
518 void Model::removeInstance( const rtl::OUString& sName )
519 throw( RuntimeException )
521 sal_Int32 nPos = lcl_findInstance( mpInstances, sName );
522 if( nPos != -1 )
523 mpInstances->removeItem( mpInstances->getItem( nPos ) );
526 Reference<XNameContainer> lcl_getModels(
527 const Reference<com::sun::star::frame::XModel>& xComponent )
529 Reference<XNameContainer> xRet;
530 Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
531 if( xSupplier.is() )
533 xRet = xSupplier->getXForms();
535 return xRet;
538 Model::XModel_t Model::newModel( const Reference<com::sun::star::frame::XModel>& xCmp,
539 const OUString& sName )
540 throw( RuntimeException )
542 Model* pModel = NULL;
543 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
544 if( xModels.is()
545 && ! xModels->hasByName( sName ) )
547 pModel = new Model();
548 pModel->setID( sName );
549 pModel->newInstance( OUString(), OUString(), sal_False );
550 xModels->insertByName( sName, makeAny( Reference<XModel>( pModel ) ) );
553 return pModel;
556 void Model::renameModel( const Reference<com::sun::star::frame::XModel>& xCmp,
557 const OUString& sFrom,
558 const OUString& sTo )
559 throw( RuntimeException )
561 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
562 if( xModels.is()
563 && xModels->hasByName( sFrom )
564 && ! xModels->hasByName( sTo ) )
566 Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
567 xModel->setID( sTo );
568 xModels->insertByName( sTo, makeAny( xModel ) );
569 xModels->removeByName( sFrom );
573 void Model::removeModel( const Reference<com::sun::star::frame::XModel>& xCmp,
574 const OUString& sName )
575 throw( RuntimeException )
577 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
578 if( xModels.is()
579 && xModels->hasByName( sName ) )
581 xModels->removeByName( sName );
585 Model::XNode_t Model::createElement( const XNode_t& xParent,
586 const OUString& sName )
587 throw( RuntimeException )
589 Reference<XNode> xNode;
590 if( xParent.is()
591 && isValidXMLName( sName ) )
593 // TODO: implement proper namespace handling
594 xNode.set( xParent->getOwnerDocument()->createElement( sName ),
595 UNO_QUERY );
597 return xNode;
600 Model::XNode_t Model::createAttribute( const XNode_t& xParent,
601 const OUString& sName )
602 throw( RuntimeException )
604 Reference<XNode> xNode;
605 Reference<XElement> xElement( xParent, UNO_QUERY );
606 if( xParent.is()
607 && xElement.is()
608 && isValidXMLName( sName ) )
610 // handle case where attribute already exists
611 sal_Int32 nCount = 0;
612 OUString sUniqueName = sName;
613 while( xElement->hasAttribute( sUniqueName ) )
615 nCount++;
616 sUniqueName = sName + OUString::valueOf( nCount );
619 // TODO: implement proper namespace handling
620 xNode.set( xParent->getOwnerDocument()->createAttribute( sUniqueName ),
621 UNO_QUERY );
623 return xNode;
626 Model::XNode_t Model::renameNode( const XNode_t& xNode,
627 const rtl::OUString& sName )
628 throw( RuntimeException )
630 // early out if we don't have to change the name
631 if( xNode->getNodeName() == sName )
632 return xNode;
634 // refuse to change name if its an attribute, and the name is already used
635 if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE
636 && xNode->getParentNode().is()
637 && Reference<XElement>(xNode->getParentNode(), UNO_QUERY_THROW)->hasAttribute( sName ) )
638 return xNode;
640 // note old binding expression so we can adjust bindings below
641 OUString sOldDefaultBindingExpression =
642 getDefaultBindingExpressionForNode( xNode );
644 Reference<XDocument> xDoc = xNode->getOwnerDocument();
645 Reference<XNode> xNew;
646 if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
648 Reference<XElement> xElem = xDoc->createElement( sName );
649 xNew.set( xElem, UNO_QUERY );
651 // iterate over all attributes and append them to the new element
652 Reference<XElement> xOldElem( xNode, UNO_QUERY );
653 OSL_ENSURE( xNode.is(), "no element?" );
655 Reference<XNamedNodeMap> xMap = xNode->getAttributes();
656 sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
657 for( sal_Int32 n = 0; n < nLength; n++ )
659 Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
660 xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
663 // iterate over all children and append them to the new element
664 for( Reference<XNode> xCurrent = xNode->getFirstChild();
665 xCurrent.is();
666 xCurrent = xNode->getFirstChild() )
668 xNew->appendChild( xNode->removeChild( xCurrent ) );
671 xNode->getParentNode()->replaceChild( xNew, xNode );
673 else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
675 // create new attribute
676 Reference<XAttr> xAttr = xDoc->createAttribute( sName );
677 xAttr->setValue( xNode->getNodeValue() );
679 // replace node
680 Reference<XNode> xParent = xNode->getParentNode();
681 xParent->removeChild( xNode );
682 xNew = xParent->appendChild( Reference<XNode>( xAttr, UNO_QUERY ) );
684 else
686 OSL_ENSURE( false, "can't rename this node type" );
689 // adjust bindings (if necessary):
690 if( xNew.is() )
692 // iterate over bindings and replace default expressions
693 OUString sNewDefaultBindingExpression =
694 getDefaultBindingExpressionForNode( xNew );
695 for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
697 Binding* pBinding = Binding::getBinding(
698 mpBindings->Collection<XPropertySet_t>::getItem( n ) );
700 if( pBinding->getBindingExpression()
701 == sOldDefaultBindingExpression )
702 pBinding->setBindingExpression( sNewDefaultBindingExpression );
706 // return node; return old node if renaming failed
707 return xNew.is() ? xNew : xNode;
710 Model::XPropertySet_t Model::getBindingForNode( const XNode_t& xNode,
711 sal_Bool bCreate )
712 throw( RuntimeException )
714 OSL_ENSURE( xNode.is(), "no node?" );
716 // We will iterate over all bindings and determine the
717 // appropriateness of the respective binding for this node. The
718 // best one will be used. If we don't find any and bCreate is set,
719 // then we will create a suitable binding.
720 Binding* pBestBinding = NULL;
721 sal_Int32 nBestScore = 0;
723 for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
725 Binding* pBinding = Binding::getBinding(
726 mpBindings->Collection<XPropertySet_t>::getItem( n ) );
728 OSL_ENSURE( pBinding != NULL, "no binding?" );
729 Reference<XNodeList> xNodeList = pBinding->getXNodeList();
731 sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
732 if( nNodes > 0 && xNodeList->item( 0 ) == xNode )
734 // allright, we found a suitable node. Let's determine how
735 // well it fits. Score:
736 // - bind to exactly this node is better than whole nodeset
737 // - simple binding expressions is better than complex ones
738 sal_Int32 nScore = 0;
739 if( nNodes == 1 )
740 nScore ++;
741 if( pBinding->isSimpleBindingExpression() )
742 nScore ++;
744 // if we found a better binding, remember it
745 if( nScore > nBestScore )
747 pBestBinding = pBinding;
748 nBestScore = nScore;
753 // create binding, if none was found and bCreate is set
754 OSL_ENSURE( ( nBestScore == 0 ) == ( pBestBinding == NULL ),
755 "score != binding?" );
756 if( bCreate && pBestBinding == NULL )
758 pBestBinding = new Binding();
759 pBestBinding->setBindingExpression(
760 getDefaultBindingExpressionForNode( xNode ) );
761 mpBindings->addItem( pBestBinding );
764 return pBestBinding;
767 void Model::removeBindingForNode( const XNode_t& )
768 throw( RuntimeException )
770 // determine whether suitable binding is still used
773 OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
775 ::rtl::OUString sResult;
776 OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
777 if ( _rxAttrNode.is() )
779 ::rtl::OUStringBuffer aBuffer;
780 aBuffer.append( _rxAttrNode->getName() );
781 aBuffer.appendAscii( "=" );
782 ::rtl::OUString sValue = _rxAttrNode->getValue();
783 sal_Unicode nQuote = '"';
784 if ( sValue.indexOf( nQuote ) >= 0 )
785 nQuote = '\'';
786 aBuffer.append( nQuote );
787 aBuffer.append( sValue );
788 aBuffer.append( nQuote );
789 aBuffer.append( (sal_Unicode)' ' );
790 sResult = aBuffer.makeStringAndClear();
792 return sResult;
795 OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
797 ::rtl::OUString sResult;
799 // create document fragment
800 Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
801 Reference<XDocumentFragment> xFragment(
802 xDocument->createDocumentFragment() );
803 Reference<XNode> xNode( xFragment, UNO_QUERY );
804 OSL_ENSURE( xFragment.is(), "xFragment" );
805 OSL_ENSURE( xNode.is(), "xNode" );
807 sal_Int32 nAttributeNodes = 0;
809 // attach nodelist to fragment
810 sal_Int32 nLength = xNodes->getLength();
811 for( sal_Int32 i = 0; i < nLength; i++ )
813 Reference<XNode> xCurrent = xNodes->item( i );
815 switch ( xCurrent->getNodeType() )
817 case NodeType_DOCUMENT_NODE:
818 // special-case documents: use top-level element instead
819 xCurrent = xCurrent->getFirstChild();
820 break;
821 case NodeType_ATTRIBUTE_NODE:
823 Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
824 if ( xAttr.is() )
826 sResult += lcl_serializeForDisplay( xAttr );
827 ++nAttributeNodes;
830 continue;
832 default:
833 break;
836 // append node
837 xNode->appendChild( xDocument->importNode( xCurrent, sal_True ) );
839 OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
840 "lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
841 if ( nAttributeNodes )
842 // had only attribute nodes
843 return sResult;
845 // serialize fragment
846 CSerializationAppXML aSerialization;
847 aSerialization.setSource( xFragment );
848 aSerialization.serialize();
850 // copy stream into buffer
851 Reference<XTextInputStream> xTextInputStream(
852 createInstance( OUSTRING("com.sun.star.io.TextInputStream") ),
853 UNO_QUERY );
854 Reference<XActiveDataSink>( xTextInputStream, UNO_QUERY_THROW )
855 ->setInputStream( aSerialization.getInputStream() );
857 /* WORK AROUND for problem in serialization: currently, multiple
858 XML delarations (<?xml...?>) are being written out and we don't
859 want them. When this is fixed, the code below is nice and
860 simple. The current code filters out the declarations.
861 OUString sResult = xTextInputStream->readString( Sequence<sal_Unicode>(),
862 sal_True );
865 // well, the serialization prepends XML header(s) that we need to
866 // remove first.
867 OUStringBuffer aBuffer;
868 while( ! xTextInputStream->isEOF() )
870 OUString sLine = xTextInputStream->readLine();
871 if( sLine.getLength() > 0
872 && sLine.compareToAscii( "<?xml", 5 ) != 0 )
874 aBuffer.append( sLine );
875 aBuffer.append( sal_Unicode('\n') );
878 sResult = aBuffer.makeStringAndClear();
880 return sResult;
883 OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
885 // error handling first
886 if( ! xResult.is() )
887 return getResource( RID_STR_XFORMS_CANT_EVALUATE );
890 // TODO: localize
891 OUStringBuffer aBuffer;
893 switch( xResult->getObjectType() )
895 case XPathObjectType_XPATH_BOOLEAN:
896 aBuffer.append( xResult->getBoolean()
897 ? OUSTRING("true")
898 : OUSTRING("false") );
899 break;
901 case XPathObjectType_XPATH_STRING:
902 aBuffer.append( sal_Unicode('"') );
903 aBuffer.append( xResult->getString() );
904 aBuffer.append( sal_Unicode('"') );
905 break;
907 case XPathObjectType_XPATH_NODESET:
908 aBuffer.append( lcl_serializeForDisplay( xResult->getNodeList() ) );
909 break;
911 case XPathObjectType_XPATH_NUMBER:
912 aBuffer.append( xResult->getDouble() );
913 break;
915 case XPathObjectType_XPATH_UNDEFINED:
916 case XPathObjectType_XPATH_POINT:
917 case XPathObjectType_XPATH_RANGE:
918 case XPathObjectType_XPATH_LOCATIONSET:
919 case XPathObjectType_XPATH_USERS:
920 case XPathObjectType_XPATH_XSLT_TREE:
921 default:
922 // TODO: localized error message?
923 break;
926 return aBuffer.makeStringAndClear();
929 OUString Model::getResultForExpression(
930 const XPropertySet_t& xBinding,
931 sal_Bool bIsBindingExpression,
932 const OUString& sExpression )
933 throw( RuntimeException )
935 Binding* pBinding = Binding::getBinding( xBinding );
936 if( pBinding == NULL )
937 throw RuntimeException();
939 // prepare & evaluate expression
940 OUStringBuffer aBuffer;
941 ComputedExpression aExpression;
942 aExpression.setExpression( sExpression );
943 if( bIsBindingExpression )
945 // binding: use binding context and evaluation
946 aExpression.evaluate( pBinding->getEvaluationContext() );
947 aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
949 else
951 // MIP (not binding): iterate over bindings contexts
952 std::vector<EvaluationContext> aContext =
953 pBinding->getMIPEvaluationContexts();
954 for( std::vector<EvaluationContext>::iterator aIter = aContext.begin();
955 aIter != aContext.end();
956 aIter ++ )
958 aExpression.evaluate( *aIter );
959 aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
960 aBuffer.append( sal_Unicode('\n') );
963 return aBuffer.makeStringAndClear();
966 sal_Bool Model::isValidXMLName( const OUString& sName )
967 throw( RuntimeException )
969 return isValidQName( sName, NULL );
972 sal_Bool Model::isValidPrefixName( const OUString& sName )
973 throw( RuntimeException )
975 return ::isValidPrefixName( sName, NULL );
978 void Model::setNodeValue(
979 const XNode_t& xNode,
980 const rtl::OUString& sValue )
981 throw( RuntimeException )
983 setSimpleContent( xNode, sValue );
988 // helper functions from model_helper.hxx
991 void xforms::getInstanceData(
992 const Sequence<PropertyValue>& aValues,
993 OUString* pID,
994 Reference<XDocument>* pInstance,
995 OUString* pURL,
996 bool* pURLOnce )
998 sal_Int32 nValues = aValues.getLength();
999 const PropertyValue* pValues = aValues.getConstArray();
1000 for( sal_Int32 n = 0; n < nValues; n++ )
1002 const PropertyValue& rValue = pValues[n];
1003 #define PROP(NAME) \
1004 if( p##NAME != NULL && \
1005 rValue.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(#NAME)) ) \
1006 rValue.Value >>= (*p##NAME)
1007 PROP(ID);
1008 PROP(Instance);
1009 PROP(URL);
1010 PROP(URLOnce);
1011 #undef PROP
1015 void xforms::setInstanceData(
1016 Sequence<PropertyValue>& aSequence,
1017 const OUString* _pID,
1018 const Reference<XDocument>* _pInstance,
1019 const OUString* _pURL,
1020 const bool* _pURLOnce )
1022 // get old instance data
1023 OUString sID;
1024 Reference<XDocument> xInstance;
1025 OUString sURL;
1026 bool bURLOnce = false;
1027 getInstanceData( aSequence, &sID, &xInstance, &sURL, &bURLOnce );
1028 const OUString* pID = ( sID.getLength() > 0 ) ? &sID : NULL;
1029 const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : NULL;
1030 const OUString* pURL = ( sURL.getLength() > 0 ) ? &sURL : NULL;
1031 const bool* pURLOnce = ( bURLOnce && pURL != NULL ) ? &bURLOnce : NULL;
1033 // determine new instance data
1034 #define PROP(NAME) if( _p##NAME != NULL ) p##NAME = _p##NAME
1035 PROP(ID);
1036 PROP(Instance);
1037 PROP(URL);
1038 PROP(URLOnce);
1039 #undef PROP
1041 // count # of values we want to set
1042 sal_Int32 nCount = 0;
1043 #define PROP(NAME) if( p##NAME != NULL ) nCount++
1044 PROP(ID);
1045 PROP(Instance);
1046 PROP(URL);
1047 PROP(URLOnce);
1048 #undef PROP
1050 // realloc sequence and enter values;
1051 aSequence.realloc( nCount );
1052 PropertyValue* pSequence = aSequence.getArray();
1053 sal_Int32 nIndex = 0;
1054 #define PROP(NAME) \
1055 if( p##NAME != NULL ) \
1057 pSequence[ nIndex ].Name = OUSTRING(#NAME); \
1058 pSequence[ nIndex ].Value <<= *p##NAME; \
1059 nIndex++; \
1061 PROP(ID);
1062 PROP(Instance);
1063 PROP(URL);
1064 PROP(URLOnce);
1065 #undef PROP