bump product version to 4.1.6.2
[LibreOffice.git] / forms / source / xforms / model_ui.cxx
blob39c269087710df6d349b62b7763f8c59eea78beb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "model.hxx"
22 #include "model_helper.hxx"
23 #include "mip.hxx"
24 #include "evaluationcontext.hxx"
25 #include "unohelper.hxx"
26 #include "submission/serialization_app_xml.hxx"
27 #include "resourcehelper.hxx"
28 #include "xmlhelper.hxx"
29 #include "convert.hxx"
31 #include <rtl/ustring.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <tools/debug.hxx>
34 #include <comphelper/processfactory.hxx>
36 // UNO classes
37 #include <com/sun/star/xml/dom/XNode.hpp>
38 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
39 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
40 #include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
41 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
42 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
43 #include <com/sun/star/beans/PropertyValue.hpp>
44 #include <com/sun/star/io/XInputStream.hpp>
45 #include <com/sun/star/io/XActiveDataSink.hpp>
46 #include <com/sun/star/io/TextInputStream.hpp>
47 #include <com/sun/star/container/XEnumeration.hpp>
48 #include <com/sun/star/container/XNameContainer.hpp>
49 #include <com/sun/star/frame/XModel.hpp>
50 #include <com/sun/star/xforms/XFormsSupplier.hpp>
51 #include <com/sun/star/xforms/XDataTypeRepository.hpp>
52 #include <com/sun/star/xsd/XDataType.hpp>
53 #include <com/sun/star/xsd/DataTypeClass.hpp>
56 using com::sun::star::beans::PropertyValue;
57 using com::sun::star::io::XInputStream;
58 using com::sun::star::io::XActiveDataSink;
59 using com::sun::star::io::TextInputStream;
60 using com::sun::star::io::XTextInputStream2;
61 using com::sun::star::container::XEnumeration;
62 using com::sun::star::container::XNameContainer;
63 using com::sun::star::xforms::XFormsSupplier;
65 using namespace xforms;
66 using namespace com::sun::star::uno;
67 using namespace com::sun::star::xml::dom;
68 using namespace com::sun::star::xml::xpath;
73 // implement XFormsUIHelper1
76 OUString Model::getDefaultServiceNameForNode( const XNode_t& xNode )
77 throw( RuntimeException )
79 // determine service for control. string/text field is default.
80 OUString sService = "com.sun.star.form.component.TextField";
82 // query repository for suitable type
83 OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
84 OUString sTypeName = queryMIP( xNode ).getTypeName();
85 if( mxDataTypes->hasByName( sTypeName ) )
87 OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
88 "has or has not?" );
90 switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
92 case com::sun::star::xsd::DataTypeClass::BOOLEAN:
93 sService = "com.sun.star.form.component.CheckBox";
94 break;
95 case com::sun::star::xsd::DataTypeClass::DOUBLE:
96 case com::sun::star::xsd::DataTypeClass::DECIMAL:
97 case com::sun::star::xsd::DataTypeClass::FLOAT:
98 sService = "com.sun.star.form.component.NumericField";
99 break;
101 case com::sun::star::xsd::DataTypeClass::STRING:
102 case com::sun::star::xsd::DataTypeClass::DURATION:
103 case com::sun::star::xsd::DataTypeClass::DATETIME:
104 case com::sun::star::xsd::DataTypeClass::TIME:
105 case com::sun::star::xsd::DataTypeClass::DATE:
106 case com::sun::star::xsd::DataTypeClass::gYearMonth:
107 case com::sun::star::xsd::DataTypeClass::gYear:
108 case com::sun::star::xsd::DataTypeClass::gMonthDay:
109 case com::sun::star::xsd::DataTypeClass::gDay:
110 case com::sun::star::xsd::DataTypeClass::gMonth:
111 case com::sun::star::xsd::DataTypeClass::hexBinary:
112 case com::sun::star::xsd::DataTypeClass::base64Binary:
113 case com::sun::star::xsd::DataTypeClass::anyURI:
114 case com::sun::star::xsd::DataTypeClass::QName:
115 case com::sun::star::xsd::DataTypeClass::NOTATION:
116 default:
117 // keep default
118 break;
122 return sService;
126 static void lcl_OutPosition( OUStringBuffer& rBuffer,
127 const Reference<XNode>& xNode )
129 OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
131 // count # of occurrences of this node
132 sal_Int32 nFound = 0;
133 sal_Int32 nPosition = -1;
134 if( xNode->getParentNode().is() )
136 for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
137 xIter != NULL;
138 xIter = xIter->getNextSibling() )
140 if( xIter->getNodeType() == xNode->getNodeType() &&
141 xIter->getNodeName() == xNode->getNodeName() &&
142 xIter->getNamespaceURI() == xNode->getNamespaceURI() )
144 nFound++;
145 if( xIter == xNode )
146 nPosition = nFound;
150 OSL_ENSURE( nFound > 0 && nPosition > 0, "node not found???" );
152 // output position (if necessary)
153 if( nFound > 1 )
155 rBuffer.insert( 0, sal_Unicode(']') );
156 rBuffer.insert( 0, nPosition );
157 rBuffer.insert( 0, sal_Unicode('[') );
161 static void lcl_OutName( OUStringBuffer& rBuffer,
162 const Reference<XNode>& xNode )
164 rBuffer.insert( 0, xNode->getNodeName() );
165 OUString sPrefix = xNode->getPrefix();
166 if( !sPrefix.isEmpty() )
168 rBuffer.insert( 0, sal_Unicode(':') );
169 rBuffer.insert( 0, sPrefix );
173 static void lcl_OutInstance( OUStringBuffer& rBuffer,
174 const Reference<XNode>& xNode,
175 Model* pModel )
177 Reference<XDocument> xDoc = xNode->getOwnerDocument();
179 if( xDoc != pModel->getDefaultInstance() )
181 rBuffer.insert( 0, "')" );
183 // iterate over instances, and find the right one
184 OUString sInstanceName;
185 Reference<XEnumeration> xEnum =
186 pModel->getInstances()->createEnumeration();
187 while( sInstanceName.isEmpty() && xEnum->hasMoreElements() )
189 Sequence<PropertyValue> aValues;
190 xEnum->nextElement() >>= aValues;
192 // get ID and instance
193 OUString sId;
194 Reference<XDocument> xInstance;
195 getInstanceData( aValues, &sId, &xInstance, NULL, NULL );
197 // now check whether this was our instance:
198 if( xInstance == xDoc )
199 sInstanceName = sId;
202 rBuffer.insert( 0, sInstanceName );
203 rBuffer.insert( 0, "instance('" );
207 OUString Model::getDefaultBindingExpressionForNode(
208 const XNode_t& xNode,
209 const EvaluationContext& rContext)
211 OSL_ENSURE( xNode.is(), "need node" );
213 // iterate upwards and put sections into the expression buffer.
214 // Stop iteration either at context node (relative expression) or
215 // at document root, whichever occurs first.
216 OUStringBuffer aBuffer;
217 for( Reference<XNode> xCurrent = xNode;
218 xCurrent.is() && xCurrent != rContext.mxContextNode;
219 xCurrent = xCurrent->getParentNode() )
221 // insert a '/' for every step except the first
222 if( aBuffer.getLength() > 0 )
223 aBuffer.insert( 0, sal_Unicode('/') );
225 switch( xCurrent->getNodeType() )
227 case NodeType_ELEMENT_NODE:
228 lcl_OutPosition( aBuffer, xCurrent );
229 lcl_OutName( aBuffer, xCurrent );
230 break;
232 case NodeType_TEXT_NODE:
233 lcl_OutPosition( aBuffer, xCurrent );
234 aBuffer.insert( 0, "text()" );
235 break;
237 case NodeType_ATTRIBUTE_NODE:
238 lcl_OutName( aBuffer, xCurrent );
239 aBuffer.insert( 0, sal_Unicode('@') );
240 break;
242 case NodeType_DOCUMENT_NODE:
243 // check for which instance we have
244 lcl_OutInstance( aBuffer, xCurrent, this );
245 break;
247 default:
248 // unknown type? fail!
249 OSL_FAIL( "unknown node type!" );
250 xCurrent.set( NULL );
251 aBuffer.makeStringAndClear();
252 // we'll remove the slash below
253 aBuffer.insert( 0, sal_Unicode('/') );
254 break;
258 return aBuffer.makeStringAndClear();
263 OUString Model::getDefaultBindingExpressionForNode( const XNode_t& xNode )
264 throw( RuntimeException )
266 return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
269 static bool lcl_isWhitespace( const OUString& rString )
271 sal_Int32 nLength = rString.getLength();
272 const sal_Unicode* pStr = rString.getStr();
274 bool bWhitespace = true;
275 for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
277 sal_Unicode c = pStr[i];
278 bWhitespace = ( c == sal_Unicode(0x09) ||
279 c == sal_Unicode(0x0A) ||
280 c == sal_Unicode(0x0D) ||
281 c == sal_Unicode(0x20) );
283 return bWhitespace;
286 OUString Model::getNodeDisplayName( const XNode_t& xNode,
287 sal_Bool bDetail )
288 throw( RuntimeException )
290 OUStringBuffer aBuffer;
292 switch( xNode->getNodeType() )
294 case NodeType_ELEMENT_NODE:
295 lcl_OutName( aBuffer, xNode );
296 break;
298 case NodeType_TEXT_NODE:
300 OUString sContent = xNode->getNodeValue();
301 if( bDetail || ! lcl_isWhitespace( sContent ) )
303 aBuffer.append( sal_Unicode('"') );
304 aBuffer.append( Convert::collapseWhitespace( sContent ) );
305 aBuffer.append( sal_Unicode('"') );
308 break;
310 case NodeType_ATTRIBUTE_NODE:
311 lcl_OutName( aBuffer, xNode );
312 aBuffer.insert( 0, sal_Unicode('@') );
313 break;
315 case NodeType_DOCUMENT_NODE:
316 if( xNode == getDefaultInstance() )
317 aBuffer.append( sal_Unicode('/') );
318 else
319 lcl_OutInstance( aBuffer, xNode, this );
320 break;
322 default:
323 // unknown type? fail!
324 OSL_FAIL( "unknown node type!" );
325 break;
328 return aBuffer.makeStringAndClear();
331 OUString Model::getNodeName( const XNode_t& xNode )
332 throw( RuntimeException )
334 OUStringBuffer aBuffer;
336 switch( xNode->getNodeType() )
338 case NodeType_ELEMENT_NODE:
339 case NodeType_ATTRIBUTE_NODE:
340 lcl_OutName( aBuffer, xNode );
341 break;
343 case NodeType_TEXT_NODE:
344 case NodeType_DOCUMENT_NODE:
345 default:
346 // unknown type? fail!
347 OSL_FAIL( "no name for this node type!" );
348 break;
351 return aBuffer.makeStringAndClear();
354 OUString Model::getBindingName( const XPropertySet_t& xBinding,
355 sal_Bool /*bDetail*/ )
356 throw( RuntimeException )
358 OUString sID;
359 xBinding->getPropertyValue( "BindingID" ) >>= sID;
360 OUString sExpression;
361 xBinding->getPropertyValue( "BindingExpression" ) >>= sExpression;
363 OUStringBuffer aBuffer;
364 if( !sID.isEmpty() )
366 aBuffer.append( sID );
367 aBuffer.append( " (" );
368 aBuffer.append( sExpression );
369 aBuffer.append( ")" );
371 else
372 aBuffer.append( sExpression );
374 return aBuffer.makeStringAndClear();
377 OUString Model::getSubmissionName( const XPropertySet_t& xSubmission,
378 sal_Bool /*bDetail*/ )
379 throw( RuntimeException )
381 OUString sID;
382 xSubmission->getPropertyValue( "ID" ) >>= sID;
383 return sID;
386 Model::XPropertySet_t Model::cloneBindingAsGhost( const XPropertySet_t &xBinding )
387 throw( RuntimeException )
389 // Create a new binding instance first...
390 Binding *pBinding = new Binding();
392 // ...and bump up the "defered notification counter"
393 // to prevent this binding from contributing to the
394 // MIPs table...
395 pBinding->deferNotifications(true);
397 // Copy the propertyset and return result...
398 XPropertySet_t xNewBinding(pBinding);
399 copy( xBinding, xNewBinding );
400 return xNewBinding;
403 void Model::removeBindingIfUseless( const XPropertySet_t& xBinding )
404 throw( RuntimeException )
406 Binding* pBinding = Binding::getBinding( xBinding );
407 if( pBinding != NULL )
409 if( ! pBinding->isUseful() )
410 mpBindings->removeItem( pBinding );
414 Model::XDocument_t Model::newInstance( const OUString& sName,
415 const OUString& sURL,
416 sal_Bool bURLOnce )
417 throw( RuntimeException )
419 // create a default instance with <instanceData> element
420 XDocument_t xInstance = getDocumentBuilder()->newDocument();
421 DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
423 Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
424 Reference<XNode>( xInstance->createElement( "instanceData" ),
425 UNO_QUERY_THROW ) );
427 Sequence<PropertyValue> aSequence;
428 bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
429 setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
430 sal_Int32 nInstance = mpInstances->addItem( aSequence );
431 loadInstance( nInstance );
433 return xInstance;
436 static sal_Int32 lcl_findProp( const PropertyValue* pValues,
437 sal_Int32 nLength,
438 const OUString& rName )
440 bool bFound = false;
441 sal_Int32 n = 0;
442 for( ; !bFound && n < nLength; n++ )
444 bFound = ( pValues[n].Name == rName );
446 return bFound ? ( n - 1) : -1;
449 sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
450 const OUString& rName )
452 sal_Int32 nLength = pInstances->countItems();
453 sal_Int32 n = 0;
454 bool bFound = false;
455 for( ; !bFound && n < nLength; n++ )
457 OUString sName;
458 getInstanceData( pInstances->getItem( n ), &sName, NULL, NULL, NULL );
459 bFound = ( sName == rName );
461 return bFound ? ( n - 1 ) : -1;
464 void Model::renameInstance( const OUString& sFrom,
465 const OUString& sTo,
466 const OUString& sURL,
467 sal_Bool bURLOnce )
468 throw( RuntimeException )
470 sal_Int32 nPos = lcl_findInstance( mpInstances, sFrom );
471 if( nPos != -1 )
473 Sequence<PropertyValue> aSeq = mpInstances->getItem( nPos );
474 PropertyValue* pSeq = aSeq.getArray();
475 sal_Int32 nLength = aSeq.getLength();
477 sal_Int32 nProp = lcl_findProp( pSeq, nLength, "ID" );
478 if( nProp == -1 )
480 // add name property
481 aSeq.realloc( nLength + 1 );
482 pSeq = aSeq.getArray();
483 pSeq[ nLength ].Name = "ID";
484 nProp = nLength;
487 // change name
488 pSeq[ nProp ].Value <<= sTo;
490 // change url
491 nProp = lcl_findProp( pSeq, nLength, "URL" );
492 if(nProp != -1)
493 pSeq[ nProp ].Value <<= sURL;
495 // change urlonce
496 nProp = lcl_findProp( pSeq, nLength, "URLOnce" );
497 if(nProp != -1)
498 pSeq[ nProp ].Value <<= bURLOnce;
500 // set instance
501 mpInstances->setItem( nPos, aSeq );
505 void Model::removeInstance( const OUString& sName )
506 throw( RuntimeException )
508 sal_Int32 nPos = lcl_findInstance( mpInstances, sName );
509 if( nPos != -1 )
510 mpInstances->removeItem( mpInstances->getItem( nPos ) );
513 static Reference<XNameContainer> lcl_getModels(
514 const Reference<com::sun::star::frame::XModel>& xComponent )
516 Reference<XNameContainer> xRet;
517 Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
518 if( xSupplier.is() )
520 xRet = xSupplier->getXForms();
522 return xRet;
525 Model::XModel_t Model::newModel( const Reference<com::sun::star::frame::XModel>& xCmp,
526 const OUString& sName )
527 throw( RuntimeException )
529 Model::XModel_t xModel;
530 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
531 if( xModels.is()
532 && ! xModels->hasByName( sName ) )
534 Model* pModel = new Model();
535 xModel.set( pModel );
537 pModel->setID( sName );
538 pModel->newInstance( OUString(), OUString(), sal_False );
539 pModel->initialize();
540 xModels->insertByName( sName, makeAny( xModel ) );
543 return xModel;
546 void Model::renameModel( const Reference<com::sun::star::frame::XModel>& xCmp,
547 const OUString& sFrom,
548 const OUString& sTo )
549 throw( RuntimeException )
551 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
552 if( xModels.is()
553 && xModels->hasByName( sFrom )
554 && ! xModels->hasByName( sTo ) )
556 Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
557 xModel->setID( sTo );
558 xModels->insertByName( sTo, makeAny( xModel ) );
559 xModels->removeByName( sFrom );
563 void Model::removeModel( const Reference<com::sun::star::frame::XModel>& xCmp,
564 const OUString& sName )
565 throw( RuntimeException )
567 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
568 if( xModels.is()
569 && xModels->hasByName( sName ) )
571 xModels->removeByName( sName );
575 Model::XNode_t Model::createElement( const XNode_t& xParent,
576 const OUString& sName )
577 throw( RuntimeException )
579 Reference<XNode> xNode;
580 if( xParent.is()
581 && isValidXMLName( sName ) )
583 // TODO: implement proper namespace handling
584 xNode.set( xParent->getOwnerDocument()->createElement( sName ),
585 UNO_QUERY );
587 return xNode;
590 Model::XNode_t Model::createAttribute( const XNode_t& xParent,
591 const OUString& sName )
592 throw( RuntimeException )
594 Reference<XNode> xNode;
595 Reference<XElement> xElement( xParent, UNO_QUERY );
596 if( xParent.is()
597 && xElement.is()
598 && isValidXMLName( sName ) )
600 // handle case where attribute already exists
601 sal_Int32 nCount = 0;
602 OUString sUniqueName = sName;
603 while( xElement->hasAttribute( sUniqueName ) )
605 nCount++;
606 sUniqueName = sName + OUString::valueOf( nCount );
609 // TODO: implement proper namespace handling
610 xNode.set( xParent->getOwnerDocument()->createAttribute( sUniqueName ),
611 UNO_QUERY );
613 return xNode;
616 Model::XNode_t Model::renameNode( const XNode_t& xNode,
617 const OUString& sName )
618 throw( RuntimeException )
620 // early out if we don't have to change the name
621 if( xNode->getNodeName() == sName )
622 return xNode;
624 // refuse to change name if its an attribute, and the name is already used
625 if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE
626 && xNode->getParentNode().is()
627 && Reference<XElement>(xNode->getParentNode(), UNO_QUERY_THROW)->hasAttribute( sName ) )
628 return xNode;
630 // note old binding expression so we can adjust bindings below
631 OUString sOldDefaultBindingExpression =
632 getDefaultBindingExpressionForNode( xNode );
634 Reference<XDocument> xDoc = xNode->getOwnerDocument();
635 Reference<XNode> xNew;
636 if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
638 Reference<XElement> xElem = xDoc->createElement( sName );
639 xNew.set( xElem, UNO_QUERY );
641 // iterate over all attributes and append them to the new element
642 Reference<XElement> xOldElem( xNode, UNO_QUERY );
643 OSL_ENSURE( xNode.is(), "no element?" );
645 Reference<XNamedNodeMap> xMap = xNode->getAttributes();
646 sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
647 for( sal_Int32 n = 0; n < nLength; n++ )
649 Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
650 xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
653 // iterate over all children and append them to the new element
654 for( Reference<XNode> xCurrent = xNode->getFirstChild();
655 xCurrent.is();
656 xCurrent = xNode->getFirstChild() )
658 xNew->appendChild( xNode->removeChild( xCurrent ) );
661 xNode->getParentNode()->replaceChild( xNew, xNode );
663 else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
665 // create new attribute
666 Reference<XAttr> xAttr = xDoc->createAttribute( sName );
667 xAttr->setValue( xNode->getNodeValue() );
669 // replace node
670 Reference<XNode> xParent = xNode->getParentNode();
671 xParent->removeChild( xNode );
672 xNew = xParent->appendChild( Reference<XNode>( xAttr, UNO_QUERY ) );
674 else
676 OSL_FAIL( "can't rename this node type" );
679 // adjust bindings (if necessary):
680 if( xNew.is() )
682 // iterate over bindings and replace default expressions
683 OUString sNewDefaultBindingExpression =
684 getDefaultBindingExpressionForNode( xNew );
685 for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
687 Binding* pBinding = Binding::getBinding(
688 mpBindings->Collection<XPropertySet_t>::getItem( n ) );
690 if( pBinding->getBindingExpression()
691 == sOldDefaultBindingExpression )
692 pBinding->setBindingExpression( sNewDefaultBindingExpression );
696 // return node; return old node if renaming failed
697 return xNew.is() ? xNew : xNode;
700 Model::XPropertySet_t Model::getBindingForNode( const XNode_t& xNode,
701 sal_Bool bCreate )
702 throw( RuntimeException )
704 OSL_ENSURE( xNode.is(), "no node?" );
706 // We will iterate over all bindings and determine the
707 // appropriateness of the respective binding for this node. The
708 // best one will be used. If we don't find any and bCreate is set,
709 // then we will create a suitable binding.
710 Binding* pBestBinding = NULL;
711 sal_Int32 nBestScore = 0;
713 for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
715 Binding* pBinding = Binding::getBinding(
716 mpBindings->Collection<XPropertySet_t>::getItem( n ) );
718 OSL_ENSURE( pBinding != NULL, "no binding?" );
719 Reference<XNodeList> xNodeList = pBinding->getXNodeList();
721 sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
722 if( nNodes > 0 && xNodeList->item( 0 ) == xNode )
724 // allright, we found a suitable node. Let's determine how
725 // well it fits. Score:
726 // - bind to exactly this node is better than whole nodeset
727 // - simple binding expressions is better than complex ones
728 sal_Int32 nScore = 0;
729 if( nNodes == 1 )
730 nScore ++;
731 if( pBinding->isSimpleBindingExpression() )
732 nScore ++;
734 // if we found a better binding, remember it
735 if( nScore > nBestScore )
737 pBestBinding = pBinding;
738 nBestScore = nScore;
743 // create binding, if none was found and bCreate is set
744 OSL_ENSURE( ( nBestScore == 0 ) == ( pBestBinding == NULL ),
745 "score != binding?" );
746 if( bCreate && pBestBinding == NULL )
748 pBestBinding = new Binding();
749 pBestBinding->setBindingExpression(
750 getDefaultBindingExpressionForNode( xNode ) );
751 mpBindings->addItem( pBestBinding );
754 return pBestBinding;
757 void Model::removeBindingForNode( const XNode_t& )
758 throw( RuntimeException )
760 // determine whether suitable binding is still used
763 static OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
765 OUString sResult;
766 OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
767 if ( _rxAttrNode.is() )
769 OUStringBuffer aBuffer;
770 aBuffer.append( _rxAttrNode->getName() );
771 aBuffer.appendAscii( "=" );
772 OUString sValue = _rxAttrNode->getValue();
773 sal_Unicode nQuote = '"';
774 if ( sValue.indexOf( nQuote ) >= 0 )
775 nQuote = '\'';
776 aBuffer.append( nQuote );
777 aBuffer.append( sValue );
778 aBuffer.append( nQuote );
779 aBuffer.append( (sal_Unicode)' ' );
780 sResult = aBuffer.makeStringAndClear();
782 return sResult;
785 static OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
787 OUString sResult;
789 // create document fragment
790 Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
791 Reference<XDocumentFragment> xFragment(
792 xDocument->createDocumentFragment() );
793 Reference<XNode> xNode( xFragment, UNO_QUERY );
794 OSL_ENSURE( xFragment.is(), "xFragment" );
795 OSL_ENSURE( xNode.is(), "xNode" );
797 sal_Int32 nAttributeNodes = 0;
799 // attach nodelist to fragment
800 sal_Int32 nLength = xNodes->getLength();
801 for( sal_Int32 i = 0; i < nLength; i++ )
803 Reference<XNode> xCurrent = xNodes->item( i );
805 switch ( xCurrent->getNodeType() )
807 case NodeType_DOCUMENT_NODE:
808 // special-case documents: use top-level element instead
809 xCurrent = xCurrent->getFirstChild();
810 break;
811 case NodeType_ATTRIBUTE_NODE:
813 Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
814 if ( xAttr.is() )
816 sResult += lcl_serializeForDisplay( xAttr );
817 ++nAttributeNodes;
820 continue;
822 default:
823 break;
826 // append node
827 xNode->appendChild( xDocument->importNode( xCurrent, sal_True ) );
829 OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
830 "lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
831 if ( nAttributeNodes )
832 // had only attribute nodes
833 return sResult;
835 // serialize fragment
836 CSerializationAppXML aSerialization;
837 aSerialization.setSource( xFragment );
838 aSerialization.serialize();
840 // copy stream into buffer
841 Reference<XTextInputStream2> xTextInputStream = TextInputStream::create( comphelper::getProcessComponentContext() );
842 xTextInputStream->setInputStream( aSerialization.getInputStream() );
844 /* WORK AROUND for problem in serialization: currently, multiple
845 XML delarations (<?xml...?>) are being written out and we don't
846 want them. When this is fixed, the code below is nice and
847 simple. The current code filters out the declarations.
848 OUString sResult = xTextInputStream->readString( Sequence<sal_Unicode>(),
849 sal_True );
852 // well, the serialization prepends XML header(s) that we need to
853 // remove first.
854 OUStringBuffer aBuffer;
855 while( ! xTextInputStream->isEOF() )
857 OUString sLine = xTextInputStream->readLine();
858 if( !sLine.isEmpty()
859 && !sLine.startsWith( "<?xml" ) )
861 aBuffer.append( sLine );
862 aBuffer.append( sal_Unicode('\n') );
865 sResult = aBuffer.makeStringAndClear();
867 return sResult;
870 static OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
872 // error handling first
873 if( ! xResult.is() )
874 return getResource( RID_STR_XFORMS_CANT_EVALUATE );
877 // TODO: localize
878 OUStringBuffer aBuffer;
880 switch( xResult->getObjectType() )
882 case XPathObjectType_XPATH_BOOLEAN:
883 aBuffer.append( xResult->getBoolean()
884 ? OUString("true")
885 : OUString("false") );
886 break;
888 case XPathObjectType_XPATH_STRING:
889 aBuffer.append( sal_Unicode('"') );
890 aBuffer.append( xResult->getString() );
891 aBuffer.append( sal_Unicode('"') );
892 break;
894 case XPathObjectType_XPATH_NODESET:
895 aBuffer.append( lcl_serializeForDisplay( xResult->getNodeList() ) );
896 break;
898 case XPathObjectType_XPATH_NUMBER:
899 aBuffer.append( xResult->getDouble() );
900 break;
902 case XPathObjectType_XPATH_UNDEFINED:
903 case XPathObjectType_XPATH_POINT:
904 case XPathObjectType_XPATH_RANGE:
905 case XPathObjectType_XPATH_LOCATIONSET:
906 case XPathObjectType_XPATH_USERS:
907 case XPathObjectType_XPATH_XSLT_TREE:
908 default:
909 // TODO: localized error message?
910 break;
913 return aBuffer.makeStringAndClear();
916 OUString Model::getResultForExpression(
917 const XPropertySet_t& xBinding,
918 sal_Bool bIsBindingExpression,
919 const OUString& sExpression )
920 throw( RuntimeException )
922 Binding* pBinding = Binding::getBinding( xBinding );
923 if( pBinding == NULL )
924 throw RuntimeException();
926 // prepare & evaluate expression
927 OUStringBuffer aBuffer;
928 ComputedExpression aExpression;
929 aExpression.setExpression( sExpression );
930 if( bIsBindingExpression )
932 // binding: use binding context and evaluation
933 aExpression.evaluate( pBinding->getEvaluationContext() );
934 aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
936 else
938 // MIP (not binding): iterate over bindings contexts
939 std::vector<EvaluationContext> aContext =
940 pBinding->getMIPEvaluationContexts();
941 for( std::vector<EvaluationContext>::iterator aIter = aContext.begin();
942 aIter != aContext.end();
943 ++aIter )
945 aExpression.evaluate( *aIter );
946 aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
947 aBuffer.append( sal_Unicode('\n') );
950 return aBuffer.makeStringAndClear();
953 sal_Bool Model::isValidXMLName( const OUString& sName )
954 throw( RuntimeException )
956 return isValidQName( sName, NULL );
959 sal_Bool Model::isValidPrefixName( const OUString& sName )
960 throw( RuntimeException )
962 return ::isValidPrefixName( sName, NULL );
965 void Model::setNodeValue(
966 const XNode_t& xNode,
967 const OUString& sValue )
968 throw( RuntimeException )
970 setSimpleContent( xNode, sValue );
975 // helper functions from model_helper.hxx
978 void xforms::getInstanceData(
979 const Sequence<PropertyValue>& aValues,
980 OUString* pID,
981 Reference<XDocument>* pInstance,
982 OUString* pURL,
983 bool* pURLOnce )
985 sal_Int32 nValues = aValues.getLength();
986 const PropertyValue* pValues = aValues.getConstArray();
987 for( sal_Int32 n = 0; n < nValues; n++ )
989 const PropertyValue& rValue = pValues[n];
990 #define PROP(NAME) \
991 if( p##NAME != NULL && \
992 rValue.Name == #NAME ) \
993 rValue.Value >>= (*p##NAME)
994 PROP(ID);
995 PROP(Instance);
996 PROP(URL);
997 PROP(URLOnce);
998 #undef PROP
1002 void xforms::setInstanceData(
1003 Sequence<PropertyValue>& aSequence,
1004 const OUString* _pID,
1005 const Reference<XDocument>* _pInstance,
1006 const OUString* _pURL,
1007 const bool* _pURLOnce )
1009 // get old instance data
1010 OUString sID;
1011 Reference<XDocument> xInstance;
1012 OUString sURL;
1013 bool bURLOnce = false;
1014 getInstanceData( aSequence, &sID, &xInstance, &sURL, &bURLOnce );
1015 const OUString* pID = !sID.isEmpty() ? &sID : NULL;
1016 const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : NULL;
1017 const OUString* pURL = !sURL.isEmpty() ? &sURL : NULL;
1018 const bool* pURLOnce = ( bURLOnce && pURL != NULL ) ? &bURLOnce : NULL;
1020 // determine new instance data
1021 #define PROP(NAME) if( _p##NAME != NULL ) p##NAME = _p##NAME
1022 PROP(ID);
1023 PROP(Instance);
1024 PROP(URL);
1025 PROP(URLOnce);
1026 #undef PROP
1028 // count # of values we want to set
1029 sal_Int32 nCount = 0;
1030 #define PROP(NAME) if( p##NAME != NULL ) nCount++
1031 PROP(ID);
1032 PROP(Instance);
1033 PROP(URL);
1034 PROP(URLOnce);
1035 #undef PROP
1037 // realloc sequence and enter values;
1038 aSequence.realloc( nCount );
1039 PropertyValue* pSequence = aSequence.getArray();
1040 sal_Int32 nIndex = 0;
1041 #define PROP(NAME) \
1042 if( p##NAME != NULL ) \
1044 pSequence[ nIndex ].Name = OUString(#NAME); \
1045 pSequence[ nIndex ].Value <<= *p##NAME; \
1046 nIndex++; \
1048 PROP(ID);
1049 PROP(Instance);
1050 PROP(URL);
1051 PROP(URLOnce);
1052 #undef PROP
1055 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */