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