xref: /aoo42x/main/forms/source/xforms/model_ui.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_forms.hxx"
30 
31 #include "model.hxx"
32 #include "model_helper.hxx"
33 #include "mip.hxx"
34 #include "evaluationcontext.hxx"
35 #include "unohelper.hxx"
36 #include "submission/serialization_app_xml.hxx"
37 #include "resourcehelper.hxx"
38 #include "xmlhelper.hxx"
39 #include "convert.hxx"
40 
41 #include <rtl/ustring.hxx>
42 #include <rtl/ustrbuf.hxx>
43 #include <tools/debug.hxx>
44 
45 // UNO classes
46 #include <com/sun/star/xml/dom/XNode.hpp>
47 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
48 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
49 #include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
50 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
51 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/io/XInputStream.hpp>
54 #include <com/sun/star/io/XActiveDataSink.hpp>
55 #include <com/sun/star/io/XTextInputStream.hpp>
56 #include <com/sun/star/container/XEnumeration.hpp>
57 #include <com/sun/star/container/XNameContainer.hpp>
58 #include <com/sun/star/frame/XModel.hpp>
59 #include <com/sun/star/xforms/XFormsSupplier.hpp>
60 #include <com/sun/star/xforms/XDataTypeRepository.hpp>
61 #include <com/sun/star/xsd/XDataType.hpp>
62 #include <com/sun/star/xsd/DataTypeClass.hpp>
63 
64 
65 using rtl::OUString;
66 using rtl::OUStringBuffer;
67 using com::sun::star::beans::PropertyValue;
68 using com::sun::star::io::XInputStream;
69 using com::sun::star::io::XActiveDataSink;
70 using com::sun::star::io::XTextInputStream;
71 using com::sun::star::container::XEnumeration;
72 using com::sun::star::container::XNameContainer;
73 using com::sun::star::xforms::XFormsSupplier;
74 
75 using namespace xforms;
76 using namespace com::sun::star::uno;
77 using namespace com::sun::star::xml::dom;
78 using namespace com::sun::star::xml::xpath;
79 
80 
81 
82 //
83 // implement XFormsUIHelper1
84 //
85 
86 OUString Model::getDefaultServiceNameForNode( const XNode_t& xNode )
87     throw( RuntimeException )
88 {
89     // determine service for control. string/text field is default.
90     OUString sService = OUSTRING("com.sun.star.form.component.TextField");
91 
92     // query repository for suitable type
93     OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
94     OUString sTypeName = queryMIP( xNode ).getTypeName();
95     if( mxDataTypes->hasByName( sTypeName ) )
96     {
97         OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
98                     "has or has not?" );
99 
100         switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
101         {
102         case com::sun::star::xsd::DataTypeClass::BOOLEAN:
103             sService = OUSTRING("com.sun.star.form.component.CheckBox");
104             break;
105         case com::sun::star::xsd::DataTypeClass::DOUBLE:
106         case com::sun::star::xsd::DataTypeClass::DECIMAL:
107         case com::sun::star::xsd::DataTypeClass::FLOAT:
108             sService = OUSTRING("com.sun.star.form.component.NumericField");
109             break;
110 
111         case com::sun::star::xsd::DataTypeClass::STRING:
112         case com::sun::star::xsd::DataTypeClass::DURATION:
113         case com::sun::star::xsd::DataTypeClass::DATETIME:
114         case com::sun::star::xsd::DataTypeClass::TIME:
115         case com::sun::star::xsd::DataTypeClass::DATE:
116         case com::sun::star::xsd::DataTypeClass::gYearMonth:
117         case com::sun::star::xsd::DataTypeClass::gYear:
118         case com::sun::star::xsd::DataTypeClass::gMonthDay:
119         case com::sun::star::xsd::DataTypeClass::gDay:
120         case com::sun::star::xsd::DataTypeClass::gMonth:
121         case com::sun::star::xsd::DataTypeClass::hexBinary:
122         case com::sun::star::xsd::DataTypeClass::base64Binary:
123         case com::sun::star::xsd::DataTypeClass::anyURI:
124         case com::sun::star::xsd::DataTypeClass::QName:
125         case com::sun::star::xsd::DataTypeClass::NOTATION:
126         default:
127             // keep default
128             break;
129         }
130     }
131 
132     return sService;
133 }
134 
135 
136 void lcl_OutPosition( OUStringBuffer& rBuffer,
137                       const Reference<XNode>& xNode )
138 {
139     OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
140 
141     // count # of occurences of this node
142     sal_Int32 nFound = 0;
143     sal_Int32 nPosition = -1;
144     if( xNode->getParentNode().is() )
145     {
146         for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
147              xIter != NULL;
148              xIter = xIter->getNextSibling() )
149         {
150             if( xIter->getNodeType() == xNode->getNodeType() &&
151                 xIter->getNodeName() == xNode->getNodeName() &&
152                 xIter->getNamespaceURI() == xNode->getNamespaceURI() )
153             {
154                 nFound++;
155                 if( xIter == xNode )
156                     nPosition = nFound;
157             }
158         }
159     }
160     OSL_ENSURE( nFound > 0  &&  nPosition > 0, "node not found???" );
161 
162     // output position (if necessary)
163     if( nFound > 1 )
164     {
165         rBuffer.insert( 0, sal_Unicode(']') );
166         rBuffer.insert( 0, nPosition );
167         rBuffer.insert( 0, sal_Unicode('[') );
168     }
169 }
170 
171 void lcl_OutName( OUStringBuffer& rBuffer,
172                   const Reference<XNode>& xNode )
173 {
174     rBuffer.insert( 0, xNode->getNodeName() );
175     OUString sPrefix = xNode->getPrefix();
176     if( sPrefix.getLength() > 0 )
177     {
178         rBuffer.insert( 0, sal_Unicode(':') );
179         rBuffer.insert( 0, sPrefix );
180     }
181 }
182 
183 void lcl_OutInstance( OUStringBuffer& rBuffer,
184                       const Reference<XNode>& xNode,
185                       Model* pModel )
186 {
187     Reference<XDocument> xDoc = xNode->getOwnerDocument();
188 
189     if( xDoc != pModel->getDefaultInstance() )
190     {
191         rBuffer.insert( 0, OUSTRING("')") );
192 
193         // iterate over instances, and find the right one
194         OUString sInstanceName;
195         Reference<XEnumeration> xEnum =
196             pModel->getInstances()->createEnumeration();
197         while( ( sInstanceName.getLength() == 0 ) && xEnum->hasMoreElements() )
198         {
199             Sequence<PropertyValue> aValues;
200             xEnum->nextElement() >>= aValues;
201 
202             // get ID and instance
203             OUString sId;
204             Reference<XDocument> xInstance;
205             getInstanceData( aValues, &sId, &xInstance, NULL, NULL );
206 
207             // now check whether this was our instance:
208             if( xInstance == xDoc )
209                 sInstanceName = sId;
210         }
211 
212         rBuffer.insert( 0, sInstanceName );
213         rBuffer.insert( 0, OUSTRING("instance('") );
214     }
215 }
216 
217 OUString Model::getDefaultBindingExpressionForNode(
218     const XNode_t& xNode,
219     const EvaluationContext& rContext)
220 {
221     OSL_ENSURE( xNode.is(), "need node" );
222 
223     // iterate upwards and put sections into the expression buffer.
224     // Stop iteration either at context node (relative expression) or
225     // at document root, whichever occurs first.
226     OUStringBuffer aBuffer;
227     for( Reference<XNode> xCurrent = xNode;
228          xCurrent.is()  &&  xCurrent != rContext.mxContextNode;
229          xCurrent = xCurrent->getParentNode() )
230     {
231         // insert a '/' for every step except the first
232         if( aBuffer.getLength() > 0 )
233             aBuffer.insert( 0, sal_Unicode('/') );
234 
235         switch( xCurrent->getNodeType() )
236         {
237         case NodeType_ELEMENT_NODE:
238             lcl_OutPosition( aBuffer, xCurrent );
239             lcl_OutName( aBuffer, xCurrent );
240             break;
241 
242         case NodeType_TEXT_NODE:
243             lcl_OutPosition( aBuffer, xCurrent );
244             aBuffer.insert( 0, OUSTRING("text()") );
245             break;
246 
247         case NodeType_ATTRIBUTE_NODE:
248             lcl_OutName( aBuffer, xCurrent );
249             aBuffer.insert( 0, sal_Unicode('@') );
250             break;
251 
252         case NodeType_DOCUMENT_NODE:
253             // check for which instance we have
254             lcl_OutInstance( aBuffer, xCurrent, this );
255             break;
256 
257         default:
258             // unknown type? fail!
259             OSL_ENSURE( false, "unknown node type!" );
260             xCurrent.set( NULL );
261             aBuffer.makeStringAndClear();
262             // we'll remove the slash below
263             aBuffer.insert( 0, sal_Unicode('/') );
264             break;
265         }
266     }
267 
268     return aBuffer.makeStringAndClear();
269 }
270 
271 
272 
273 OUString Model::getDefaultBindingExpressionForNode( const XNode_t& xNode )
274     throw( RuntimeException )
275 {
276     return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
277 }
278 
279 bool lcl_isWhitespace( const OUString& rString )
280 {
281     sal_Int32 nLength = rString.getLength();
282     const sal_Unicode* pStr = rString.getStr();
283 
284     bool bWhitespace = true;
285     for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
286     {
287         sal_Unicode c = pStr[i];
288         bWhitespace = ( c == sal_Unicode(0x09) ||
289                         c == sal_Unicode(0x0A) ||
290                         c == sal_Unicode(0x0D) ||
291                         c == sal_Unicode(0x20) );
292     }
293     return bWhitespace;
294 }
295 
296 OUString Model::getNodeDisplayName( const XNode_t& xNode,
297                                     sal_Bool bDetail )
298     throw( RuntimeException )
299 {
300     OUStringBuffer aBuffer;
301 
302     switch( xNode->getNodeType() )
303     {
304     case NodeType_ELEMENT_NODE:
305         lcl_OutName( aBuffer, xNode );
306         break;
307 
308     case NodeType_TEXT_NODE:
309         {
310             OUString sContent = xNode->getNodeValue();
311             if( bDetail || ! lcl_isWhitespace( sContent ) )
312             {
313                 aBuffer.append( sal_Unicode('"') );
314                 aBuffer.append( Convert::collapseWhitespace( sContent ) );
315                 aBuffer.append( sal_Unicode('"') );
316             }
317         }
318         break;
319 
320     case NodeType_ATTRIBUTE_NODE:
321         lcl_OutName( aBuffer, xNode );
322         aBuffer.insert( 0, sal_Unicode('@') );
323         break;
324 
325     case NodeType_DOCUMENT_NODE:
326         if( xNode == getDefaultInstance() )
327             aBuffer.append( sal_Unicode('/') );
328         else
329             lcl_OutInstance( aBuffer, xNode, this );
330         break;
331 
332     default:
333         // unknown type? fail!
334         OSL_ENSURE( false, "unknown node type!" );
335         break;
336     }
337 
338     return aBuffer.makeStringAndClear();
339 }
340 
341 OUString Model::getNodeName( const XNode_t& xNode )
342     throw( RuntimeException )
343 {
344     OUStringBuffer aBuffer;
345 
346     switch( xNode->getNodeType() )
347     {
348     case NodeType_ELEMENT_NODE:
349     case NodeType_ATTRIBUTE_NODE:
350         lcl_OutName( aBuffer, xNode );
351         break;
352 
353     case NodeType_TEXT_NODE:
354     case NodeType_DOCUMENT_NODE:
355     default:
356         // unknown type? fail!
357         OSL_ENSURE( false, "no name for this node type!" );
358         break;
359     }
360 
361     return aBuffer.makeStringAndClear();
362 }
363 
364 OUString Model::getBindingName( const XPropertySet_t& xBinding,
365                                 sal_Bool /*bDetail*/ )
366     throw( RuntimeException )
367 {
368     OUString sID;
369     xBinding->getPropertyValue( OUSTRING("BindingID" ) ) >>= sID;
370     OUString sExpression;
371     xBinding->getPropertyValue( OUSTRING("BindingExpression" ) ) >>= sExpression;
372 
373     OUStringBuffer aBuffer;
374     if( sID.getLength() > 0 )
375     {
376         aBuffer.append( sID );
377         aBuffer.append( OUSTRING(" (" ));
378         aBuffer.append( sExpression );
379         aBuffer.append( OUSTRING(")" ));
380     }
381     else
382         aBuffer.append( sExpression );
383 
384     return aBuffer.makeStringAndClear();
385 }
386 
387 OUString Model::getSubmissionName( const XPropertySet_t& xSubmission,
388                                    sal_Bool /*bDetail*/ )
389     throw( RuntimeException )
390 {
391     OUString sID;
392     xSubmission->getPropertyValue( OUSTRING("ID") ) >>= sID;
393     return sID;
394 }
395 
396 Model::XPropertySet_t Model::cloneBindingAsGhost( const XPropertySet_t &xBinding )
397 	throw( RuntimeException )
398 {
399 	// Create a new binding instance first...
400 	Binding *pBinding = new Binding();
401 
402 	// ...and bump up the "defered notification counter"
403 	// to prevent this binding from contributing to the
404 	// MIPs table...
405 	pBinding->deferNotifications(true);
406 
407 	// Copy the propertyset and return result...
408     XPropertySet_t xNewBinding(pBinding);
409     copy( xBinding, xNewBinding );
410     return xNewBinding;
411 }
412 
413 void Model::removeBindingIfUseless( const XPropertySet_t& xBinding )
414     throw( RuntimeException )
415 {
416     Binding* pBinding = Binding::getBinding( xBinding );
417     if( pBinding != NULL )
418     {
419         if( ! pBinding->isUseful() )
420             mpBindings->removeItem( pBinding );
421     }
422 }
423 
424 Model::XDocument_t Model::newInstance( const rtl::OUString& sName,
425                          const rtl::OUString& sURL,
426                          sal_Bool bURLOnce )
427     throw( RuntimeException )
428 {
429     // create a default instance with <instanceData> element
430     XDocument_t xInstance = getDocumentBuilder()->newDocument();
431     DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
432 
433     Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
434         Reference<XNode>( xInstance->createElement( OUSTRING("instanceData") ),
435                           UNO_QUERY_THROW ) );
436 
437     Sequence<PropertyValue> aSequence;
438     bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
439     setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
440     sal_Int32 nInstance = mpInstances->addItem( aSequence );
441     loadInstance( nInstance );
442 
443     return xInstance;
444 }
445 
446 sal_Int32 lcl_findProp( const PropertyValue* pValues,
447                         sal_Int32 nLength,
448                         const rtl::OUString& rName )
449 {
450     bool bFound = false;
451     sal_Int32 n = 0;
452     for( ; !bFound && n < nLength; n++ )
453     {
454         bFound = ( pValues[n].Name == rName );
455     }
456     return bFound ? ( n - 1) : -1;
457 }
458 
459 sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
460                                     const rtl::OUString& rName )
461 {
462     sal_Int32 nLength = pInstances->countItems();
463     sal_Int32 n = 0;
464     bool bFound = false;
465     for( ; !bFound  &&  n < nLength; n++ )
466     {
467         OUString sName;
468         getInstanceData( pInstances->getItem( n ), &sName, NULL, NULL, NULL );
469         bFound = ( sName == rName );
470     }
471     return bFound ? ( n - 1 ) : -1;
472 }
473 
474 void Model::renameInstance( const rtl::OUString& sFrom,
475                             const rtl::OUString& sTo,
476                             const rtl::OUString& sURL,
477                             sal_Bool bURLOnce )
478     throw( RuntimeException )
479 {
480     sal_Int32 nPos = lcl_findInstance( mpInstances, sFrom );
481     if( nPos != -1 )
482     {
483         Sequence<PropertyValue> aSeq = mpInstances->getItem( nPos );
484         PropertyValue* pSeq = aSeq.getArray();
485         sal_Int32 nLength = aSeq.getLength();
486 
487         sal_Int32 nProp = lcl_findProp( pSeq, nLength, OUSTRING("ID") );
488         if( nProp == -1 )
489         {
490             // add name property
491             aSeq.realloc( nLength + 1 );
492             pSeq = aSeq.getArray();
493             pSeq[ nLength ].Name = OUSTRING("ID");
494             nProp = nLength;
495         }
496 
497         // change name
498         pSeq[ nProp ].Value <<= sTo;
499 
500 		// change url
501         nProp = lcl_findProp( pSeq, nLength, OUSTRING("URL") );
502 		if(nProp != -1)
503 	        pSeq[ nProp ].Value <<= sURL;
504 
505 		// change urlonce
506         nProp = lcl_findProp( pSeq, nLength, OUSTRING("URLOnce") );
507 		if(nProp != -1)
508 	        pSeq[ nProp ].Value <<= bURLOnce;
509 
510         // set instance
511         mpInstances->setItem( nPos, aSeq );
512     }
513 }
514 
515 void Model::removeInstance( const rtl::OUString& sName )
516     throw( RuntimeException )
517 {
518     sal_Int32 nPos = lcl_findInstance( mpInstances, sName );
519     if( nPos != -1 )
520         mpInstances->removeItem( mpInstances->getItem( nPos ) );
521 }
522 
523 Reference<XNameContainer> lcl_getModels(
524     const Reference<com::sun::star::frame::XModel>& xComponent )
525 {
526     Reference<XNameContainer> xRet;
527     Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
528     if( xSupplier.is() )
529     {
530         xRet = xSupplier->getXForms();
531     }
532     return xRet;
533 }
534 
535 Model::XModel_t Model::newModel( const Reference<com::sun::star::frame::XModel>& xCmp,
536                                  const OUString& sName )
537     throw( RuntimeException )
538 {
539     Model::XModel_t xModel;
540     Reference<XNameContainer> xModels = lcl_getModels( xCmp );
541     if( xModels.is()
542         && ! xModels->hasByName( sName ) )
543     {
544         Model* pModel = new Model();
545         xModel.set( pModel );
546 
547         pModel->setID( sName );
548         pModel->newInstance( OUString(), OUString(), sal_False );
549         pModel->initialize();
550         xModels->insertByName( sName, makeAny( xModel ) );
551     }
552 
553     return xModel;
554 }
555 
556 void Model::renameModel( const Reference<com::sun::star::frame::XModel>& xCmp,
557                          const OUString& sFrom,
558                          const OUString& sTo )
559     throw( RuntimeException )
560 {
561     Reference<XNameContainer> xModels = lcl_getModels( xCmp );
562     if( xModels.is()
563         && xModels->hasByName( sFrom )
564         && ! xModels->hasByName( sTo ) )
565     {
566         Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
567         xModel->setID( sTo );
568         xModels->insertByName( sTo, makeAny( xModel ) );
569         xModels->removeByName( sFrom );
570     }
571 }
572 
573 void Model::removeModel( const Reference<com::sun::star::frame::XModel>& xCmp,
574                          const OUString& sName )
575     throw( RuntimeException )
576 {
577     Reference<XNameContainer> xModels = lcl_getModels( xCmp );
578     if( xModels.is()
579         && xModels->hasByName( sName ) )
580     {
581         xModels->removeByName( sName );
582     }
583 }
584 
585 Model::XNode_t Model::createElement( const XNode_t& xParent,
586                                      const OUString& sName )
587     throw( RuntimeException )
588 {
589     Reference<XNode> xNode;
590     if( xParent.is()
591         && isValidXMLName( sName ) )
592     {
593         // TODO: implement proper namespace handling
594         xNode.set( xParent->getOwnerDocument()->createElement( sName ),
595                    UNO_QUERY );
596     }
597     return xNode;
598 }
599 
600 Model::XNode_t Model::createAttribute( const XNode_t& xParent,
601                                        const OUString& sName )
602     throw( RuntimeException )
603 {
604     Reference<XNode> xNode;
605     Reference<XElement> xElement( xParent, UNO_QUERY );
606     if( xParent.is()
607         && xElement.is()
608         && isValidXMLName( sName ) )
609     {
610         // handle case where attribute already exists
611         sal_Int32 nCount = 0;
612         OUString sUniqueName = sName;
613         while( xElement->hasAttribute( sUniqueName ) )
614         {
615             nCount++;
616             sUniqueName = sName + OUString::valueOf( nCount );
617         }
618 
619         // TODO: implement proper namespace handling
620         xNode.set( xParent->getOwnerDocument()->createAttribute( sUniqueName ),
621                    UNO_QUERY );
622     }
623     return xNode;
624 }
625 
626 Model::XNode_t Model::renameNode( const XNode_t& xNode,
627                                   const rtl::OUString& sName )
628     throw( RuntimeException )
629 {
630     // early out if we don't have to change the name
631     if( xNode->getNodeName() == sName )
632         return xNode;
633 
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;
639 
640     // note old binding expression so we can adjust bindings below
641     OUString sOldDefaultBindingExpression =
642         getDefaultBindingExpressionForNode( xNode );
643 
644     Reference<XDocument> xDoc = xNode->getOwnerDocument();
645     Reference<XNode> xNew;
646     if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
647     {
648         Reference<XElement> xElem = xDoc->createElement( sName );
649         xNew.set( xElem, UNO_QUERY );
650 
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?" );
654 
655         Reference<XNamedNodeMap> xMap = xNode->getAttributes();
656         sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
657         for( sal_Int32 n = 0; n < nLength; n++ )
658         {
659             Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
660             xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
661         }
662 
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() )
667         {
668             xNew->appendChild( xNode->removeChild( xCurrent ) );
669         }
670 
671         xNode->getParentNode()->replaceChild( xNew, xNode );
672     }
673     else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
674     {
675         // create new attribute
676         Reference<XAttr> xAttr = xDoc->createAttribute( sName );
677         xAttr->setValue( xNode->getNodeValue() );
678 
679         // replace node
680         Reference<XNode> xParent = xNode->getParentNode();
681         xParent->removeChild( xNode );
682         xNew = xParent->appendChild( Reference<XNode>( xAttr, UNO_QUERY ) );
683     }
684     else
685     {
686         OSL_ENSURE( false, "can't rename this node type" );
687     }
688 
689     // adjust bindings (if necessary):
690     if( xNew.is() )
691     {
692         // iterate over bindings and replace default expressions
693         OUString sNewDefaultBindingExpression =
694             getDefaultBindingExpressionForNode( xNew );
695         for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
696         {
697             Binding* pBinding = Binding::getBinding(
698                 mpBindings->Collection<XPropertySet_t>::getItem( n ) );
699 
700             if( pBinding->getBindingExpression()
701                     == sOldDefaultBindingExpression )
702                 pBinding->setBindingExpression( sNewDefaultBindingExpression );
703         }
704     }
705 
706     // return node; return old node if renaming failed
707     return xNew.is() ? xNew : xNode;
708 }
709 
710 Model::XPropertySet_t Model::getBindingForNode( const XNode_t& xNode,
711                                                 sal_Bool bCreate )
712     throw( RuntimeException )
713 {
714     OSL_ENSURE( xNode.is(), "no node?" );
715 
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;
722 
723     for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
724     {
725         Binding* pBinding = Binding::getBinding(
726             mpBindings->Collection<XPropertySet_t>::getItem( n ) );
727 
728         OSL_ENSURE( pBinding != NULL, "no binding?" );
729         Reference<XNodeList> xNodeList = pBinding->getXNodeList();
730 
731         sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
732         if( nNodes > 0  &&  xNodeList->item( 0 ) == xNode )
733         {
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 ++;
743 
744             // if we found a better binding, remember it
745             if( nScore > nBestScore )
746             {
747                 pBestBinding = pBinding;
748                 nBestScore = nScore;
749             }
750         }
751     }
752 
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 )
757     {
758         pBestBinding = new Binding();
759         pBestBinding->setBindingExpression(
760             getDefaultBindingExpressionForNode( xNode ) );
761         mpBindings->addItem( pBestBinding );
762     }
763 
764     return pBestBinding;
765 }
766 
767 void Model::removeBindingForNode( const XNode_t& )
768     throw( RuntimeException )
769 {
770     // determine whether suitable binding is still used
771 }
772 
773 OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
774 {
775     ::rtl::OUString sResult;
776     OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
777     if ( _rxAttrNode.is() )
778     {
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();
791     }
792     return sResult;
793 }
794 
795 OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
796 {
797     ::rtl::OUString sResult;
798 
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" );
806 
807     sal_Int32 nAttributeNodes = 0;
808 
809     // attach nodelist to fragment
810     sal_Int32 nLength = xNodes->getLength();
811     for( sal_Int32 i = 0; i < nLength; i++ )
812     {
813         Reference<XNode> xCurrent = xNodes->item( i );
814 
815         switch ( xCurrent->getNodeType() )
816         {
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:
822         {
823             Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
824             if ( xAttr.is() )
825             {
826                 sResult += lcl_serializeForDisplay( xAttr );
827                 ++nAttributeNodes;
828             }
829         }
830         continue;
831 
832         default:
833             break;
834         }
835 
836         // append node
837         xNode->appendChild( xDocument->importNode( xCurrent, sal_True ) );
838     }
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;
844 
845     // serialize fragment
846     CSerializationAppXML aSerialization;
847     aSerialization.setSource( xFragment );
848     aSerialization.serialize();
849 
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() );
856 
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 );
863     */
864 
865     // well, the serialization prepends XML header(s) that we need to
866     // remove first.
867     OUStringBuffer aBuffer;
868     while( ! xTextInputStream->isEOF() )
869     {
870         OUString sLine = xTextInputStream->readLine();
871         if( sLine.getLength() > 0
872             && sLine.compareToAscii( "<?xml", 5 ) != 0 )
873         {
874             aBuffer.append( sLine );
875             aBuffer.append( sal_Unicode('\n') );
876         }
877     }
878     sResult = aBuffer.makeStringAndClear();
879 
880     return sResult;
881 }
882 
883 OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
884 {
885     // error handling first
886     if( ! xResult.is() )
887         return getResource( RID_STR_XFORMS_CANT_EVALUATE );
888 
889 
890     // TODO: localize
891     OUStringBuffer aBuffer;
892 
893     switch( xResult->getObjectType() )
894     {
895     case XPathObjectType_XPATH_BOOLEAN:
896         aBuffer.append( xResult->getBoolean()
897                         ? OUSTRING("true")
898                         : OUSTRING("false") );
899         break;
900 
901     case XPathObjectType_XPATH_STRING:
902         aBuffer.append( sal_Unicode('"') );
903         aBuffer.append( xResult->getString() );
904         aBuffer.append( sal_Unicode('"') );
905         break;
906 
907     case XPathObjectType_XPATH_NODESET:
908         aBuffer.append( lcl_serializeForDisplay( xResult->getNodeList() ) );
909         break;
910 
911     case XPathObjectType_XPATH_NUMBER:
912         aBuffer.append( xResult->getDouble() );
913         break;
914 
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;
924     }
925 
926     return aBuffer.makeStringAndClear();
927 }
928 
929 OUString Model::getResultForExpression(
930     const XPropertySet_t& xBinding,
931     sal_Bool bIsBindingExpression,
932     const OUString& sExpression )
933     throw( RuntimeException )
934 {
935     Binding* pBinding = Binding::getBinding( xBinding );
936     if( pBinding == NULL )
937         throw RuntimeException();
938 
939     // prepare & evaluate expression
940     OUStringBuffer aBuffer;
941     ComputedExpression aExpression;
942     aExpression.setExpression( sExpression );
943     if( bIsBindingExpression )
944     {
945         // binding: use binding context and evaluation
946         aExpression.evaluate( pBinding->getEvaluationContext() );
947         aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
948     }
949     else
950     {
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 ++ )
957         {
958             aExpression.evaluate( *aIter );
959             aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
960             aBuffer.append( sal_Unicode('\n') );
961         }
962     }
963     return aBuffer.makeStringAndClear();
964 }
965 
966 sal_Bool Model::isValidXMLName( const OUString& sName )
967     throw( RuntimeException )
968 {
969     return isValidQName( sName, NULL );
970 }
971 
972 sal_Bool Model::isValidPrefixName( const OUString& sName )
973     throw( RuntimeException )
974 {
975     return ::isValidPrefixName( sName, NULL );
976 }
977 
978 void Model::setNodeValue(
979     const XNode_t& xNode,
980     const rtl::OUString& sValue )
981     throw( RuntimeException )
982 {
983     setSimpleContent( xNode, sValue );
984 }
985 
986 
987 //
988 // helper functions from model_helper.hxx
989 //
990 
991 void xforms::getInstanceData(
992     const Sequence<PropertyValue>& aValues,
993     OUString* pID,
994     Reference<XDocument>* pInstance,
995     OUString* pURL,
996     bool* pURLOnce )
997 {
998     sal_Int32 nValues = aValues.getLength();
999     const PropertyValue* pValues = aValues.getConstArray();
1000     for( sal_Int32 n = 0; n < nValues; n++ )
1001     {
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
1012     }
1013 }
1014 
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 )
1021 {
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;
1032 
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
1040 
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
1049 
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 ) \
1056     { \
1057         pSequence[ nIndex ].Name = OUSTRING(#NAME); \
1058         pSequence[ nIndex ].Value <<= *p##NAME; \
1059         nIndex++; \
1060     }
1061     PROP(ID);
1062     PROP(Instance);
1063     PROP(URL);
1064     PROP(URLOnce);
1065 #undef PROP
1066 }
1067