bump product version to 7.6.3.2-android
[LibreOffice.git] / forms / source / xforms / model_ui.cxx
blob5d56e8b3baa062ccdd3509177041e51b1d85858d
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"
30 #include <strings.hrc>
32 #include <rtl/ustring.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <tools/debug.hxx>
35 #include <comphelper/processfactory.hxx>
37 // UNO classes
38 #include <com/sun/star/xml/dom/XNode.hpp>
39 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
40 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
41 #include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
42 #include <com/sun/star/xml/xpath/XXPathObject.hpp>
43 #include <com/sun/star/xml/xpath/XPathObjectType.hpp>
44 #include <com/sun/star/beans/PropertyValue.hpp>
45 #include <com/sun/star/io/TextInputStream.hpp>
46 #include <com/sun/star/container/XEnumeration.hpp>
47 #include <com/sun/star/container/XNameContainer.hpp>
48 #include <com/sun/star/frame/XModel.hpp>
49 #include <com/sun/star/xforms/XFormsSupplier.hpp>
50 #include <com/sun/star/xforms/XDataTypeRepository.hpp>
51 #include <com/sun/star/xsd/XDataType.hpp>
52 #include <com/sun/star/xsd/DataTypeClass.hpp>
55 using com::sun::star::beans::PropertyValue;
56 using com::sun::star::io::TextInputStream;
57 using com::sun::star::io::XTextInputStream2;
58 using com::sun::star::container::XEnumeration;
59 using com::sun::star::container::XNameContainer;
60 using com::sun::star::xforms::XFormsSupplier;
62 using namespace xforms;
63 using namespace com::sun::star::uno;
64 using namespace com::sun::star::xml::dom;
65 using namespace com::sun::star::xml::xpath;
68 // implement XFormsUIHelper1
71 OUString Model::getDefaultServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
73 // determine service for control. string/text field is default.
74 OUString sService = "com.sun.star.form.component.TextField";
76 // query repository for suitable type
77 OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
78 OUString sTypeName = queryMIP( xNode ).getTypeName();
79 if( mxDataTypes->hasByName( sTypeName ) )
81 OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
82 "has or has not?" );
84 switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
86 case css::xsd::DataTypeClass::BOOLEAN:
87 sService = "com.sun.star.form.component.CheckBox";
88 break;
89 case css::xsd::DataTypeClass::DOUBLE:
90 case css::xsd::DataTypeClass::DECIMAL:
91 case css::xsd::DataTypeClass::FLOAT:
92 sService = "com.sun.star.form.component.NumericField";
93 break;
95 case css::xsd::DataTypeClass::STRING:
96 case css::xsd::DataTypeClass::DURATION:
97 case css::xsd::DataTypeClass::DATETIME:
98 case css::xsd::DataTypeClass::TIME:
99 case css::xsd::DataTypeClass::DATE:
100 case css::xsd::DataTypeClass::gYearMonth:
101 case css::xsd::DataTypeClass::gYear:
102 case css::xsd::DataTypeClass::gMonthDay:
103 case css::xsd::DataTypeClass::gDay:
104 case css::xsd::DataTypeClass::gMonth:
105 case css::xsd::DataTypeClass::hexBinary:
106 case css::xsd::DataTypeClass::base64Binary:
107 case css::xsd::DataTypeClass::anyURI:
108 case css::xsd::DataTypeClass::QName:
109 case css::xsd::DataTypeClass::NOTATION:
110 default:
111 // keep default
112 break;
116 return sService;
120 static void lcl_OutPosition( OUStringBuffer& rBuffer,
121 const Reference<XNode>& xNode )
123 OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
125 // count # of occurrences of this node
126 sal_Int32 nFound = 0;
127 sal_Int32 nPosition = -1;
128 if( xNode->getParentNode().is() )
130 for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
131 xIter != nullptr;
132 xIter = xIter->getNextSibling() )
134 if( xIter->getNodeType() == xNode->getNodeType() &&
135 xIter->getNodeName() == xNode->getNodeName() &&
136 xIter->getNamespaceURI() == xNode->getNamespaceURI() )
138 nFound++;
139 if( xIter == xNode )
140 nPosition = nFound;
144 OSL_ENSURE( nFound > 0 && nPosition > 0, "node not found???" );
146 // output position (if necessary)
147 if( nFound > 1 )
149 rBuffer.insert( 0, ']' );
150 rBuffer.insert( 0, nPosition );
151 rBuffer.insert( 0, '[' );
155 static void lcl_OutName( OUStringBuffer& rBuffer,
156 const Reference<XNode>& xNode )
158 rBuffer.insert( 0, xNode->getNodeName() );
159 OUString sPrefix = xNode->getPrefix();
160 if( !sPrefix.isEmpty() )
162 rBuffer.insert( 0, sPrefix + ":" );
166 static void lcl_OutInstance( OUStringBuffer& rBuffer,
167 const Reference<XNode>& xNode,
168 Model* pModel )
170 Reference<XDocument> xDoc = xNode->getOwnerDocument();
172 if( xDoc == pModel->getDefaultInstance() )
173 return;
175 rBuffer.insert( 0, "')" );
177 // iterate over instances, and find the right one
178 OUString sInstanceName;
179 Reference<XEnumeration> xEnum =
180 pModel->getInstances()->createEnumeration();
181 while( sInstanceName.isEmpty() && xEnum->hasMoreElements() )
183 Sequence<PropertyValue> aValues;
184 xEnum->nextElement() >>= aValues;
186 // get ID and instance
187 OUString sId;
188 Reference<XDocument> xInstance;
189 getInstanceData( aValues, &sId, &xInstance, nullptr, nullptr );
191 // now check whether this was our instance:
192 if( xInstance == xDoc )
193 sInstanceName = sId;
196 rBuffer.insert( 0, "instance('" + sInstanceName );
199 OUString Model::getDefaultBindingExpressionForNode(
200 const XNode_t& xNode,
201 const EvaluationContext& rContext)
203 OSL_ENSURE( xNode.is(), "need node" );
205 // iterate upwards and put sections into the expression buffer.
206 // Stop iteration either at context node (relative expression) or
207 // at document root, whichever occurs first.
208 OUStringBuffer aBuffer;
209 for( Reference<XNode> xCurrent = xNode;
210 xCurrent.is() && xCurrent != rContext.mxContextNode;
211 xCurrent = xCurrent->getParentNode() )
213 // insert a '/' for every step except the first
214 if( !aBuffer.isEmpty() )
215 aBuffer.insert( 0, '/' );
217 switch( xCurrent->getNodeType() )
219 case NodeType_ELEMENT_NODE:
220 lcl_OutPosition( aBuffer, xCurrent );
221 lcl_OutName( aBuffer, xCurrent );
222 break;
224 case NodeType_TEXT_NODE:
225 lcl_OutPosition( aBuffer, xCurrent );
226 aBuffer.insert( 0, "text()" );
227 break;
229 case NodeType_ATTRIBUTE_NODE:
230 lcl_OutName( aBuffer, xCurrent );
231 aBuffer.insert( 0, '@' );
232 break;
234 case NodeType_DOCUMENT_NODE:
235 // check for which instance we have
236 lcl_OutInstance( aBuffer, xCurrent, this );
237 break;
239 default:
240 // unknown type? fail!
241 OSL_FAIL( "unknown node type!" );
242 return OUString();
246 return aBuffer.makeStringAndClear();
250 OUString Model::getDefaultBindingExpressionForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
252 return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
255 static bool lcl_isWhitespace( const OUString& rString )
257 sal_Int32 nLength = rString.getLength();
258 const sal_Unicode* pStr = rString.getStr();
260 bool bWhitespace = true;
261 for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
263 sal_Unicode c = pStr[i];
264 bWhitespace = ( c == u'\x0009' ||
265 c == u'\x000A' ||
266 c == u'\x000D' ||
267 c == u' ' );
269 return bWhitespace;
272 OUString Model::getNodeDisplayName( const css::uno::Reference<css::xml::dom::XNode>& xNode,
273 sal_Bool bDetail )
275 OUStringBuffer aBuffer;
277 switch( xNode->getNodeType() )
279 case NodeType_ELEMENT_NODE:
280 lcl_OutName( aBuffer, xNode );
281 break;
283 case NodeType_TEXT_NODE:
285 OUString sContent = xNode->getNodeValue();
286 if( bDetail || ! lcl_isWhitespace( sContent ) )
288 aBuffer.append("\"" + Convert::collapseWhitespace( sContent ) + "\"");
291 break;
293 case NodeType_ATTRIBUTE_NODE:
294 lcl_OutName( aBuffer, xNode );
295 aBuffer.insert( 0, '@' );
296 break;
298 case NodeType_DOCUMENT_NODE:
299 if( xNode == getDefaultInstance() )
300 aBuffer.append( '/' );
301 else
302 lcl_OutInstance( aBuffer, xNode, this );
303 break;
305 default:
306 // unknown type? fail!
307 OSL_FAIL( "unknown node type!" );
308 break;
311 return aBuffer.makeStringAndClear();
314 OUString Model::getNodeName( const css::uno::Reference<css::xml::dom::XNode>& xNode )
316 OUStringBuffer aBuffer;
318 switch( xNode->getNodeType() )
320 case NodeType_ELEMENT_NODE:
321 case NodeType_ATTRIBUTE_NODE:
322 lcl_OutName( aBuffer, xNode );
323 break;
325 case NodeType_TEXT_NODE:
326 case NodeType_DOCUMENT_NODE:
327 default:
328 // unknown type? fail!
329 OSL_FAIL( "no name for this node type!" );
330 break;
333 return aBuffer.makeStringAndClear();
336 OUString Model::getBindingName( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
337 sal_Bool /*bDetail*/ )
339 OUString sID;
340 xBinding->getPropertyValue( "BindingID" ) >>= sID;
341 OUString sExpression;
342 xBinding->getPropertyValue( "BindingExpression" ) >>= sExpression;
344 OUString sRet;
345 if( !sID.isEmpty() )
347 sRet = sID + " (" + sExpression + ") ";
349 else
350 sRet = sExpression;
352 return sRet;
355 OUString Model::getSubmissionName( const css::uno::Reference< ::css::beans::XPropertySet >& xSubmission,
356 sal_Bool /*bDetail*/ )
358 OUString sID;
359 xSubmission->getPropertyValue( "ID" ) >>= sID;
360 return sID;
363 css::uno::Reference< ::css::beans::XPropertySet > Model::cloneBindingAsGhost( const css::uno::Reference< ::css::beans::XPropertySet > &xBinding )
365 // Create a new binding instance first...
366 rtl::Reference<Binding> pBinding = new Binding();
368 // ...and bump up the "deferred notification counter"
369 // to prevent this binding from contributing to the
370 // MIPs table...
371 pBinding->deferNotifications(true);
373 // Copy the propertyset and return result...
374 XPropertySet_t xNewBinding(pBinding);
375 copy( xBinding, xNewBinding );
376 return xNewBinding;
379 void Model::removeBindingIfUseless( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding )
381 Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
382 if( pBinding != nullptr )
384 if( ! pBinding->isUseful() )
385 mxBindings->removeItem( pBinding );
389 css::uno::Reference<css::xml::dom::XDocument> Model::newInstance( const OUString& sName,
390 const OUString& sURL,
391 sal_Bool bURLOnce )
393 // create a default instance with <instanceData> element
394 css::uno::Reference<css::xml::dom::XDocument> xInstance = getDocumentBuilder()->newDocument();
395 DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
397 Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
398 Reference<XNode>( xInstance->createElement( "instanceData" ),
399 UNO_QUERY_THROW ) );
401 Sequence<PropertyValue> aSequence;
402 bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
403 setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
404 sal_Int32 nInstance = mxInstances->addItem( aSequence );
405 loadInstance( nInstance );
407 return xInstance;
410 static sal_Int32 lcl_findProp( const PropertyValue* pValues,
411 sal_Int32 nLength,
412 std::u16string_view rName )
414 bool bFound = false;
415 sal_Int32 n = 0;
416 for( ; !bFound && n < nLength; n++ )
418 bFound = ( pValues[n].Name == rName );
420 return bFound ? ( n - 1) : -1;
423 sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
424 std::u16string_view rName )
426 sal_Int32 nLength = pInstances->countItems();
427 sal_Int32 n = 0;
428 bool bFound = false;
429 for( ; !bFound && n < nLength; n++ )
431 OUString sName;
432 getInstanceData( pInstances->getItem( n ), &sName, nullptr, nullptr, nullptr );
433 bFound = ( sName == rName );
435 return bFound ? ( n - 1 ) : -1;
438 void Model::renameInstance( const OUString& sFrom,
439 const OUString& sTo,
440 const OUString& sURL,
441 sal_Bool bURLOnce )
443 sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sFrom );
444 if( nPos == -1 )
445 return;
447 Sequence<PropertyValue> aSeq = mxInstances->getItem( nPos );
448 PropertyValue* pSeq = aSeq.getArray();
449 sal_Int32 nLength = aSeq.getLength();
451 sal_Int32 nProp = lcl_findProp( pSeq, nLength, u"ID" );
452 if( nProp == -1 )
454 // add name property
455 aSeq.realloc( nLength + 1 );
456 pSeq = aSeq.getArray();
457 pSeq[ nLength ].Name = "ID";
458 nProp = nLength;
461 // change name
462 pSeq[ nProp ].Value <<= sTo;
464 // change url
465 nProp = lcl_findProp( pSeq, nLength, u"URL" );
466 if(nProp != -1)
467 pSeq[ nProp ].Value <<= sURL;
469 // change urlonce
470 nProp = lcl_findProp( pSeq, nLength, u"URLOnce" );
471 if(nProp != -1)
472 pSeq[ nProp ].Value <<= bURLOnce;
474 // set instance
475 mxInstances->setItem( nPos, aSeq );
478 void Model::removeInstance( const OUString& sName )
480 sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sName );
481 if( nPos != -1 )
482 mxInstances->removeItem( mxInstances->getItem( nPos ) );
485 static Reference<XNameContainer> lcl_getModels(
486 const Reference<css::frame::XModel>& xComponent )
488 Reference<XNameContainer> xRet;
489 Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
490 if( xSupplier.is() )
492 xRet = xSupplier->getXForms();
494 return xRet;
497 css::uno::Reference<css::xforms::XModel> Model::newModel( const Reference<css::frame::XModel>& xCmp,
498 const OUString& sName )
500 css::uno::Reference<css::xforms::XModel> xModel;
501 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
502 if( xModels.is()
503 && ! xModels->hasByName( sName ) )
505 rtl::Reference<Model> pModel = new Model();
506 xModel.set( pModel );
508 pModel->setID( sName );
509 pModel->newInstance( OUString(), OUString(), false );
510 pModel->initialize();
511 xModels->insertByName( sName, Any( xModel ) );
514 return xModel;
517 void Model::renameModel( const Reference<css::frame::XModel>& xCmp,
518 const OUString& sFrom,
519 const OUString& sTo )
521 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
522 if( xModels.is()
523 && xModels->hasByName( sFrom )
524 && ! xModels->hasByName( sTo ) )
526 Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
527 xModel->setID( sTo );
528 xModels->insertByName( sTo, Any( xModel ) );
529 xModels->removeByName( sFrom );
533 void Model::removeModel( const Reference<css::frame::XModel>& xCmp,
534 const OUString& sName )
536 Reference<XNameContainer> xModels = lcl_getModels( xCmp );
537 if( xModels.is()
538 && xModels->hasByName( sName ) )
540 xModels->removeByName( sName );
544 css::uno::Reference<css::xml::dom::XNode> Model::createElement( const css::uno::Reference<css::xml::dom::XNode>& xParent,
545 const OUString& sName )
547 Reference<XNode> xNode;
548 if( xParent.is()
549 && isValidXMLName( sName ) )
551 // TODO: implement proper namespace handling
552 xNode = xParent->getOwnerDocument()->createElement( sName );
554 return xNode;
557 css::uno::Reference<css::xml::dom::XNode> Model::createAttribute( const css::uno::Reference<css::xml::dom::XNode>& xParent,
558 const OUString& sName )
560 Reference<XNode> xNode;
561 Reference<XElement> xElement( xParent, UNO_QUERY );
562 if( xParent.is()
563 && xElement.is()
564 && isValidXMLName( sName ) )
566 // handle case where attribute already exists
567 sal_Int32 nCount = 0;
568 OUString sUniqueName = sName;
569 while( xElement->hasAttribute( sUniqueName ) )
571 nCount++;
572 sUniqueName = sName + OUString::number( nCount );
575 // TODO: implement proper namespace handling
576 xNode = xParent->getOwnerDocument()->createAttribute( sUniqueName );
578 return xNode;
581 css::uno::Reference<css::xml::dom::XNode> Model::renameNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
582 const OUString& sName )
584 // early out if we don't have to change the name
585 if( xNode->getNodeName() == sName )
586 return xNode;
588 // refuse to change name if it's an attribute, and the name is already used
589 if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE
590 && xNode->getParentNode().is()
591 && Reference<XElement>(xNode->getParentNode(), UNO_QUERY_THROW)->hasAttribute( sName ) )
592 return xNode;
594 // note old binding expression so we can adjust bindings below
595 OUString sOldDefaultBindingExpression =
596 getDefaultBindingExpressionForNode( xNode );
598 Reference<XDocument> xDoc = xNode->getOwnerDocument();
599 Reference<XNode> xNew;
600 if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
602 Reference<XElement> xElem = xDoc->createElement( sName );
603 xNew = xElem;
605 // iterate over all attributes and append them to the new element
606 Reference<XElement> xOldElem( xNode, UNO_QUERY );
607 OSL_ENSURE( xNode.is(), "no element?" );
609 Reference<XNamedNodeMap> xMap = xNode->getAttributes();
610 sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
611 // looping until nLength is suspicious wrt removeAttributeNode
612 // presumably shrinking XNamedNodeMap::getLength by 1
613 for( sal_Int32 n = 0; n < nLength; n++ )
615 Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
616 xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
619 // iterate over all children and append them to the new element
620 for( Reference<XNode> xCurrent = xNode->getFirstChild();
621 xCurrent.is();
622 xCurrent = xNode->getFirstChild() )
624 xNew->appendChild( xNode->removeChild( xCurrent ) );
627 xNode->getParentNode()->replaceChild( xNew, xNode );
629 else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
631 // create new attribute
632 Reference<XAttr> xAttr = xDoc->createAttribute( sName );
633 xAttr->setValue( xNode->getNodeValue() );
635 // replace node
636 Reference<XNode> xParent = xNode->getParentNode();
637 xParent->removeChild( xNode );
638 xNew = xParent->appendChild( xAttr );
640 else
642 OSL_FAIL( "can't rename this node type" );
645 // adjust bindings (if necessary):
646 if( xNew.is() )
648 // iterate over bindings and replace default expressions
649 OUString sNewDefaultBindingExpression =
650 getDefaultBindingExpressionForNode( xNew );
651 for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
653 Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
654 mxBindings->Collection<XPropertySet_t>::getItem( n ) );
656 if( pBinding->getBindingExpression()
657 == sOldDefaultBindingExpression )
658 pBinding->setBindingExpression( sNewDefaultBindingExpression );
662 // return node; return old node if renaming failed
663 return xNew.is() ? xNew : xNode;
666 css::uno::Reference< ::css::beans::XPropertySet > Model::getBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
667 sal_Bool bCreate )
669 OSL_ENSURE( xNode.is(), "no node?" );
671 // We will iterate over all bindings and determine the
672 // appropriateness of the respective binding for this node. The
673 // best one will be used. If we don't find any and bCreate is set,
674 // then we will create a suitable binding.
675 rtl::Reference<Binding> pBestBinding;
676 sal_Int32 nBestScore = 0;
678 for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
680 Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
681 mxBindings->Collection<XPropertySet_t>::getItem( n ) );
683 OSL_ENSURE( pBinding != nullptr, "no binding?" );
684 Reference<XNodeList> xNodeList = pBinding->getXNodeList();
686 sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
687 if( nNodes > 0 && xNodeList->item( 0 ) == xNode )
689 // allright, we found a suitable node. Let's determine how
690 // well it fits. Score:
691 // - bind to exactly this node is better than whole nodeset
692 // - simple binding expressions is better than complex ones
693 sal_Int32 nScore = 0;
694 if( nNodes == 1 )
695 nScore ++;
696 if( pBinding->isSimpleBindingExpression() )
697 nScore ++;
699 // if we found a better binding, remember it
700 if( nScore > nBestScore )
702 pBestBinding = pBinding;
703 nBestScore = nScore;
708 // create binding, if none was found and bCreate is set
709 OSL_ENSURE( ( nBestScore == 0 ) == ( pBestBinding == nullptr ),
710 "score != binding?" );
711 if( bCreate && pBestBinding == nullptr )
713 pBestBinding = new Binding();
714 pBestBinding->setBindingExpression(
715 getDefaultBindingExpressionForNode( xNode ) );
716 mxBindings->addItem( pBestBinding );
719 return pBestBinding;
722 void Model::removeBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& )
724 // determine whether suitable binding is still used
727 static OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
729 OUString sResult;
730 OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
731 if ( _rxAttrNode.is() )
733 OUString sValue = _rxAttrNode->getValue();
734 sal_Unicode nQuote = '"';
735 if ( sValue.indexOf( nQuote ) >= 0 )
736 nQuote = '\'';
738 sResult = _rxAttrNode->getName() + "=" + OUStringChar(nQuote) + sValue + OUStringChar(nQuote) + " ";
740 return sResult;
743 static OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
745 OUStringBuffer sResult;
747 // create document fragment
748 Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
749 Reference<XDocumentFragment> xFragment(
750 xDocument->createDocumentFragment() );
751 OSL_ENSURE( xFragment.is(), "xFragment" );
753 sal_Int32 nAttributeNodes = 0;
755 // attach nodelist to fragment
756 sal_Int32 nLength = xNodes->getLength();
757 for( sal_Int32 i = 0; i < nLength; i++ )
759 Reference<XNode> xCurrent = xNodes->item( i );
761 switch ( xCurrent->getNodeType() )
763 case NodeType_DOCUMENT_NODE:
764 // special-case documents: use top-level element instead
765 xCurrent = xCurrent->getFirstChild();
766 break;
767 case NodeType_ATTRIBUTE_NODE:
769 Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
770 if ( xAttr.is() )
772 sResult.append(lcl_serializeForDisplay( xAttr ));
773 ++nAttributeNodes;
776 continue;
778 default:
779 break;
782 // append node
783 xFragment->appendChild( xDocument->importNode( xCurrent, true ) );
785 OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
786 "lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
787 if ( nAttributeNodes )
788 // had only attribute nodes
789 return sResult.makeStringAndClear();
791 // serialize fragment
792 CSerializationAppXML aSerialization;
793 aSerialization.setSource( xFragment );
794 aSerialization.serialize();
796 // copy stream into buffer
797 Reference<XTextInputStream2> xTextInputStream = TextInputStream::create( comphelper::getProcessComponentContext() );
798 xTextInputStream->setInputStream( aSerialization.getInputStream() );
800 /* WORK AROUND for problem in serialization: currently, multiple
801 XML declarations (<?xml...?>) are being written out and we don't
802 want them. When this is fixed, the code below is nice and
803 simple. The current code filters out the declarations.
804 OUString sResult = xTextInputStream->readString( Sequence<sal_Unicode>(),
805 sal_True );
808 // well, the serialization prepends XML header(s) that we need to
809 // remove first.
810 sResult.setLength(0);
811 while( ! xTextInputStream->isEOF() )
813 OUString sLine = xTextInputStream->readLine();
814 if( !sLine.isEmpty()
815 && !sLine.startsWith( "<?xml" ) )
817 sResult.append( sLine + "\n" );
821 return sResult.makeStringAndClear();
824 static OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
826 // error handling first
827 if( ! xResult.is() )
828 return getResource( RID_STR_XFORMS_CANT_EVALUATE );
830 // TODO: localize
831 switch( xResult->getObjectType() )
833 case XPathObjectType_XPATH_BOOLEAN:
834 return OUString::boolean(xResult->getBoolean());
836 case XPathObjectType_XPATH_STRING:
837 return "\"" + xResult->getString() + "\"";
839 case XPathObjectType_XPATH_NODESET:
840 return lcl_serializeForDisplay( xResult->getNodeList() );
842 case XPathObjectType_XPATH_NUMBER:
843 return OUString::number(xResult->getDouble());
845 case XPathObjectType_XPATH_UNDEFINED:
846 case XPathObjectType_XPATH_POINT:
847 case XPathObjectType_XPATH_RANGE:
848 case XPathObjectType_XPATH_LOCATIONSET:
849 case XPathObjectType_XPATH_USERS:
850 case XPathObjectType_XPATH_XSLT_TREE:
851 default:
852 // TODO: localized error message?
853 return OUString();
857 OUString Model::getResultForExpression(
858 const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
859 sal_Bool bIsBindingExpression,
860 const OUString& sExpression )
862 Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
863 if( pBinding == nullptr )
864 throw RuntimeException();
866 // prepare & evaluate expression
867 OUStringBuffer aBuffer;
868 ComputedExpression aExpression;
869 aExpression.setExpression( sExpression );
870 if( bIsBindingExpression )
872 // binding: use binding context and evaluation
873 aExpression.evaluate( pBinding->getEvaluationContext() );
874 aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
876 else
878 // MIP (not binding): iterate over bindings contexts
879 std::vector<EvaluationContext> aContext =
880 pBinding->getMIPEvaluationContexts();
881 for (auto const& elem : aContext)
883 aExpression.evaluate(elem);
884 aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
885 aBuffer.append( '\n' );
888 return aBuffer.makeStringAndClear();
891 sal_Bool Model::isValidXMLName( const OUString& sName )
893 return isValidQName( sName, nullptr );
896 sal_Bool Model::isValidPrefixName( const OUString& sName )
898 return ::isValidPrefixName( sName, nullptr );
901 void Model::setNodeValue(
902 const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
903 const OUString& sValue )
905 setSimpleContent( xNode, sValue );
909 // helper functions from model_helper.hxx
912 void xforms::getInstanceData(
913 const Sequence<PropertyValue>& aValues,
914 OUString* pID,
915 Reference<XDocument>* pInstance,
916 OUString* pURL,
917 bool* pURLOnce )
919 sal_Int32 nValues = aValues.getLength();
920 const PropertyValue* pValues = aValues.getConstArray();
921 for( sal_Int32 n = 0; n < nValues; n++ )
923 const PropertyValue& rValue = pValues[n];
924 if( pID != nullptr && rValue.Name == "ID")
925 rValue.Value >>= *pID;
926 if( pInstance != nullptr && rValue.Name == "Instance")
927 rValue.Value >>= *pInstance;
928 if( pURL != nullptr && rValue.Name == "URL")
929 rValue.Value >>= *pURL;
930 if( pURLOnce != nullptr && rValue.Name == "URLOnce")
931 rValue.Value >>= *pURLOnce;
935 void xforms::setInstanceData(
936 Sequence<PropertyValue>& aSequence,
937 const OUString* _pID,
938 const Reference<XDocument>* _pInstance,
939 const OUString* _pURL,
940 const bool* _pURLOnce )
942 // get old instance data
943 OUString sID;
944 Reference<XDocument> xInstance;
945 OUString sURL;
946 bool bURLOnce = false;
947 getInstanceData( aSequence, &sID, &xInstance, &sURL, &bURLOnce );
948 const OUString* pID = !sID.isEmpty() ? &sID : nullptr;
949 const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : nullptr;
950 const OUString* pURL = !sURL.isEmpty() ? &sURL : nullptr;
951 const bool* pURLOnce = ( bURLOnce && pURL != nullptr ) ? &bURLOnce : nullptr;
953 // determine new instance data
954 if (_pID != nullptr)
955 pID = _pID;
956 if (_pInstance != nullptr)
957 pInstance = _pInstance;
958 if (_pURL != nullptr)
959 pURL = _pURL;
960 if (_pURLOnce != nullptr)
961 pURLOnce = _pURLOnce;
963 // count # of values we want to set
964 sal_Int32 nCount = 0;
965 if (pID != nullptr)
966 ++nCount;
967 if (pInstance != nullptr)
968 ++nCount;
969 if (pURL != nullptr)
970 ++nCount;
971 if (pURLOnce != nullptr)
972 ++nCount;
974 // realloc sequence and enter values;
975 aSequence.realloc( nCount );
976 PropertyValue* pSequence = aSequence.getArray();
977 sal_Int32 nIndex = 0;
978 if(pID != nullptr)
980 pSequence[ nIndex ].Name = "ID";
981 pSequence[ nIndex ].Value <<= *pID;
982 nIndex++;
984 if(pInstance != nullptr)
986 pSequence[ nIndex ].Name = "Instance";
987 pSequence[ nIndex ].Value <<= *pInstance;
988 nIndex++;
990 if(pURL != nullptr)
992 pSequence[ nIndex ].Name = "URL";
993 pSequence[ nIndex ].Value <<= *pURL;
994 nIndex++;
996 if(pURLOnce != nullptr)
998 pSequence[ nIndex ].Name = "URLOnce";
999 pSequence[ nIndex ].Value <<= *pURLOnce;
1000 nIndex++;
1004 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */