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