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_ucb.hxx" 26 27 /************************************************************************** 28 TODO 29 ************************************************************************** 30 31 - optimize transfer command. "Move" should be implementable much more 32 efficient! 33 34 ************************************************************************** 35 36 - Root Folder vs. 'normal' Folder 37 - root doesn't support command 'delete' 38 - root doesn't support command 'insert' 39 - root needs not created via XContentCreator - queryContent with root 40 folder id ( HIERARCHY_ROOT_FOLDER_URL ) always returns a value != 0 41 - root has no parent. 42 43 *************************************************************************/ 44 #include <osl/diagnose.h> 45 46 #include "osl/doublecheckedlocking.h" 47 #include <rtl/ustring.h> 48 #include <rtl/ustring.hxx> 49 #include <com/sun/star/beans/PropertyAttribute.hpp> 50 #include <com/sun/star/beans/PropertyState.hpp> 51 #include <com/sun/star/beans/PropertyValue.hpp> 52 #include <com/sun/star/beans/XPropertyAccess.hpp> 53 #include <com/sun/star/lang/IllegalAccessException.hpp> 54 #include <com/sun/star/sdbc/XRow.hpp> 55 #include <com/sun/star/ucb/ContentInfoAttribute.hpp> 56 #include <com/sun/star/ucb/InsertCommandArgument.hpp> 57 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp> 58 #include <com/sun/star/ucb/MissingPropertiesException.hpp> 59 #include <com/sun/star/ucb/NameClash.hpp> 60 #include <com/sun/star/ucb/NameClashException.hpp> 61 #include <com/sun/star/ucb/OpenCommandArgument2.hpp> 62 #include <com/sun/star/ucb/TransferInfo.hpp> 63 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp> 64 #include <com/sun/star/ucb/XCommandInfo.hpp> 65 #include <com/sun/star/ucb/XPersistentPropertySet.hpp> 66 #include <com/sun/star/uno/Any.hxx> 67 #include <com/sun/star/uno/Sequence.hxx> 68 #include <ucbhelper/contentidentifier.hxx> 69 #include <ucbhelper/propertyvalueset.hxx> 70 #include <ucbhelper/cancelcommandexecution.hxx> 71 #include "hierarchycontent.hxx" 72 #include "hierarchyprovider.hxx" 73 #include "dynamicresultset.hxx" 74 #include "hierarchyuri.hxx" 75 76 #include "../inc/urihelper.hxx" 77 78 using namespace com::sun::star; 79 using namespace hierarchy_ucp; 80 81 //========================================================================= 82 //========================================================================= 83 // 84 // HierarchyContent Implementation. 85 // 86 //========================================================================= 87 //========================================================================= 88 89 // static ( "virtual" ctor ) 90 HierarchyContent* HierarchyContent::create( 91 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 92 HierarchyContentProvider* pProvider, 93 const uno::Reference< ucb::XContentIdentifier >& Identifier ) 94 { 95 // Fail, if content does not exist. 96 HierarchyContentProperties aProps; 97 if ( !loadData( rxSMgr, pProvider, Identifier, aProps ) ) 98 return 0; 99 100 return new HierarchyContent( rxSMgr, pProvider, Identifier, aProps ); 101 } 102 103 //========================================================================= 104 // static ( "virtual" ctor ) 105 HierarchyContent* HierarchyContent::create( 106 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 107 HierarchyContentProvider* pProvider, 108 const uno::Reference< ucb::XContentIdentifier >& Identifier, 109 const ucb::ContentInfo& Info ) 110 { 111 if ( !Info.Type.getLength() ) 112 return 0; 113 114 if ( !Info.Type.equalsAsciiL( 115 RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) ) && 116 !Info.Type.equalsAsciiL( 117 RTL_CONSTASCII_STRINGPARAM( HIERARCHY_LINK_CONTENT_TYPE ) ) ) 118 return 0; 119 120 #if 0 121 // Fail, if content does exist. 122 if ( hasData( rxSMgr, pProvider, Identifier ) ) 123 return 0; 124 #endif 125 126 return new HierarchyContent( rxSMgr, pProvider, Identifier, Info ); 127 } 128 129 //========================================================================= 130 HierarchyContent::HierarchyContent( 131 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 132 HierarchyContentProvider* pProvider, 133 const uno::Reference< ucb::XContentIdentifier >& Identifier, 134 const HierarchyContentProperties& rProps ) 135 : ContentImplHelper( rxSMgr, pProvider, Identifier ), 136 m_aProps( rProps ), 137 m_eState( PERSISTENT ), 138 m_pProvider( pProvider ), 139 m_bCheckedReadOnly( false ), 140 m_bIsReadOnly( true ) 141 { 142 setKind( Identifier ); 143 } 144 145 //========================================================================= 146 HierarchyContent::HierarchyContent( 147 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 148 HierarchyContentProvider* pProvider, 149 const uno::Reference< ucb::XContentIdentifier >& Identifier, 150 const ucb::ContentInfo& Info ) 151 : ContentImplHelper( rxSMgr, pProvider, Identifier ), 152 m_aProps( Info.Type.equalsAsciiL( 153 RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) ) 154 ? HierarchyEntryData::FOLDER 155 : HierarchyEntryData::LINK ), 156 m_eState( TRANSIENT ), 157 m_pProvider( pProvider ), 158 m_bCheckedReadOnly( false ), 159 m_bIsReadOnly( true ) 160 { 161 setKind( Identifier ); 162 } 163 164 //========================================================================= 165 // virtual 166 HierarchyContent::~HierarchyContent() 167 { 168 } 169 170 //========================================================================= 171 // 172 // XInterface methods. 173 // 174 //========================================================================= 175 176 // virtual 177 void SAL_CALL HierarchyContent::acquire() 178 throw( ) 179 { 180 ContentImplHelper::acquire(); 181 } 182 183 //========================================================================= 184 // virtual 185 void SAL_CALL HierarchyContent::release() 186 throw( ) 187 { 188 ContentImplHelper::release(); 189 } 190 191 //========================================================================= 192 // virtual 193 uno::Any SAL_CALL HierarchyContent::queryInterface( const uno::Type & rType ) 194 throw ( uno::RuntimeException ) 195 { 196 uno::Any aRet = ContentImplHelper::queryInterface( rType ); 197 198 if ( !aRet.hasValue() ) 199 { 200 // Note: isReadOnly may be relative expensive. So avoid calling it 201 // unless it is really necessary. 202 aRet = cppu::queryInterface( 203 rType, static_cast< ucb::XContentCreator * >( this ) ); 204 if ( aRet.hasValue() ) 205 { 206 if ( !isFolder() || isReadOnly() ) 207 return uno::Any(); 208 } 209 } 210 211 return aRet; 212 } 213 214 //========================================================================= 215 // 216 // XTypeProvider methods. 217 // 218 //========================================================================= 219 220 XTYPEPROVIDER_COMMON_IMPL( HierarchyContent ); 221 222 //========================================================================= 223 // virtual 224 uno::Sequence< uno::Type > SAL_CALL HierarchyContent::getTypes() 225 throw( uno::RuntimeException ) 226 { 227 cppu::OTypeCollection * pCollection = 0; 228 229 if ( isFolder() && !isReadOnly() ) 230 { 231 static cppu::OTypeCollection* pFolderTypes = 0; 232 233 pCollection = pFolderTypes; 234 if ( !pCollection ) 235 { 236 osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() ); 237 238 pCollection = pFolderTypes; 239 if ( !pCollection ) 240 { 241 static cppu::OTypeCollection aCollection( 242 CPPU_TYPE_REF( lang::XTypeProvider ), 243 CPPU_TYPE_REF( lang::XServiceInfo ), 244 CPPU_TYPE_REF( lang::XComponent ), 245 CPPU_TYPE_REF( ucb::XContent ), 246 CPPU_TYPE_REF( ucb::XCommandProcessor ), 247 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ), 248 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ), 249 CPPU_TYPE_REF( beans::XPropertyContainer ), 250 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ), 251 CPPU_TYPE_REF( container::XChild ), 252 CPPU_TYPE_REF( ucb::XContentCreator ) ); // !! 253 pCollection = &aCollection; 254 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 255 pFolderTypes = pCollection; 256 } 257 } 258 else { 259 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 260 } 261 } 262 else 263 { 264 static cppu::OTypeCollection* pDocumentTypes = 0; 265 266 pCollection = pDocumentTypes; 267 if ( !pCollection ) 268 { 269 osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() ); 270 271 pCollection = pDocumentTypes; 272 if ( !pCollection ) 273 { 274 static cppu::OTypeCollection aCollection( 275 CPPU_TYPE_REF( lang::XTypeProvider ), 276 CPPU_TYPE_REF( lang::XServiceInfo ), 277 CPPU_TYPE_REF( lang::XComponent ), 278 CPPU_TYPE_REF( ucb::XContent ), 279 CPPU_TYPE_REF( ucb::XCommandProcessor ), 280 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ), 281 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ), 282 CPPU_TYPE_REF( beans::XPropertyContainer ), 283 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ), 284 CPPU_TYPE_REF( container::XChild ) ); 285 pCollection = &aCollection; 286 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 287 pDocumentTypes = pCollection; 288 } 289 } 290 else { 291 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 292 } 293 } 294 295 return (*pCollection).getTypes(); 296 } 297 298 //========================================================================= 299 // 300 // XServiceInfo methods. 301 // 302 //========================================================================= 303 304 // virtual 305 rtl::OUString SAL_CALL HierarchyContent::getImplementationName() 306 throw( uno::RuntimeException ) 307 { 308 return rtl::OUString::createFromAscii( 309 "com.sun.star.comp.ucb.HierarchyContent" ); 310 } 311 312 //========================================================================= 313 // virtual 314 uno::Sequence< rtl::OUString > SAL_CALL 315 HierarchyContent::getSupportedServiceNames() 316 throw( uno::RuntimeException ) 317 { 318 uno::Sequence< rtl::OUString > aSNS( 1 ); 319 320 if ( m_eKind == LINK ) 321 aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii( 322 HIERARCHY_LINK_CONTENT_SERVICE_NAME ); 323 else if ( m_eKind == FOLDER ) 324 aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii( 325 HIERARCHY_FOLDER_CONTENT_SERVICE_NAME ); 326 else 327 aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii( 328 HIERARCHY_ROOT_FOLDER_CONTENT_SERVICE_NAME ); 329 330 return aSNS; 331 } 332 333 //========================================================================= 334 // 335 // XContent methods. 336 // 337 //========================================================================= 338 339 // virtual 340 rtl::OUString SAL_CALL HierarchyContent::getContentType() 341 throw( uno::RuntimeException ) 342 { 343 return m_aProps.getContentType(); 344 } 345 346 //========================================================================= 347 // virtual 348 uno::Reference< ucb::XContentIdentifier > SAL_CALL 349 HierarchyContent::getIdentifier() 350 throw( uno::RuntimeException ) 351 { 352 // Transient? 353 if ( m_eState == TRANSIENT ) 354 { 355 // Transient contents have no identifier. 356 return uno::Reference< ucb::XContentIdentifier >(); 357 } 358 359 return ContentImplHelper::getIdentifier(); 360 } 361 362 //========================================================================= 363 // 364 // XCommandProcessor methods. 365 // 366 //========================================================================= 367 368 // virtual 369 uno::Any SAL_CALL HierarchyContent::execute( 370 const ucb::Command& aCommand, 371 sal_Int32 /*CommandId*/, 372 const uno::Reference< ucb::XCommandEnvironment >& Environment ) 373 throw( uno::Exception, 374 ucb::CommandAbortedException, 375 uno::RuntimeException ) 376 { 377 uno::Any aRet; 378 379 if ( aCommand.Name.equalsAsciiL( 380 RTL_CONSTASCII_STRINGPARAM( "getPropertyValues" ) ) ) 381 { 382 ////////////////////////////////////////////////////////////////// 383 // getPropertyValues 384 ////////////////////////////////////////////////////////////////// 385 386 uno::Sequence< beans::Property > Properties; 387 if ( !( aCommand.Argument >>= Properties ) ) 388 { 389 ucbhelper::cancelCommandExecution( 390 uno::makeAny( lang::IllegalArgumentException( 391 rtl::OUString::createFromAscii( 392 "Wrong argument type!" ), 393 static_cast< cppu::OWeakObject * >( this ), 394 -1 ) ), 395 Environment ); 396 // Unreachable 397 } 398 399 aRet <<= getPropertyValues( Properties ); 400 } 401 else if ( aCommand.Name.equalsAsciiL( 402 RTL_CONSTASCII_STRINGPARAM( "setPropertyValues" ) ) ) 403 { 404 ////////////////////////////////////////////////////////////////// 405 // setPropertyValues 406 ////////////////////////////////////////////////////////////////// 407 408 uno::Sequence< beans::PropertyValue > aProperties; 409 if ( !( aCommand.Argument >>= aProperties ) ) 410 { 411 ucbhelper::cancelCommandExecution( 412 uno::makeAny( lang::IllegalArgumentException( 413 rtl::OUString::createFromAscii( 414 "Wrong argument type!" ), 415 static_cast< cppu::OWeakObject * >( this ), 416 -1 ) ), 417 Environment ); 418 // Unreachable 419 } 420 421 if ( !aProperties.getLength() ) 422 { 423 ucbhelper::cancelCommandExecution( 424 uno::makeAny( lang::IllegalArgumentException( 425 rtl::OUString::createFromAscii( 426 "No properties!" ), 427 static_cast< cppu::OWeakObject * >( this ), 428 -1 ) ), 429 Environment ); 430 // Unreachable 431 } 432 433 aRet <<= setPropertyValues( aProperties, Environment ); 434 } 435 else if ( aCommand.Name.equalsAsciiL( 436 RTL_CONSTASCII_STRINGPARAM( "getPropertySetInfo" ) ) ) 437 { 438 ////////////////////////////////////////////////////////////////// 439 // getPropertySetInfo 440 ////////////////////////////////////////////////////////////////// 441 442 aRet <<= getPropertySetInfo( Environment ); 443 } 444 else if ( aCommand.Name.equalsAsciiL( 445 RTL_CONSTASCII_STRINGPARAM( "getCommandInfo" ) ) ) 446 { 447 ////////////////////////////////////////////////////////////////// 448 // getCommandInfo 449 ////////////////////////////////////////////////////////////////// 450 451 aRet <<= getCommandInfo( Environment ); 452 } 453 else if ( aCommand.Name.equalsAsciiL( 454 RTL_CONSTASCII_STRINGPARAM( "open" ) ) && isFolder() ) 455 { 456 ////////////////////////////////////////////////////////////////// 457 // open command for a folder content 458 ////////////////////////////////////////////////////////////////// 459 460 ucb::OpenCommandArgument2 aOpenCommand; 461 if ( !( aCommand.Argument >>= aOpenCommand ) ) 462 { 463 ucbhelper::cancelCommandExecution( 464 uno::makeAny( lang::IllegalArgumentException( 465 rtl::OUString::createFromAscii( 466 "Wrong argument type!" ), 467 static_cast< cppu::OWeakObject * >( this ), 468 -1 ) ), 469 Environment ); 470 // Unreachable 471 } 472 473 uno::Reference< ucb::XDynamicResultSet > xSet 474 = new DynamicResultSet( m_xSMgr, this, aOpenCommand ); 475 aRet <<= xSet; 476 } 477 else if ( aCommand.Name.equalsAsciiL( 478 RTL_CONSTASCII_STRINGPARAM( "insert" ) ) && 479 ( m_eKind != ROOT ) && !isReadOnly() ) 480 { 481 ////////////////////////////////////////////////////////////////// 482 // insert 483 // ( Not available at root folder ) 484 ////////////////////////////////////////////////////////////////// 485 486 ucb::InsertCommandArgument aArg; 487 if ( !( aCommand.Argument >>= aArg ) ) 488 { 489 ucbhelper::cancelCommandExecution( 490 uno::makeAny( lang::IllegalArgumentException( 491 rtl::OUString::createFromAscii( 492 "Wrong argument type!" ), 493 static_cast< cppu::OWeakObject * >( this ), 494 -1 ) ), 495 Environment ); 496 // Unreachable 497 } 498 499 sal_Int32 nNameClash = aArg.ReplaceExisting 500 ? ucb::NameClash::OVERWRITE 501 : ucb::NameClash::ERROR; 502 insert( nNameClash, Environment ); 503 } 504 else if ( aCommand.Name.equalsAsciiL( 505 RTL_CONSTASCII_STRINGPARAM( "delete" ) ) && 506 ( m_eKind != ROOT ) && !isReadOnly() ) 507 { 508 ////////////////////////////////////////////////////////////////// 509 // delete 510 // ( Not available at root folder ) 511 ////////////////////////////////////////////////////////////////// 512 513 sal_Bool bDeletePhysical = sal_False; 514 aCommand.Argument >>= bDeletePhysical; 515 destroy( bDeletePhysical, Environment ); 516 517 // Remove own and all children's persistent data. 518 if ( !removeData() ) 519 { 520 uno::Any aProps 521 = uno::makeAny( 522 beans::PropertyValue( 523 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 524 "Uri")), 525 -1, 526 uno::makeAny(m_xIdentifier-> 527 getContentIdentifier()), 528 beans::PropertyState_DIRECT_VALUE)); 529 ucbhelper::cancelCommandExecution( 530 ucb::IOErrorCode_CANT_WRITE, 531 uno::Sequence< uno::Any >(&aProps, 1), 532 Environment, 533 rtl::OUString::createFromAscii( 534 "Cannot remove persistent data!" ), 535 this ); 536 // Unreachable 537 } 538 539 // Remove own and all children's Additional Core Properties. 540 removeAdditionalPropertySet( sal_True ); 541 } 542 else if ( aCommand.Name.equalsAsciiL( 543 RTL_CONSTASCII_STRINGPARAM( "transfer" ) ) && 544 isFolder() && !isReadOnly() ) 545 { 546 ////////////////////////////////////////////////////////////////// 547 // transfer 548 // ( Not available at link objects ) 549 ////////////////////////////////////////////////////////////////// 550 551 ucb::TransferInfo aInfo; 552 if ( !( aCommand.Argument >>= aInfo ) ) 553 { 554 OSL_ENSURE( sal_False, "Wrong argument type!" ); 555 ucbhelper::cancelCommandExecution( 556 uno::makeAny( lang::IllegalArgumentException( 557 rtl::OUString::createFromAscii( 558 "Wrong argument type!" ), 559 static_cast< cppu::OWeakObject * >( this ), 560 -1 ) ), 561 Environment ); 562 // Unreachable 563 } 564 565 transfer( aInfo, Environment ); 566 } 567 else if ( aCommand.Name.equalsAsciiL( 568 RTL_CONSTASCII_STRINGPARAM( "createNewContent" ) ) && 569 isFolder() && !isReadOnly() ) 570 { 571 ////////////////////////////////////////////////////////////////// 572 // createNewContent 573 // ( Not available at link objects ) 574 ////////////////////////////////////////////////////////////////// 575 576 ucb::ContentInfo aInfo; 577 if ( !( aCommand.Argument >>= aInfo ) ) 578 { 579 OSL_ENSURE( sal_False, "Wrong argument type!" ); 580 ucbhelper::cancelCommandExecution( 581 uno::makeAny( lang::IllegalArgumentException( 582 rtl::OUString::createFromAscii( 583 "Wrong argument type!" ), 584 static_cast< cppu::OWeakObject * >( this ), 585 -1 ) ), 586 Environment ); 587 // Unreachable 588 } 589 590 aRet <<= createNewContent( aInfo ); 591 } 592 else 593 { 594 ////////////////////////////////////////////////////////////////// 595 // Unsupported command 596 ////////////////////////////////////////////////////////////////// 597 598 ucbhelper::cancelCommandExecution( 599 uno::makeAny( ucb::UnsupportedCommandException( 600 rtl::OUString(), 601 static_cast< cppu::OWeakObject * >( this ) ) ), 602 Environment ); 603 // Unreachable 604 } 605 606 return aRet; 607 } 608 609 //========================================================================= 610 // virtual 611 void SAL_CALL HierarchyContent::abort( sal_Int32 /*CommandId*/ ) 612 throw( uno::RuntimeException ) 613 { 614 // @@@ Generally, no action takes much time... 615 } 616 617 //========================================================================= 618 // 619 // XContentCreator methods. 620 // 621 //========================================================================= 622 623 // virtual 624 uno::Sequence< ucb::ContentInfo > SAL_CALL 625 HierarchyContent::queryCreatableContentsInfo() 626 throw( uno::RuntimeException ) 627 { 628 return m_aProps.getCreatableContentsInfo(); 629 } 630 631 //========================================================================= 632 // virtual 633 uno::Reference< ucb::XContent > SAL_CALL 634 HierarchyContent::createNewContent( const ucb::ContentInfo& Info ) 635 throw( uno::RuntimeException ) 636 { 637 if ( isFolder() ) 638 { 639 osl::Guard< osl::Mutex > aGuard( m_aMutex ); 640 641 if ( !Info.Type.getLength() ) 642 return uno::Reference< ucb::XContent >(); 643 644 sal_Bool bCreateFolder = 645 Info.Type.equalsAsciiL( 646 RTL_CONSTASCII_STRINGPARAM( HIERARCHY_FOLDER_CONTENT_TYPE ) ); 647 648 if ( !bCreateFolder && 649 !Info.Type.equalsAsciiL( 650 RTL_CONSTASCII_STRINGPARAM( HIERARCHY_LINK_CONTENT_TYPE ) ) ) 651 return uno::Reference< ucb::XContent >(); 652 653 rtl::OUString aURL = m_xIdentifier->getContentIdentifier(); 654 655 OSL_ENSURE( aURL.getLength() > 0, 656 "HierarchyContent::createNewContent - empty identifier!" ); 657 658 if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() ) 659 aURL += rtl::OUString::createFromAscii( "/" ); 660 661 if ( bCreateFolder ) 662 aURL += rtl::OUString::createFromAscii( "New_Folder" ); 663 else 664 aURL += rtl::OUString::createFromAscii( "New_Link" ); 665 666 uno::Reference< ucb::XContentIdentifier > xId 667 = new ::ucbhelper::ContentIdentifier( m_xSMgr, aURL ); 668 669 return create( m_xSMgr, m_pProvider, xId, Info ); 670 } 671 else 672 { 673 OSL_ENSURE( sal_False, 674 "createNewContent called on non-folder object!" ); 675 return uno::Reference< ucb::XContent >(); 676 } 677 } 678 679 //========================================================================= 680 // virtual 681 rtl::OUString HierarchyContent::getParentURL() 682 { 683 HierarchyUri aUri( m_xIdentifier->getContentIdentifier() ); 684 return aUri.getParentUri(); 685 } 686 687 //========================================================================= 688 //static 689 sal_Bool HierarchyContent::hasData( 690 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 691 HierarchyContentProvider* pProvider, 692 const uno::Reference< ucb::XContentIdentifier >& Identifier ) 693 { 694 rtl::OUString aURL = Identifier->getContentIdentifier(); 695 696 // Am I a root folder? 697 HierarchyUri aUri( aURL ); 698 if ( aUri.isRootFolder() ) 699 { 700 // hasData must always return 'true' for root folder 701 // even if no persistent data exist!!! 702 return sal_True; 703 } 704 705 return HierarchyEntry( rxSMgr, pProvider, aURL ).hasData(); 706 } 707 708 //========================================================================= 709 //static 710 sal_Bool HierarchyContent::loadData( 711 const uno::Reference< lang::XMultiServiceFactory >& rxSMgr, 712 HierarchyContentProvider* pProvider, 713 const uno::Reference< ucb::XContentIdentifier >& Identifier, 714 HierarchyContentProperties& rProps ) 715 { 716 rtl::OUString aURL = Identifier->getContentIdentifier(); 717 718 // Am I a root folder? 719 HierarchyUri aUri( aURL ); 720 if ( aUri.isRootFolder() ) 721 { 722 rProps = HierarchyContentProperties( HierarchyEntryData::FOLDER ); 723 } 724 else 725 { 726 HierarchyEntry aEntry( rxSMgr, pProvider, aURL ); 727 HierarchyEntryData aData; 728 if ( !aEntry.getData( aData ) ) 729 return sal_False; 730 731 rProps = HierarchyContentProperties( aData ); 732 } 733 return sal_True; 734 } 735 736 //========================================================================= 737 sal_Bool HierarchyContent::storeData() 738 { 739 HierarchyEntry aEntry( 740 m_xSMgr, m_pProvider, m_xIdentifier->getContentIdentifier() ); 741 return aEntry.setData( m_aProps.getHierarchyEntryData(), sal_True ); 742 } 743 744 //========================================================================= 745 sal_Bool HierarchyContent::renameData( 746 const uno::Reference< ucb::XContentIdentifier >& xOldId, 747 const uno::Reference< ucb::XContentIdentifier >& xNewId ) 748 { 749 HierarchyEntry aEntry( 750 m_xSMgr, m_pProvider, xOldId->getContentIdentifier() ); 751 return aEntry.move( xNewId->getContentIdentifier(), 752 m_aProps.getHierarchyEntryData() ); 753 } 754 755 //========================================================================= 756 sal_Bool HierarchyContent::removeData() 757 { 758 HierarchyEntry aEntry( 759 m_xSMgr, m_pProvider, m_xIdentifier->getContentIdentifier() ); 760 return aEntry.remove(); 761 } 762 763 //========================================================================= 764 void HierarchyContent::setKind( 765 const uno::Reference< ucb::XContentIdentifier >& Identifier ) 766 { 767 if ( m_aProps.getIsFolder() ) 768 { 769 // Am I a root folder? 770 HierarchyUri aUri( Identifier->getContentIdentifier() ); 771 if ( aUri.isRootFolder() ) 772 m_eKind = ROOT; 773 else 774 m_eKind = FOLDER; 775 } 776 else 777 m_eKind = LINK; 778 } 779 780 //========================================================================= 781 bool HierarchyContent::isReadOnly() 782 { 783 if ( !m_bCheckedReadOnly ) 784 { 785 osl::Guard< osl::Mutex > aGuard( m_aMutex ); 786 if ( !m_bCheckedReadOnly ) 787 { 788 m_bCheckedReadOnly = true; 789 m_bIsReadOnly = true; 790 791 HierarchyUri aUri( m_xIdentifier->getContentIdentifier() ); 792 uno::Reference< lang::XMultiServiceFactory > xConfigProv 793 = m_pProvider->getConfigProvider( aUri.getService() ); 794 if ( xConfigProv.is() ) 795 { 796 uno::Sequence< rtl::OUString > aNames 797 = xConfigProv->getAvailableServiceNames(); 798 sal_Int32 nCount = aNames.getLength(); 799 for ( sal_Int32 n = 0; n < nCount; ++n ) 800 { 801 if ( aNames[ n ].equalsAsciiL( 802 RTL_CONSTASCII_STRINGPARAM( 803 "com.sun.star.ucb.HierarchyDataReadWriteAccess" 804 ) ) ) 805 { 806 m_bIsReadOnly = false; 807 break; 808 } 809 } 810 } 811 } 812 } 813 814 return m_bIsReadOnly; 815 } 816 817 //========================================================================= 818 uno::Reference< ucb::XContentIdentifier > 819 HierarchyContent::makeNewIdentifier( const rtl::OUString& rTitle ) 820 { 821 osl::Guard< osl::Mutex > aGuard( m_aMutex ); 822 823 // Assemble new content identifier... 824 HierarchyUri aUri( m_xIdentifier->getContentIdentifier() ); 825 rtl::OUString aNewURL = aUri.getParentUri(); 826 aNewURL += rtl::OUString::createFromAscii( "/" ); 827 aNewURL += ::ucb_impl::urihelper::encodeSegment( rTitle ); 828 829 return uno::Reference< ucb::XContentIdentifier >( 830 new ::ucbhelper::ContentIdentifier( m_xSMgr, aNewURL ) ); 831 } 832 833 //========================================================================= 834 void HierarchyContent::queryChildren( HierarchyContentRefList& rChildren ) 835 { 836 if ( ( m_eKind != FOLDER ) && ( m_eKind != ROOT ) ) 837 return; 838 839 // Obtain a list with a snapshot of all currently instanciated contents 840 // from provider and extract the contents which are direct children 841 // of this content. 842 843 ::ucbhelper::ContentRefList aAllContents; 844 m_xProvider->queryExistingContents( aAllContents ); 845 846 rtl::OUString aURL = m_xIdentifier->getContentIdentifier(); 847 sal_Int32 nURLPos = aURL.lastIndexOf( '/' ); 848 849 if ( nURLPos != ( aURL.getLength() - 1 ) ) 850 { 851 // No trailing slash found. Append. 852 aURL += rtl::OUString::createFromAscii( "/" ); 853 } 854 855 sal_Int32 nLen = aURL.getLength(); 856 857 ::ucbhelper::ContentRefList::const_iterator it = aAllContents.begin(); 858 ::ucbhelper::ContentRefList::const_iterator end = aAllContents.end(); 859 860 while ( it != end ) 861 { 862 ::ucbhelper::ContentImplHelperRef xChild = (*it); 863 rtl::OUString aChildURL 864 = xChild->getIdentifier()->getContentIdentifier(); 865 866 // Is aURL a prefix of aChildURL? 867 if ( ( aChildURL.getLength() > nLen ) && 868 ( aChildURL.compareTo( aURL, nLen ) == 0 ) ) 869 { 870 sal_Int32 nPos = nLen; 871 nPos = aChildURL.indexOf( '/', nPos ); 872 873 if ( ( nPos == -1 ) || 874 ( nPos == ( aChildURL.getLength() - 1 ) ) ) 875 { 876 // No further slashes/ only a final slash. It's a child! 877 rChildren.push_back( 878 HierarchyContentRef( 879 static_cast< HierarchyContent * >( xChild.get() ) ) ); 880 } 881 } 882 ++it; 883 } 884 } 885 886 //========================================================================= 887 sal_Bool HierarchyContent::exchangeIdentity( 888 const uno::Reference< ucb::XContentIdentifier >& xNewId ) 889 { 890 if ( !xNewId.is() ) 891 return sal_False; 892 893 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); 894 895 uno::Reference< ucb::XContent > xThis = this; 896 897 // Already persistent? 898 if ( m_eState != PERSISTENT ) 899 { 900 OSL_ENSURE( sal_False, 901 "HierarchyContent::exchangeIdentity - Not persistent!" ); 902 return sal_False; 903 } 904 905 // Am I the root folder? 906 if ( m_eKind == ROOT ) 907 { 908 OSL_ENSURE( sal_False, "HierarchyContent::exchangeIdentity - " 909 "Not supported by root folder!" ); 910 return sal_False; 911 } 912 913 // Exchange own identitity. 914 915 // Fail, if a content with given id already exists. 916 if ( !hasData( xNewId ) ) 917 { 918 rtl::OUString aOldURL = m_xIdentifier->getContentIdentifier(); 919 920 aGuard.clear(); 921 if ( exchange( xNewId ) ) 922 { 923 if ( m_eKind == FOLDER ) 924 { 925 // Process instanciated children... 926 927 HierarchyContentRefList aChildren; 928 queryChildren( aChildren ); 929 930 HierarchyContentRefList::const_iterator it = aChildren.begin(); 931 HierarchyContentRefList::const_iterator end = aChildren.end(); 932 933 while ( it != end ) 934 { 935 HierarchyContentRef xChild = (*it); 936 937 // Create new content identifier for the child... 938 uno::Reference< ucb::XContentIdentifier > xOldChildId 939 = xChild->getIdentifier(); 940 rtl::OUString aOldChildURL 941 = xOldChildId->getContentIdentifier(); 942 rtl::OUString aNewChildURL 943 = aOldChildURL.replaceAt( 944 0, 945 aOldURL.getLength(), 946 xNewId->getContentIdentifier() ); 947 uno::Reference< ucb::XContentIdentifier > xNewChildId 948 = new ::ucbhelper::ContentIdentifier( 949 m_xSMgr, aNewChildURL ); 950 951 if ( !xChild->exchangeIdentity( xNewChildId ) ) 952 return sal_False; 953 954 ++it; 955 } 956 } 957 return sal_True; 958 } 959 } 960 961 OSL_ENSURE( sal_False, 962 "HierarchyContent::exchangeIdentity - " 963 "Panic! Cannot exchange identity!" ); 964 return sal_False; 965 } 966 967 //========================================================================= 968 // static 969 uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues( 970 const uno::Reference< lang::XMultiServiceFactory >& rSMgr, 971 const uno::Sequence< beans::Property >& rProperties, 972 const HierarchyContentProperties& rData, 973 HierarchyContentProvider* pProvider, 974 const rtl::OUString& rContentId ) 975 { 976 // Note: Empty sequence means "get values of all supported properties". 977 978 rtl::Reference< ::ucbhelper::PropertyValueSet > xRow 979 = new ::ucbhelper::PropertyValueSet( rSMgr ); 980 981 sal_Int32 nCount = rProperties.getLength(); 982 if ( nCount ) 983 { 984 uno::Reference< beans::XPropertySet > xAdditionalPropSet; 985 sal_Bool bTriedToGetAdditonalPropSet = sal_False; 986 987 const beans::Property* pProps = rProperties.getConstArray(); 988 for ( sal_Int32 n = 0; n < nCount; ++n ) 989 { 990 const beans::Property& rProp = pProps[ n ]; 991 992 // Process Core properties. 993 994 if ( rProp.Name.equalsAsciiL( 995 RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) ) 996 { 997 xRow->appendString ( rProp, rData.getContentType() ); 998 } 999 else if ( rProp.Name.equalsAsciiL( 1000 RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) 1001 { 1002 xRow->appendString ( rProp, rData.getTitle() ); 1003 } 1004 else if ( rProp.Name.equalsAsciiL( 1005 RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ) 1006 { 1007 xRow->appendBoolean( rProp, rData.getIsDocument() ); 1008 } 1009 else if ( rProp.Name.equalsAsciiL( 1010 RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ) 1011 { 1012 xRow->appendBoolean( rProp, rData.getIsFolder() ); 1013 } 1014 else if ( rProp.Name.equalsAsciiL( 1015 RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) ) 1016 { 1017 xRow->appendObject( 1018 rProp, uno::makeAny( rData.getCreatableContentsInfo() ) ); 1019 } 1020 else if ( rProp.Name.equalsAsciiL( 1021 RTL_CONSTASCII_STRINGPARAM( "TargetURL" ) ) ) 1022 { 1023 // TargetURL is only supported by links. 1024 1025 if ( rData.getIsDocument() ) 1026 xRow->appendString( rProp, rData.getTargetURL() ); 1027 else 1028 xRow->appendVoid( rProp ); 1029 } 1030 else 1031 { 1032 // Not a Core Property! Maybe it's an Additional Core Property?! 1033 1034 if ( !bTriedToGetAdditonalPropSet && !xAdditionalPropSet.is() ) 1035 { 1036 xAdditionalPropSet 1037 = uno::Reference< beans::XPropertySet >( 1038 pProvider->getAdditionalPropertySet( rContentId, 1039 sal_False ), 1040 uno::UNO_QUERY ); 1041 bTriedToGetAdditonalPropSet = sal_True; 1042 } 1043 1044 if ( xAdditionalPropSet.is() ) 1045 { 1046 if ( !xRow->appendPropertySetValue( 1047 xAdditionalPropSet, 1048 rProp ) ) 1049 { 1050 // Append empty entry. 1051 xRow->appendVoid( rProp ); 1052 } 1053 } 1054 else 1055 { 1056 // Append empty entry. 1057 xRow->appendVoid( rProp ); 1058 } 1059 } 1060 } 1061 } 1062 else 1063 { 1064 // Append all Core Properties. 1065 xRow->appendString ( 1066 beans::Property( rtl::OUString::createFromAscii( "ContentType" ), 1067 -1, 1068 getCppuType( static_cast< const rtl::OUString * >( 0 ) ), 1069 beans::PropertyAttribute::BOUND 1070 | beans::PropertyAttribute::READONLY ), 1071 rData.getContentType() ); 1072 xRow->appendString ( 1073 beans::Property( rtl::OUString::createFromAscii( "Title" ), 1074 -1, 1075 getCppuType( static_cast< const rtl::OUString * >( 0 ) ), 1076 // @@@ Might actually be read-only! 1077 beans::PropertyAttribute::BOUND ), 1078 rData.getTitle() ); 1079 xRow->appendBoolean( 1080 beans::Property( rtl::OUString::createFromAscii( "IsDocument" ), 1081 -1, 1082 getCppuBooleanType(), 1083 beans::PropertyAttribute::BOUND 1084 | beans::PropertyAttribute::READONLY ), 1085 rData.getIsDocument() ); 1086 xRow->appendBoolean( 1087 beans::Property( rtl::OUString::createFromAscii( "IsFolder" ), 1088 -1, 1089 getCppuBooleanType(), 1090 beans::PropertyAttribute::BOUND 1091 | beans::PropertyAttribute::READONLY ), 1092 rData.getIsFolder() ); 1093 1094 if ( rData.getIsDocument() ) 1095 xRow->appendString( 1096 beans::Property( rtl::OUString::createFromAscii( "TargetURL" ), 1097 -1, 1098 getCppuType( 1099 static_cast< const rtl::OUString * >( 0 ) ), 1100 // @@@ Might actually be read-only! 1101 beans::PropertyAttribute::BOUND ), 1102 rData.getTargetURL() ); 1103 xRow->appendObject( 1104 beans::Property( 1105 rtl::OUString::createFromAscii( "CreatableContentsInfo" ), 1106 -1, 1107 getCppuType( static_cast< 1108 const uno::Sequence< ucb::ContentInfo > * >( 0 ) ), 1109 beans::PropertyAttribute::BOUND 1110 | beans::PropertyAttribute::READONLY ), 1111 uno::makeAny( rData.getCreatableContentsInfo() ) ); 1112 1113 // Append all Additional Core Properties. 1114 1115 uno::Reference< beans::XPropertySet > xSet( 1116 pProvider->getAdditionalPropertySet( rContentId, sal_False ), 1117 uno::UNO_QUERY ); 1118 xRow->appendPropertySet( xSet ); 1119 } 1120 1121 return uno::Reference< sdbc::XRow >( xRow.get() ); 1122 } 1123 1124 //========================================================================= 1125 uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues( 1126 const uno::Sequence< beans::Property >& rProperties ) 1127 { 1128 osl::Guard< osl::Mutex > aGuard( m_aMutex ); 1129 return getPropertyValues( m_xSMgr, 1130 rProperties, 1131 m_aProps, 1132 m_pProvider, 1133 m_xIdentifier->getContentIdentifier() ); 1134 } 1135 1136 //========================================================================= 1137 uno::Sequence< uno::Any > HierarchyContent::setPropertyValues( 1138 const uno::Sequence< beans::PropertyValue >& rValues, 1139 const uno::Reference< ucb::XCommandEnvironment > & xEnv ) 1140 throw( uno::Exception ) 1141 { 1142 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); 1143 1144 uno::Sequence< uno::Any > aRet( rValues.getLength() ); 1145 uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() ); 1146 sal_Int32 nChanged = 0; 1147 1148 beans::PropertyChangeEvent aEvent; 1149 aEvent.Source = static_cast< cppu::OWeakObject * >( this ); 1150 aEvent.Further = sal_False; 1151 // aEvent.PropertyName = 1152 aEvent.PropertyHandle = -1; 1153 // aEvent.OldValue = 1154 // aEvent.NewValue = 1155 1156 const beans::PropertyValue* pValues = rValues.getConstArray(); 1157 sal_Int32 nCount = rValues.getLength(); 1158 1159 uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet; 1160 sal_Bool bTriedToGetAdditonalPropSet = sal_False; 1161 1162 sal_Bool bExchange = sal_False; 1163 rtl::OUString aOldTitle; 1164 rtl::OUString aOldName; 1165 sal_Int32 nTitlePos = -1; 1166 1167 for ( sal_Int32 n = 0; n < nCount; ++n ) 1168 { 1169 const beans::PropertyValue& rValue = pValues[ n ]; 1170 1171 if ( rValue.Name.equalsAsciiL( 1172 RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) ) 1173 { 1174 // Read-only property! 1175 aRet[ n ] <<= lang::IllegalAccessException( 1176 rtl::OUString::createFromAscii( 1177 "Property is read-only!" ), 1178 static_cast< cppu::OWeakObject * >( this ) ); 1179 } 1180 else if ( rValue.Name.equalsAsciiL( 1181 RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ) 1182 { 1183 // Read-only property! 1184 aRet[ n ] <<= lang::IllegalAccessException( 1185 rtl::OUString::createFromAscii( 1186 "Property is read-only!" ), 1187 static_cast< cppu::OWeakObject * >( this ) ); 1188 } 1189 else if ( rValue.Name.equalsAsciiL( 1190 RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ) 1191 { 1192 // Read-only property! 1193 aRet[ n ] <<= lang::IllegalAccessException( 1194 rtl::OUString::createFromAscii( 1195 "Property is read-only!" ), 1196 static_cast< cppu::OWeakObject * >( this ) ); 1197 } 1198 else if ( rValue.Name.equalsAsciiL( 1199 RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) ) 1200 { 1201 // Read-only property! 1202 aRet[ n ] <<= lang::IllegalAccessException( 1203 rtl::OUString::createFromAscii( 1204 "Property is read-only!" ), 1205 static_cast< cppu::OWeakObject * >( this ) ); 1206 } 1207 else if ( rValue.Name.equalsAsciiL( 1208 RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) 1209 { 1210 if ( isReadOnly() ) 1211 { 1212 aRet[ n ] <<= lang::IllegalAccessException( 1213 rtl::OUString::createFromAscii( 1214 "Property is read-only!" ), 1215 static_cast< cppu::OWeakObject * >( this ) ); 1216 } 1217 else 1218 { 1219 rtl::OUString aNewValue; 1220 if ( rValue.Value >>= aNewValue ) 1221 { 1222 // No empty titles! 1223 if ( aNewValue.getLength() > 0 ) 1224 { 1225 if ( aNewValue != m_aProps.getTitle() ) 1226 { 1227 // modified title -> modified URL -> exchange ! 1228 if ( m_eState == PERSISTENT ) 1229 bExchange = sal_True; 1230 1231 aOldTitle = m_aProps.getTitle(); 1232 aOldName = m_aProps.getName(); 1233 1234 m_aProps.setTitle( aNewValue ); 1235 m_aProps.setName( 1236 ::ucb_impl::urihelper::encodeSegment( 1237 aNewValue ) ); 1238 1239 // property change event will be set later... 1240 1241 // remember position within sequence of values 1242 // (for error handling). 1243 nTitlePos = n; 1244 } 1245 } 1246 else 1247 { 1248 aRet[ n ] <<= lang::IllegalArgumentException( 1249 rtl::OUString::createFromAscii( 1250 "Empty title not allowed!" ), 1251 static_cast< cppu::OWeakObject * >( this ), 1252 -1 ); 1253 } 1254 } 1255 else 1256 { 1257 aRet[ n ] <<= beans::IllegalTypeException( 1258 rtl::OUString::createFromAscii( 1259 "Property value has wrong type!" ), 1260 static_cast< cppu::OWeakObject * >( this ) ); 1261 } 1262 } 1263 } 1264 else if ( rValue.Name.equalsAsciiL( 1265 RTL_CONSTASCII_STRINGPARAM( "TargetURL" ) ) ) 1266 { 1267 if ( isReadOnly() ) 1268 { 1269 aRet[ n ] <<= lang::IllegalAccessException( 1270 rtl::OUString::createFromAscii( 1271 "Property is read-only!" ), 1272 static_cast< cppu::OWeakObject * >( this ) ); 1273 } 1274 else 1275 { 1276 // TargetURL is only supported by links. 1277 1278 if ( m_eKind == LINK ) 1279 { 1280 rtl::OUString aNewValue; 1281 if ( rValue.Value >>= aNewValue ) 1282 { 1283 // No empty target URL's! 1284 if ( aNewValue.getLength() > 0 ) 1285 { 1286 if ( aNewValue != m_aProps.getTargetURL() ) 1287 { 1288 aEvent.PropertyName = rValue.Name; 1289 aEvent.OldValue 1290 = uno::makeAny( m_aProps.getTargetURL() ); 1291 aEvent.NewValue 1292 = uno::makeAny( aNewValue ); 1293 1294 aChanges.getArray()[ nChanged ] = aEvent; 1295 1296 m_aProps.setTargetURL( aNewValue ); 1297 nChanged++; 1298 } 1299 } 1300 else 1301 { 1302 aRet[ n ] <<= lang::IllegalArgumentException( 1303 rtl::OUString::createFromAscii( 1304 "Empty target URL not allowed!" ), 1305 static_cast< cppu::OWeakObject * >( this ), 1306 -1 ); 1307 } 1308 } 1309 else 1310 { 1311 aRet[ n ] <<= beans::IllegalTypeException( 1312 rtl::OUString::createFromAscii( 1313 "Property value has wrong type!" ), 1314 static_cast< cppu::OWeakObject * >( this ) ); 1315 } 1316 } 1317 else 1318 { 1319 aRet[ n ] <<= beans::UnknownPropertyException( 1320 rtl::OUString::createFromAscii( 1321 "TargetURL only supported by links!" ), 1322 static_cast< cppu::OWeakObject * >( this ) ); 1323 } 1324 } 1325 } 1326 else 1327 { 1328 // Not a Core Property! Maybe it's an Additional Core Property?! 1329 1330 if ( !bTriedToGetAdditonalPropSet && !xAdditionalPropSet.is() ) 1331 { 1332 xAdditionalPropSet = getAdditionalPropertySet( sal_False ); 1333 bTriedToGetAdditonalPropSet = sal_True; 1334 } 1335 1336 if ( xAdditionalPropSet.is() ) 1337 { 1338 try 1339 { 1340 uno::Any aOldValue = xAdditionalPropSet->getPropertyValue( 1341 rValue.Name ); 1342 if ( aOldValue != rValue.Value ) 1343 { 1344 xAdditionalPropSet->setPropertyValue( 1345 rValue.Name, rValue.Value ); 1346 1347 aEvent.PropertyName = rValue.Name; 1348 aEvent.OldValue = aOldValue; 1349 aEvent.NewValue = rValue.Value; 1350 1351 aChanges.getArray()[ nChanged ] = aEvent; 1352 nChanged++; 1353 } 1354 } 1355 catch ( beans::UnknownPropertyException const & e ) 1356 { 1357 aRet[ n ] <<= e; 1358 } 1359 catch ( lang::WrappedTargetException const & e ) 1360 { 1361 aRet[ n ] <<= e; 1362 } 1363 catch ( beans::PropertyVetoException const & e ) 1364 { 1365 aRet[ n ] <<= e; 1366 } 1367 catch ( lang::IllegalArgumentException const & e ) 1368 { 1369 aRet[ n ] <<= e; 1370 } 1371 } 1372 else 1373 { 1374 aRet[ n ] <<= uno::Exception( 1375 rtl::OUString::createFromAscii( 1376 "No property set for storing the value!" ), 1377 static_cast< cppu::OWeakObject * >( this ) ); 1378 } 1379 } 1380 } 1381 1382 if ( bExchange ) 1383 { 1384 uno::Reference< ucb::XContentIdentifier > xOldId 1385 = m_xIdentifier; 1386 uno::Reference< ucb::XContentIdentifier > xNewId 1387 = makeNewIdentifier( m_aProps.getTitle() ); 1388 1389 aGuard.clear(); 1390 if ( exchangeIdentity( xNewId ) ) 1391 { 1392 // Adapt persistent data. 1393 renameData( xOldId, xNewId ); 1394 1395 // Adapt Additional Core Properties. 1396 renameAdditionalPropertySet( xOldId->getContentIdentifier(), 1397 xNewId->getContentIdentifier(), 1398 sal_True ); 1399 } 1400 else 1401 { 1402 // Roll-back. 1403 m_aProps.setTitle( aOldTitle ); 1404 m_aProps.setName ( aOldName ); 1405 1406 aOldTitle = aOldName = rtl::OUString(); 1407 1408 // Set error . 1409 aRet[ nTitlePos ] <<= uno::Exception( 1410 rtl::OUString::createFromAscii( "Exchange failed!" ), 1411 static_cast< cppu::OWeakObject * >( this ) ); 1412 } 1413 } 1414 1415 if ( aOldTitle.getLength() ) 1416 { 1417 aEvent.PropertyName = rtl::OUString::createFromAscii( "Title" ); 1418 aEvent.OldValue = uno::makeAny( aOldTitle ); 1419 aEvent.NewValue = uno::makeAny( m_aProps.getTitle() ); 1420 1421 aChanges.getArray()[ nChanged ] = aEvent; 1422 nChanged++; 1423 } 1424 1425 if ( nChanged > 0 ) 1426 { 1427 // Save changes, if content was already made persistent. 1428 if ( !bExchange && ( m_eState == PERSISTENT ) ) 1429 { 1430 if ( !storeData() ) 1431 { 1432 uno::Any aProps 1433 = uno::makeAny( 1434 beans::PropertyValue( 1435 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1436 "Uri")), 1437 -1, 1438 uno::makeAny(m_xIdentifier-> 1439 getContentIdentifier()), 1440 beans::PropertyState_DIRECT_VALUE)); 1441 ucbhelper::cancelCommandExecution( 1442 ucb::IOErrorCode_CANT_WRITE, 1443 uno::Sequence< uno::Any >(&aProps, 1), 1444 xEnv, 1445 rtl::OUString::createFromAscii( 1446 "Cannot store persistent data!" ), 1447 this ); 1448 // Unreachable 1449 } 1450 } 1451 1452 aChanges.realloc( nChanged ); 1453 1454 aGuard.clear(); 1455 notifyPropertiesChange( aChanges ); 1456 } 1457 1458 return aRet; 1459 } 1460 1461 //========================================================================= 1462 void HierarchyContent::insert( sal_Int32 nNameClashResolve, 1463 const uno::Reference< 1464 ucb::XCommandEnvironment > & xEnv ) 1465 throw( uno::Exception ) 1466 { 1467 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); 1468 1469 // Am I the root folder? 1470 if ( m_eKind == ROOT ) 1471 { 1472 ucbhelper::cancelCommandExecution( 1473 uno::makeAny( ucb::UnsupportedCommandException( 1474 rtl::OUString::createFromAscii( 1475 "Not supported by root folder!" ), 1476 static_cast< cppu::OWeakObject * >( this ) ) ), 1477 xEnv ); 1478 // Unreachable 1479 } 1480 1481 // Check, if all required properties were set. 1482 if ( m_aProps.getTitle().getLength() == 0 ) 1483 { 1484 uno::Sequence< rtl::OUString > aProps( 1 ); 1485 aProps[ 0 ] = rtl::OUString::createFromAscii( "Title" ); 1486 ucbhelper::cancelCommandExecution( 1487 uno::makeAny( ucb::MissingPropertiesException( 1488 rtl::OUString(), 1489 static_cast< cppu::OWeakObject * >( this ), 1490 aProps ) ), 1491 xEnv ); 1492 // Unreachable 1493 } 1494 1495 // Assemble new content identifier... 1496 1497 uno::Reference< ucb::XContentIdentifier > xId 1498 = makeNewIdentifier( m_aProps.getTitle() ); 1499 1500 // Handle possible name clash... 1501 1502 switch ( nNameClashResolve ) 1503 { 1504 // fail. 1505 case ucb::NameClash::ERROR: 1506 if ( hasData( xId ) ) 1507 { 1508 ucbhelper::cancelCommandExecution( 1509 uno::makeAny( 1510 ucb::NameClashException( 1511 rtl::OUString(), 1512 static_cast< cppu::OWeakObject * >( this ), 1513 task::InteractionClassification_ERROR, 1514 m_aProps.getTitle() ) ), 1515 xEnv ); 1516 // Unreachable 1517 } 1518 break; 1519 1520 // replace existing object. 1521 case ucb::NameClash::OVERWRITE: 1522 break; 1523 1524 // "invent" a new valid title. 1525 case ucb::NameClash::RENAME: 1526 if ( hasData( xId ) ) 1527 { 1528 sal_Int32 nTry = 0; 1529 1530 do 1531 { 1532 rtl::OUString aNewId = xId->getContentIdentifier(); 1533 aNewId += rtl::OUString::createFromAscii( "_" ); 1534 aNewId += rtl::OUString::valueOf( ++nTry ); 1535 xId = new ::ucbhelper::ContentIdentifier( m_xSMgr, aNewId ); 1536 } 1537 while ( hasData( xId ) && ( nTry < 1000 ) ); 1538 1539 if ( nTry == 1000 ) 1540 { 1541 ucbhelper::cancelCommandExecution( 1542 uno::makeAny( 1543 ucb::UnsupportedNameClashException( 1544 rtl::OUString::createFromAscii( 1545 "Unable to resolve name clash!" ), 1546 static_cast< cppu::OWeakObject * >( this ), 1547 nNameClashResolve ) ), 1548 xEnv ); 1549 // Unreachable 1550 } 1551 else 1552 { 1553 rtl::OUString aNewTitle( m_aProps.getTitle() ); 1554 aNewTitle += rtl::OUString::createFromAscii( "_" ); 1555 aNewTitle += rtl::OUString::valueOf( nTry ); 1556 m_aProps.setTitle( aNewTitle ); 1557 } 1558 } 1559 break; 1560 1561 case ucb::NameClash::KEEP: // deprecated 1562 case ucb::NameClash::ASK: 1563 default: 1564 if ( hasData( xId ) ) 1565 { 1566 ucbhelper::cancelCommandExecution( 1567 uno::makeAny( 1568 ucb::UnsupportedNameClashException( 1569 rtl::OUString(), 1570 static_cast< cppu::OWeakObject * >( this ), 1571 nNameClashResolve ) ), 1572 xEnv ); 1573 // Unreachable 1574 } 1575 break; 1576 } 1577 1578 // Identifier changed? 1579 sal_Bool bNewId = ( xId->getContentIdentifier() 1580 != m_xIdentifier->getContentIdentifier() ); 1581 m_xIdentifier = xId; 1582 1583 if ( !storeData() ) 1584 { 1585 uno::Any aProps 1586 = uno::makeAny(beans::PropertyValue( 1587 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1588 "Uri")), 1589 -1, 1590 uno::makeAny(m_xIdentifier-> 1591 getContentIdentifier()), 1592 beans::PropertyState_DIRECT_VALUE)); 1593 ucbhelper::cancelCommandExecution( 1594 ucb::IOErrorCode_CANT_WRITE, 1595 uno::Sequence< uno::Any >(&aProps, 1), 1596 xEnv, 1597 rtl::OUString::createFromAscii( "Cannot store persistent data!" ), 1598 this ); 1599 // Unreachable 1600 } 1601 1602 m_eState = PERSISTENT; 1603 1604 if ( bNewId ) 1605 { 1606 aGuard.clear(); 1607 inserted(); 1608 } 1609 } 1610 1611 //========================================================================= 1612 void HierarchyContent::destroy( sal_Bool bDeletePhysical, 1613 const uno::Reference< 1614 ucb::XCommandEnvironment > & xEnv ) 1615 throw( uno::Exception ) 1616 { 1617 // @@@ take care about bDeletePhysical -> trashcan support 1618 1619 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); 1620 1621 uno::Reference< ucb::XContent > xThis = this; 1622 1623 // Persistent? 1624 if ( m_eState != PERSISTENT ) 1625 { 1626 ucbhelper::cancelCommandExecution( 1627 uno::makeAny( ucb::UnsupportedCommandException( 1628 rtl::OUString::createFromAscii( 1629 "Not persistent!" ), 1630 static_cast< cppu::OWeakObject * >( this ) ) ), 1631 xEnv ); 1632 // Unreachable 1633 } 1634 1635 // Am I the root folder? 1636 if ( m_eKind == ROOT ) 1637 { 1638 ucbhelper::cancelCommandExecution( 1639 uno::makeAny( ucb::UnsupportedCommandException( 1640 rtl::OUString::createFromAscii( 1641 "Not supported by root folder!" ), 1642 static_cast< cppu::OWeakObject * >( this ) ) ), 1643 xEnv ); 1644 // Unreachable 1645 } 1646 1647 m_eState = DEAD; 1648 1649 aGuard.clear(); 1650 deleted(); 1651 1652 if ( m_eKind == FOLDER ) 1653 { 1654 // Process instanciated children... 1655 1656 HierarchyContentRefList aChildren; 1657 queryChildren( aChildren ); 1658 1659 HierarchyContentRefList::const_iterator it = aChildren.begin(); 1660 HierarchyContentRefList::const_iterator end = aChildren.end(); 1661 1662 while ( it != end ) 1663 { 1664 (*it)->destroy( bDeletePhysical, xEnv ); 1665 ++it; 1666 } 1667 } 1668 } 1669 1670 //========================================================================= 1671 void HierarchyContent::transfer( 1672 const ucb::TransferInfo& rInfo, 1673 const uno::Reference< ucb::XCommandEnvironment > & xEnv ) 1674 throw( uno::Exception ) 1675 { 1676 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); 1677 1678 // Persistent? 1679 if ( m_eState != PERSISTENT ) 1680 { 1681 ucbhelper::cancelCommandExecution( 1682 uno::makeAny( ucb::UnsupportedCommandException( 1683 rtl::OUString::createFromAscii( 1684 "Not persistent!" ), 1685 static_cast< cppu::OWeakObject * >( this ) ) ), 1686 xEnv ); 1687 // Unreachable 1688 } 1689 1690 // Is source a hierarchy content? 1691 if ( ( rInfo.SourceURL.getLength() < HIERARCHY_URL_SCHEME_LENGTH + 2 ) || 1692 ( rInfo.SourceURL.compareToAscii( HIERARCHY_URL_SCHEME ":/", 1693 HIERARCHY_URL_SCHEME_LENGTH + 2 ) 1694 != 0 ) ) 1695 { 1696 ucbhelper::cancelCommandExecution( 1697 uno::makeAny( ucb::InteractiveBadTransferURLException( 1698 rtl::OUString(), 1699 static_cast< cppu::OWeakObject * >( this ) ) ), 1700 xEnv ); 1701 // Unreachable 1702 } 1703 1704 // Is source not a parent of me / not me? 1705 rtl::OUString aId = m_xIdentifier->getContentIdentifier(); 1706 sal_Int32 nPos = aId.lastIndexOf( '/' ); 1707 if ( nPos != ( aId.getLength() - 1 ) ) 1708 { 1709 // No trailing slash found. Append. 1710 aId += rtl::OUString::createFromAscii( "/" ); 1711 } 1712 1713 if ( rInfo.SourceURL.getLength() <= aId.getLength() ) 1714 { 1715 if ( aId.compareTo( 1716 rInfo.SourceURL, rInfo.SourceURL.getLength() ) == 0 ) 1717 { 1718 uno::Any aProps 1719 = uno::makeAny(beans::PropertyValue( 1720 rtl::OUString( 1721 RTL_CONSTASCII_USTRINGPARAM("Uri")), 1722 -1, 1723 uno::makeAny(rInfo.SourceURL), 1724 beans::PropertyState_DIRECT_VALUE)); 1725 ucbhelper::cancelCommandExecution( 1726 ucb::IOErrorCode_RECURSIVE, 1727 uno::Sequence< uno::Any >(&aProps, 1), 1728 xEnv, 1729 rtl::OUString::createFromAscii( 1730 "Target is equal to or is a child of source!" ), 1731 this ); 1732 // Unreachable 1733 } 1734 } 1735 1736 ////////////////////////////////////////////////////////////////////// 1737 // 0) Obtain content object for source. 1738 ////////////////////////////////////////////////////////////////////// 1739 1740 uno::Reference< ucb::XContentIdentifier > xId 1741 = new ::ucbhelper::ContentIdentifier( m_xSMgr, rInfo.SourceURL ); 1742 1743 // Note: The static cast is okay here, because its sure that 1744 // m_xProvider is always the HierarchyContentProvider. 1745 rtl::Reference< HierarchyContent > xSource; 1746 1747 try 1748 { 1749 xSource = static_cast< HierarchyContent * >( 1750 m_xProvider->queryContent( xId ).get() ); 1751 } 1752 catch ( ucb::IllegalIdentifierException const & ) 1753 { 1754 // queryContent 1755 } 1756 1757 if ( !xSource.is() ) 1758 { 1759 uno::Any aProps 1760 = uno::makeAny(beans::PropertyValue( 1761 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1762 "Uri")), 1763 -1, 1764 uno::makeAny(xId->getContentIdentifier()), 1765 beans::PropertyState_DIRECT_VALUE)); 1766 ucbhelper::cancelCommandExecution( 1767 ucb::IOErrorCode_CANT_READ, 1768 uno::Sequence< uno::Any >(&aProps, 1), 1769 xEnv, 1770 rtl::OUString::createFromAscii( 1771 "Cannot instanciate source object!" ), 1772 this ); 1773 // Unreachable 1774 } 1775 1776 ////////////////////////////////////////////////////////////////////// 1777 // 1) Create new child content. 1778 ////////////////////////////////////////////////////////////////////// 1779 1780 rtl::OUString aType = xSource->isFolder() 1781 ? rtl::OUString::createFromAscii( HIERARCHY_FOLDER_CONTENT_TYPE ) 1782 : rtl::OUString::createFromAscii( HIERARCHY_LINK_CONTENT_TYPE ); 1783 ucb::ContentInfo aContentInfo; 1784 aContentInfo.Type = aType; 1785 aContentInfo.Attributes = 0; 1786 1787 // Note: The static cast is okay here, because its sure that 1788 // createNewContent always creates a HierarchyContent. 1789 rtl::Reference< HierarchyContent > xTarget 1790 = static_cast< HierarchyContent * >( 1791 createNewContent( aContentInfo ).get() ); 1792 if ( !xTarget.is() ) 1793 { 1794 uno::Any aProps 1795 = uno::makeAny(beans::PropertyValue( 1796 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1797 "Folder")), 1798 -1, 1799 uno::makeAny(aId), 1800 beans::PropertyState_DIRECT_VALUE)); 1801 ucbhelper::cancelCommandExecution( 1802 ucb::IOErrorCode_CANT_CREATE, 1803 uno::Sequence< uno::Any >(&aProps, 1), 1804 xEnv, 1805 rtl::OUString::createFromAscii( 1806 "XContentCreator::createNewContent failed!" ), 1807 this ); 1808 // Unreachable 1809 } 1810 1811 ////////////////////////////////////////////////////////////////////// 1812 // 2) Copy data from source content to child content. 1813 ////////////////////////////////////////////////////////////////////// 1814 1815 uno::Sequence< beans::Property > aSourceProps 1816 = xSource->getPropertySetInfo( xEnv )->getProperties(); 1817 sal_Int32 nCount = aSourceProps.getLength(); 1818 1819 if ( nCount ) 1820 { 1821 sal_Bool bHadTitle = ( rInfo.NewTitle.getLength() == 0 ); 1822 1823 // Get all source values. 1824 uno::Reference< sdbc::XRow > xRow 1825 = xSource->getPropertyValues( aSourceProps ); 1826 1827 uno::Sequence< beans::PropertyValue > aValues( nCount ); 1828 beans::PropertyValue* pValues = aValues.getArray(); 1829 1830 const beans::Property* pProps = aSourceProps.getConstArray(); 1831 for ( sal_Int32 n = 0; n < nCount; ++n ) 1832 { 1833 const beans::Property& rProp = pProps[ n ]; 1834 beans::PropertyValue& rValue = pValues[ n ]; 1835 1836 rValue.Name = rProp.Name; 1837 rValue.Handle = rProp.Handle; 1838 1839 if ( !bHadTitle && rProp.Name.equalsAsciiL( 1840 RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) 1841 { 1842 // Set new title instead of original. 1843 bHadTitle = sal_True; 1844 rValue.Value <<= rInfo.NewTitle; 1845 } 1846 else 1847 rValue.Value = xRow->getObject( 1848 n + 1, 1849 uno::Reference< container::XNameAccess >() ); 1850 1851 rValue.State = beans::PropertyState_DIRECT_VALUE; 1852 1853 if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE ) 1854 { 1855 // Add Additional Core Property. 1856 try 1857 { 1858 xTarget->addProperty( rProp.Name, 1859 rProp.Attributes, 1860 rValue.Value ); 1861 } 1862 catch ( beans::PropertyExistException const & ) 1863 { 1864 } 1865 catch ( beans::IllegalTypeException const & ) 1866 { 1867 } 1868 catch ( lang::IllegalArgumentException const & ) 1869 { 1870 } 1871 } 1872 } 1873 1874 // Set target values. 1875 xTarget->setPropertyValues( aValues, xEnv ); 1876 } 1877 1878 ////////////////////////////////////////////////////////////////////// 1879 // 3) Commit (insert) child. 1880 ////////////////////////////////////////////////////////////////////// 1881 1882 xTarget->insert( rInfo.NameClash, xEnv ); 1883 1884 ////////////////////////////////////////////////////////////////////// 1885 // 4) Transfer (copy) children of source. 1886 ////////////////////////////////////////////////////////////////////// 1887 1888 if ( xSource->isFolder() ) 1889 { 1890 HierarchyEntry aFolder( 1891 m_xSMgr, m_pProvider, xId->getContentIdentifier() ); 1892 HierarchyEntry::iterator it; 1893 1894 while ( aFolder.next( it ) ) 1895 { 1896 const HierarchyEntryData& rResult = *it; 1897 1898 rtl::OUString aChildId = xId->getContentIdentifier(); 1899 if ( ( aChildId.lastIndexOf( '/' ) + 1 ) != aChildId.getLength() ) 1900 aChildId += rtl::OUString::createFromAscii( "/" ); 1901 1902 aChildId += rResult.getName(); 1903 1904 ucb::TransferInfo aInfo; 1905 aInfo.MoveData = sal_False; 1906 aInfo.NewTitle = rtl::OUString(); 1907 aInfo.SourceURL = aChildId; 1908 aInfo.NameClash = rInfo.NameClash; 1909 1910 // Transfer child to target. 1911 xTarget->transfer( aInfo, xEnv ); 1912 } 1913 } 1914 1915 ////////////////////////////////////////////////////////////////////// 1916 // 5) Destroy source ( when moving only ) . 1917 ////////////////////////////////////////////////////////////////////// 1918 1919 if ( rInfo.MoveData ) 1920 { 1921 xSource->destroy( sal_True, xEnv ); 1922 1923 // Remove all persistent data of source and its children. 1924 if ( !xSource->removeData() ) 1925 { 1926 uno::Any aProps 1927 = uno::makeAny( 1928 beans::PropertyValue( 1929 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1930 "Uri")), 1931 -1, 1932 uno::makeAny( 1933 xSource->m_xIdentifier-> 1934 getContentIdentifier()), 1935 beans::PropertyState_DIRECT_VALUE)); 1936 ucbhelper::cancelCommandExecution( 1937 ucb::IOErrorCode_CANT_WRITE, 1938 uno::Sequence< uno::Any >(&aProps, 1), 1939 xEnv, 1940 rtl::OUString::createFromAscii( 1941 "Cannot remove persistent data of source object!" ), 1942 this ); 1943 // Unreachable 1944 } 1945 1946 // Remove own and all children's Additional Core Properties. 1947 xSource->removeAdditionalPropertySet( sal_True ); 1948 } 1949 } 1950 1951 //========================================================================= 1952 //========================================================================= 1953 // 1954 // HierarchyContentProperties Implementation. 1955 // 1956 //========================================================================= 1957 //========================================================================= 1958 1959 uno::Sequence< ucb::ContentInfo > 1960 HierarchyContentProperties::getCreatableContentsInfo() const 1961 { 1962 if ( getIsFolder() ) 1963 { 1964 uno::Sequence< ucb::ContentInfo > aSeq( 2 ); 1965 1966 // Folder. 1967 aSeq.getArray()[ 0 ].Type 1968 = rtl::OUString::createFromAscii( HIERARCHY_FOLDER_CONTENT_TYPE ); 1969 aSeq.getArray()[ 0 ].Attributes 1970 = ucb::ContentInfoAttribute::KIND_FOLDER; 1971 1972 uno::Sequence< beans::Property > aFolderProps( 1 ); 1973 aFolderProps.getArray()[ 0 ] = beans::Property( 1974 rtl::OUString::createFromAscii( "Title" ), 1975 -1, 1976 getCppuType( static_cast< const rtl::OUString * >( 0 ) ), 1977 beans::PropertyAttribute::BOUND ); 1978 aSeq.getArray()[ 0 ].Properties = aFolderProps; 1979 1980 // Link. 1981 aSeq.getArray()[ 1 ].Type 1982 = rtl::OUString::createFromAscii( HIERARCHY_LINK_CONTENT_TYPE ); 1983 aSeq.getArray()[ 1 ].Attributes 1984 = ucb::ContentInfoAttribute::KIND_LINK; 1985 1986 uno::Sequence< beans::Property > aLinkProps( 2 ); 1987 aLinkProps.getArray()[ 0 ] = beans::Property( 1988 rtl::OUString::createFromAscii( "Title" ), 1989 -1, 1990 getCppuType( static_cast< const rtl::OUString * >( 0 ) ), 1991 beans::PropertyAttribute::BOUND ); 1992 aLinkProps.getArray()[ 1 ] = beans::Property( 1993 rtl::OUString::createFromAscii( "TargetURL" ), 1994 -1, 1995 getCppuType( static_cast< const rtl::OUString * >( 0 ) ), 1996 beans::PropertyAttribute::BOUND ); 1997 aSeq.getArray()[ 1 ].Properties = aLinkProps; 1998 1999 return aSeq; 2000 } 2001 else 2002 { 2003 return uno::Sequence< ucb::ContentInfo >( 0 ); 2004 } 2005 } 2006