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