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 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_file.hxx" 24 #include <rtl/uri.hxx> 25 #include <rtl/ustrbuf.hxx> 26 #include <osl/file.hxx> 27 28 #include "osl/diagnose.h" 29 #include <com/sun/star/ucb/OpenMode.hpp> 30 #ifndef _COM_SUN_STAR_BEANS_PROPERTYATTRIBBUTE_HPP_ 31 #include <com/sun/star/beans/PropertyAttribute.hpp> 32 #endif 33 #include <com/sun/star/ucb/XProgressHandler.hpp> 34 #include <com/sun/star/task/XInteractionHandler.hpp> 35 #include <com/sun/star/io/XActiveDataStreamer.hpp> 36 #include <com/sun/star/io/XOutputStream.hpp> 37 #include <com/sun/star/ucb/NumberedSortingInfo.hpp> 38 #include <com/sun/star/io/XActiveDataSink.hpp> 39 #include <com/sun/star/beans/PropertyChangeEvent.hpp> 40 #include <com/sun/star/beans/PropertySetInfoChange.hpp> 41 #include <com/sun/star/ucb/ContentAction.hpp> 42 #include <com/sun/star/ucb/NameClash.hpp> 43 #include "filglob.hxx" 44 #include "filid.hxx" 45 #include "filrow.hxx" 46 #include "bc.hxx" 47 #include "prov.hxx" 48 #ifndef _FILERROR_HXX_ 49 #include "filerror.hxx" 50 #endif 51 #include "filinsreq.hxx" 52 53 54 using namespace fileaccess; 55 using namespace com::sun::star; 56 using namespace com::sun::star::uno; 57 using namespace com::sun::star::ucb; 58 59 // PropertyListeners 60 61 62 typedef cppu::OMultiTypeInterfaceContainerHelperVar< rtl::OUString,hashOUString,equalOUString > 63 PropertyListeners_impl; 64 65 class fileaccess::PropertyListeners 66 : public PropertyListeners_impl 67 { 68 public: 69 PropertyListeners( ::osl::Mutex& aMutex ) 70 : PropertyListeners_impl( aMutex ) 71 { 72 } 73 }; 74 75 76 /****************************************************************************************/ 77 /* BaseContent */ 78 /****************************************************************************************/ 79 80 //////////////////////////////////////////////////////////////////////////////// 81 // Private Constructor for just inserted Contents 82 83 BaseContent::BaseContent( shell* pMyShell, 84 const rtl::OUString& parentName, 85 sal_Bool bFolder ) 86 : m_pMyShell( pMyShell ), 87 m_xContentIdentifier( 0 ), 88 m_aUncPath( parentName ), 89 m_bFolder( bFolder ), 90 m_nState( JustInserted ), 91 m_pDisposeEventListeners( 0 ), 92 m_pContentEventListeners( 0 ), 93 m_pPropertySetInfoChangeListeners( 0 ), 94 m_pPropertyListener( 0 ) 95 { 96 m_pMyShell->m_pProvider->acquire(); 97 // No registering, since we have no name 98 } 99 100 101 //////////////////////////////////////////////////////////////////////////////// 102 // Constructor for full featured Contents 103 104 BaseContent::BaseContent( shell* pMyShell, 105 const Reference< XContentIdentifier >& xContentIdentifier, 106 const rtl::OUString& aUncPath ) 107 : m_pMyShell( pMyShell ), 108 m_xContentIdentifier( xContentIdentifier ), 109 m_aUncPath( aUncPath ), 110 m_bFolder( false ), 111 m_nState( FullFeatured ), 112 m_pDisposeEventListeners( 0 ), 113 m_pContentEventListeners( 0 ), 114 m_pPropertySetInfoChangeListeners( 0 ), 115 m_pPropertyListener( 0 ) 116 { 117 m_pMyShell->m_pProvider->acquire(); 118 m_pMyShell->registerNotifier( m_aUncPath,this ); 119 m_pMyShell->insertDefaultProperties( m_aUncPath ); 120 } 121 122 123 BaseContent::~BaseContent( ) 124 { 125 if( ( m_nState & FullFeatured ) || ( m_nState & Deleted ) ) 126 { 127 m_pMyShell->deregisterNotifier( m_aUncPath,this ); 128 } 129 m_pMyShell->m_pProvider->release(); 130 131 delete m_pDisposeEventListeners; 132 delete m_pContentEventListeners; 133 delete m_pPropertyListener; 134 delete m_pPropertySetInfoChangeListeners; 135 } 136 137 138 ////////////////////////////////////////////////////////////////////////// 139 // XInterface 140 ////////////////////////////////////////////////////////////////////////// 141 142 void SAL_CALL 143 BaseContent::acquire( void ) 144 throw() 145 { 146 OWeakObject::acquire(); 147 } 148 149 150 void SAL_CALL 151 BaseContent::release( void ) 152 throw() 153 { 154 OWeakObject::release(); 155 } 156 157 158 Any SAL_CALL 159 BaseContent::queryInterface( const Type& rType ) 160 throw( RuntimeException ) 161 { 162 Any aRet = cppu::queryInterface( rType, 163 SAL_STATIC_CAST( lang::XComponent*, this ), 164 SAL_STATIC_CAST( lang::XTypeProvider*, this ), 165 SAL_STATIC_CAST( lang::XServiceInfo*, this ), 166 SAL_STATIC_CAST( XCommandProcessor*, this ), 167 SAL_STATIC_CAST( container::XChild*, this ), 168 SAL_STATIC_CAST( beans::XPropertiesChangeNotifier*, this ), 169 SAL_STATIC_CAST( beans::XPropertyContainer*, this ), 170 SAL_STATIC_CAST( XContentCreator*,this ), 171 SAL_STATIC_CAST( beans::XPropertySetInfoChangeNotifier*, this ), 172 SAL_STATIC_CAST( XContent*,this) ); 173 return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); 174 } 175 176 177 178 179 ////////////////////////////////////////////////////////////////////////////////////////// 180 // XComponent 181 //////////////////////////////////////////////////////////////////////////////////////// 182 183 void SAL_CALL 184 BaseContent::addEventListener( const Reference< lang::XEventListener >& Listener ) 185 throw( RuntimeException ) 186 { 187 osl::MutexGuard aGuard( m_aMutex ); 188 189 if ( ! m_pDisposeEventListeners ) 190 m_pDisposeEventListeners = 191 new cppu::OInterfaceContainerHelper( m_aEventListenerMutex ); 192 193 m_pDisposeEventListeners->addInterface( Listener ); 194 } 195 196 197 void SAL_CALL 198 BaseContent::removeEventListener( const Reference< lang::XEventListener >& Listener ) 199 throw( RuntimeException ) 200 { 201 osl::MutexGuard aGuard( m_aMutex ); 202 203 if ( m_pDisposeEventListeners ) 204 m_pDisposeEventListeners->removeInterface( Listener ); 205 } 206 207 208 void SAL_CALL 209 BaseContent::dispose() 210 throw( RuntimeException ) 211 { 212 lang::EventObject aEvt; 213 cppu::OInterfaceContainerHelper* pDisposeEventListeners; 214 cppu::OInterfaceContainerHelper* pContentEventListeners; 215 cppu::OInterfaceContainerHelper* pPropertySetInfoChangeListeners; 216 PropertyListeners* pPropertyListener; 217 218 { 219 osl::MutexGuard aGuard( m_aMutex ); 220 aEvt.Source = static_cast< XContent* >( this ); 221 222 223 pDisposeEventListeners = 224 m_pDisposeEventListeners, m_pDisposeEventListeners = 0; 225 226 pContentEventListeners = 227 m_pContentEventListeners, m_pContentEventListeners = 0; 228 229 pPropertySetInfoChangeListeners = 230 m_pPropertySetInfoChangeListeners, 231 m_pPropertySetInfoChangeListeners = 0; 232 233 pPropertyListener = 234 m_pPropertyListener, m_pPropertyListener = 0; 235 } 236 237 if ( pDisposeEventListeners && pDisposeEventListeners->getLength() ) 238 pDisposeEventListeners->disposeAndClear( aEvt ); 239 240 if ( pContentEventListeners && pContentEventListeners->getLength() ) 241 pContentEventListeners->disposeAndClear( aEvt ); 242 243 if( pPropertyListener ) 244 pPropertyListener->disposeAndClear( aEvt ); 245 246 if( pPropertySetInfoChangeListeners ) 247 pPropertySetInfoChangeListeners->disposeAndClear( aEvt ); 248 249 delete pDisposeEventListeners; 250 delete pContentEventListeners; 251 delete pPropertyListener; 252 delete pPropertySetInfoChangeListeners; 253 } 254 255 256 257 ////////////////////////////////////////////////////////////////////////////////////////// 258 // XServiceInfo 259 ////////////////////////////////////////////////////////////////////////////////////////// 260 261 rtl::OUString SAL_CALL 262 BaseContent::getImplementationName() 263 throw( RuntimeException) 264 { 265 return rtl::OUString::createFromAscii( "com.sun.star.comp.ucb.FileContent" ); 266 } 267 268 269 270 sal_Bool SAL_CALL 271 BaseContent::supportsService( const rtl::OUString& ServiceName ) 272 throw( RuntimeException) 273 { 274 if( ServiceName.compareToAscii( "com.sun.star.ucb.FileContent" ) == 0 ) 275 return true; 276 else 277 return false; 278 } 279 280 281 282 Sequence< rtl::OUString > SAL_CALL 283 BaseContent::getSupportedServiceNames() 284 throw( RuntimeException ) 285 { 286 Sequence< rtl::OUString > ret( 1 ); 287 ret[0] = rtl::OUString::createFromAscii( "com.sun.star.ucb.FileContent" ); 288 return ret; 289 } 290 291 292 293 ////////////////////////////////////////////////////////////////////////////////////////// 294 // XTypeProvider 295 ////////////////////////////////////////////////////////////////////////////////////////// 296 297 XTYPEPROVIDER_IMPL_10( BaseContent, 298 lang::XComponent, 299 lang::XTypeProvider, 300 lang::XServiceInfo, 301 XCommandProcessor, 302 XContentCreator, 303 XContent, 304 container::XChild, 305 beans::XPropertiesChangeNotifier, 306 beans::XPropertyContainer, 307 beans::XPropertySetInfoChangeNotifier ) 308 309 310 ////////////////////////////////////////////////////////////////////////////////////////// 311 // XCommandProcessor 312 ////////////////////////////////////////////////////////////////////////////////////////// 313 314 sal_Int32 SAL_CALL 315 BaseContent::createCommandIdentifier( void ) 316 throw( RuntimeException ) 317 { 318 return m_pMyShell->getCommandId(); 319 } 320 321 322 void SAL_CALL 323 BaseContent::abort( sal_Int32 CommandId ) 324 throw( RuntimeException ) 325 { 326 m_pMyShell->abort( CommandId ); 327 } 328 329 330 Any SAL_CALL 331 BaseContent::execute( const Command& aCommand, 332 sal_Int32 CommandId, 333 const Reference< XCommandEnvironment >& Environment ) 334 throw( Exception, 335 CommandAbortedException, 336 RuntimeException ) 337 { 338 if( ! CommandId ) 339 // A Command with commandid zero cannot be aborted 340 CommandId = createCommandIdentifier(); 341 342 m_pMyShell->startTask( CommandId, 343 Environment ); 344 345 Any aAny; 346 347 if( ! aCommand.Name.compareToAscii( "getPropertySetInfo" ) ) // No exceptions 348 { 349 aAny <<= getPropertySetInfo( CommandId ); 350 } 351 else if( ! aCommand.Name.compareToAscii( "getCommandInfo" ) ) // no exceptions 352 { 353 aAny <<= getCommandInfo(); 354 } 355 else if( ! aCommand.Name.compareToAscii( "setPropertyValues" ) ) 356 { 357 Sequence< beans::PropertyValue > sPropertyValues; 358 359 if( ! ( aCommand.Argument >>= sPropertyValues ) ) 360 m_pMyShell->installError( CommandId, 361 TASKHANDLING_WRONG_SETPROPERTYVALUES_ARGUMENT ); 362 else 363 aAny <<= setPropertyValues( CommandId,sPropertyValues ); // calls endTask by itself 364 } 365 else if( ! aCommand.Name.compareToAscii( "getPropertyValues" ) ) 366 { 367 Sequence< beans::Property > ListOfRequestedProperties; 368 369 if( ! ( aCommand.Argument >>= ListOfRequestedProperties ) ) 370 m_pMyShell->installError( CommandId, 371 TASKHANDLING_WRONG_GETPROPERTYVALUES_ARGUMENT ); 372 else 373 aAny <<= getPropertyValues( CommandId, 374 ListOfRequestedProperties ); 375 } 376 else if( ! aCommand.Name.compareToAscii( "open" ) ) 377 { 378 OpenCommandArgument2 aOpenArgument; 379 if( ! ( aCommand.Argument >>= aOpenArgument ) ) 380 m_pMyShell->installError( CommandId, 381 TASKHANDLING_WRONG_OPEN_ARGUMENT ); 382 else 383 { 384 Reference< XDynamicResultSet > result = open( CommandId,aOpenArgument ); 385 if( result.is() ) 386 aAny <<= result; 387 } 388 } 389 else if( ! aCommand.Name.compareToAscii( "delete" ) ) 390 { 391 if( ! aCommand.Argument.has< sal_Bool >() ) 392 m_pMyShell->installError( CommandId, 393 TASKHANDLING_WRONG_DELETE_ARGUMENT ); 394 else 395 deleteContent( CommandId ); 396 } 397 else if( ! aCommand.Name.compareToAscii( "transfer" ) ) 398 { 399 TransferInfo aTransferInfo; 400 if( ! ( aCommand.Argument >>= aTransferInfo ) ) 401 m_pMyShell->installError( CommandId, 402 TASKHANDLING_WRONG_TRANSFER_ARGUMENT ); 403 else 404 transfer( CommandId, aTransferInfo ); 405 } 406 else if( ! aCommand.Name.compareToAscii( "insert" ) ) 407 { 408 InsertCommandArgument aInsertArgument; 409 if( ! ( aCommand.Argument >>= aInsertArgument ) ) 410 m_pMyShell->installError( CommandId, 411 TASKHANDLING_WRONG_INSERT_ARGUMENT ); 412 else 413 insert( CommandId,aInsertArgument ); 414 } 415 else if( ! aCommand.Name.compareToAscii( "getCasePreservingURL" ) ) 416 { 417 Sequence< beans::Property > seq(1); 418 seq[0] = beans::Property( 419 rtl::OUString::createFromAscii("CasePreservingURL"), 420 -1, 421 getCppuType( static_cast< sal_Bool* >(0) ), 422 0 ); 423 Reference< sdbc::XRow > xRow = getPropertyValues( CommandId,seq ); 424 rtl::OUString CasePreservingURL = xRow->getString(1); 425 if(!xRow->wasNull()) 426 aAny <<= CasePreservingURL; 427 } 428 else if( ! aCommand.Name.compareToAscii( "createNewContent" ) ) 429 { 430 ucb::ContentInfo aArg; 431 if ( !( aCommand.Argument >>= aArg ) ) 432 m_pMyShell->installError( CommandId, 433 TASKHANDLING_WRONG_CREATENEWCONTENT_ARGUMENT ); 434 else 435 aAny <<= createNewContent( aArg ); 436 } 437 else 438 m_pMyShell->installError( CommandId, 439 TASKHANDLER_UNSUPPORTED_COMMAND ); 440 441 442 // This is the only function allowed to throw an exception 443 endTask( CommandId ); 444 445 return aAny; 446 } 447 448 449 450 void SAL_CALL 451 BaseContent::addPropertiesChangeListener( 452 const Sequence< rtl::OUString >& PropertyNames, 453 const Reference< beans::XPropertiesChangeListener >& Listener ) 454 throw( RuntimeException ) 455 { 456 if( ! Listener.is() ) 457 return; 458 459 osl::MutexGuard aGuard( m_aMutex ); 460 461 if( ! m_pPropertyListener ) 462 m_pPropertyListener = new PropertyListeners( m_aEventListenerMutex ); 463 464 465 if( PropertyNames.getLength() == 0 ) 466 m_pPropertyListener->addInterface( rtl::OUString(),Listener ); 467 else 468 { 469 Reference< beans::XPropertySetInfo > xProp = m_pMyShell->info_p( m_aUncPath ); 470 for( sal_Int32 i = 0; i < PropertyNames.getLength(); ++i ) 471 if( xProp->hasPropertyByName( PropertyNames[i] ) ) 472 m_pPropertyListener->addInterface( PropertyNames[i],Listener ); 473 } 474 } 475 476 477 void SAL_CALL 478 BaseContent::removePropertiesChangeListener( const Sequence< rtl::OUString >& PropertyNames, 479 const Reference< beans::XPropertiesChangeListener >& Listener ) 480 throw( RuntimeException ) 481 { 482 if( ! Listener.is() ) 483 return; 484 485 osl::MutexGuard aGuard( m_aMutex ); 486 487 if( ! m_pPropertyListener ) 488 return; 489 490 for( sal_Int32 i = 0; i < PropertyNames.getLength(); ++i ) 491 m_pPropertyListener->removeInterface( PropertyNames[i],Listener ); 492 493 m_pPropertyListener->removeInterface( rtl::OUString(), Listener ); 494 } 495 496 497 ///////////////////////////////////////////////////////////////////////////////////////// 498 // XContent 499 ///////////////////////////////////////////////////////////////////////////////////////// 500 501 Reference< ucb::XContentIdentifier > SAL_CALL 502 BaseContent::getIdentifier() 503 throw( RuntimeException ) 504 { 505 return m_xContentIdentifier; 506 } 507 508 509 rtl::OUString SAL_CALL 510 BaseContent::getContentType() 511 throw( RuntimeException ) 512 { 513 if( !( m_nState & Deleted ) ) 514 { 515 if( m_nState & JustInserted ) 516 { 517 if ( m_bFolder ) 518 return m_pMyShell->FolderContentType; 519 else 520 return m_pMyShell->FileContentType; 521 } 522 else 523 { 524 try 525 { 526 // Who am I ? 527 Sequence< beans::Property > seq(1); 528 seq[0] = beans::Property( rtl::OUString::createFromAscii("IsDocument"), 529 -1, 530 getCppuType( static_cast< sal_Bool* >(0) ), 531 0 ); 532 Reference< sdbc::XRow > xRow = getPropertyValues( -1,seq ); 533 sal_Bool IsDocument = xRow->getBoolean( 1 ); 534 535 if ( !xRow->wasNull() ) 536 { 537 if ( IsDocument ) 538 return m_pMyShell->FileContentType; 539 else 540 return m_pMyShell->FolderContentType; 541 } 542 else 543 { 544 OSL_ENSURE( false, 545 "BaseContent::getContentType - Property value was null!" ); 546 } 547 } 548 catch ( sdbc::SQLException const & ) 549 { 550 OSL_ENSURE( false, 551 "BaseContent::getContentType - Caught SQLException!" ); 552 } 553 } 554 } 555 556 return rtl::OUString(); 557 } 558 559 560 561 void SAL_CALL 562 BaseContent::addContentEventListener( 563 const Reference< XContentEventListener >& Listener ) 564 throw( RuntimeException ) 565 { 566 osl::MutexGuard aGuard( m_aMutex ); 567 568 if ( ! m_pContentEventListeners ) 569 m_pContentEventListeners = 570 new cppu::OInterfaceContainerHelper( m_aEventListenerMutex ); 571 572 573 m_pContentEventListeners->addInterface( Listener ); 574 } 575 576 577 void SAL_CALL 578 BaseContent::removeContentEventListener( 579 const Reference< XContentEventListener >& Listener ) 580 throw( RuntimeException ) 581 { 582 osl::MutexGuard aGuard( m_aMutex ); 583 584 if ( m_pContentEventListeners ) 585 m_pContentEventListeners->removeInterface( Listener ); 586 } 587 588 589 590 //////////////////////////////////////////////////////////////////////////////// 591 // XPropertyContainer 592 //////////////////////////////////////////////////////////////////////////////// 593 594 595 void SAL_CALL 596 BaseContent::addProperty( 597 const rtl::OUString& Name, 598 sal_Int16 Attributes, 599 const Any& DefaultValue ) 600 throw( beans::PropertyExistException, 601 beans::IllegalTypeException, 602 lang::IllegalArgumentException, 603 RuntimeException) 604 { 605 if( ( m_nState & JustInserted ) || ( m_nState & Deleted ) || Name == rtl::OUString() ) 606 { 607 throw lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >(), 0 ); 608 } 609 610 m_pMyShell->associate( m_aUncPath,Name,DefaultValue,Attributes ); 611 } 612 613 614 void SAL_CALL 615 BaseContent::removeProperty( 616 const rtl::OUString& Name ) 617 throw( beans::UnknownPropertyException, 618 beans::NotRemoveableException, 619 RuntimeException) 620 { 621 622 if( m_nState & Deleted ) 623 throw beans::UnknownPropertyException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() ); 624 625 m_pMyShell->deassociate( m_aUncPath, Name ); 626 } 627 628 //////////////////////////////////////////////////////////////////////////////// 629 // XContentCreator 630 //////////////////////////////////////////////////////////////////////////////// 631 632 Sequence< ContentInfo > SAL_CALL 633 BaseContent::queryCreatableContentsInfo( 634 void ) 635 throw( RuntimeException ) 636 { 637 return m_pMyShell->queryCreatableContentsInfo(); 638 } 639 640 641 Reference< XContent > SAL_CALL 642 BaseContent::createNewContent( 643 const ContentInfo& Info ) 644 throw( RuntimeException ) 645 { 646 // Check type. 647 if ( !Info.Type.getLength() ) 648 return Reference< XContent >(); 649 650 sal_Bool bFolder 651 = ( Info.Type.compareTo( m_pMyShell->FolderContentType ) == 0 ); 652 if ( !bFolder ) 653 { 654 if ( Info.Type.compareTo( m_pMyShell->FileContentType ) != 0 ) 655 { 656 // Neither folder nor file to create! 657 return Reference< XContent >(); 658 } 659 } 660 661 // Who am I ? 662 sal_Bool IsDocument = false; 663 664 try 665 { 666 Sequence< beans::Property > seq(1); 667 seq[0] = beans::Property( rtl::OUString::createFromAscii("IsDocument"), 668 -1, 669 getCppuType( static_cast< sal_Bool* >(0) ), 670 0 ); 671 Reference< sdbc::XRow > xRow = getPropertyValues( -1,seq ); 672 IsDocument = xRow->getBoolean( 1 ); 673 674 if ( xRow->wasNull() ) 675 { 676 IsDocument = false; 677 // OSL_ENSURE( false, 678 // "BaseContent::createNewContent - Property value was null!" ); 679 // return Reference< XContent >(); 680 } 681 } 682 catch ( sdbc::SQLException const & ) 683 { 684 OSL_ENSURE( false, 685 "BaseContent::createNewContent - Caught SQLException!" ); 686 return Reference< XContent >(); 687 } 688 689 rtl::OUString dstUncPath; 690 691 if( IsDocument ) 692 { 693 // KSO: Why is a document a XContentCreator? This is quite unusual. 694 dstUncPath = getParentName( m_aUncPath ); 695 } 696 else 697 dstUncPath = m_aUncPath; 698 699 BaseContent* p = new BaseContent( m_pMyShell, dstUncPath, bFolder ); 700 return Reference< XContent >( p ); 701 } 702 703 704 //////////////////////////////////////////////////////////////////////////////// 705 // XPropertySetInfoChangeNotifier 706 //////////////////////////////////////////////////////////////////////////////// 707 708 709 void SAL_CALL 710 BaseContent::addPropertySetInfoChangeListener( 711 const Reference< beans::XPropertySetInfoChangeListener >& Listener ) 712 throw( RuntimeException ) 713 { 714 osl::MutexGuard aGuard( m_aMutex ); 715 if( ! m_pPropertySetInfoChangeListeners ) 716 m_pPropertySetInfoChangeListeners = new cppu::OInterfaceContainerHelper( m_aEventListenerMutex ); 717 718 m_pPropertySetInfoChangeListeners->addInterface( Listener ); 719 } 720 721 722 void SAL_CALL 723 BaseContent::removePropertySetInfoChangeListener( 724 const Reference< beans::XPropertySetInfoChangeListener >& Listener ) 725 throw( RuntimeException ) 726 { 727 osl::MutexGuard aGuard( m_aMutex ); 728 729 if( m_pPropertySetInfoChangeListeners ) 730 m_pPropertySetInfoChangeListeners->removeInterface( Listener ); 731 } 732 733 734 //////////////////////////////////////////////////////////////////////////////// 735 // XChild 736 //////////////////////////////////////////////////////////////////////////////// 737 738 Reference< XInterface > SAL_CALL 739 BaseContent::getParent( 740 void ) 741 throw( RuntimeException ) 742 { 743 rtl::OUString ParentUnq = getParentName( m_aUncPath ); 744 rtl::OUString ParentUrl; 745 746 747 sal_Bool err = m_pMyShell->getUrlFromUnq( ParentUnq, ParentUrl ); 748 if( err ) 749 return Reference< XInterface >( 0 ); 750 751 FileContentIdentifier* p = new FileContentIdentifier( m_pMyShell,ParentUnq ); 752 Reference< XContentIdentifier > Identifier( p ); 753 754 try 755 { 756 Reference< XContent > content = m_pMyShell->m_pProvider->queryContent( Identifier ); 757 return Reference<XInterface>(content,UNO_QUERY); 758 } 759 catch( IllegalIdentifierException ) 760 { 761 return Reference< XInterface >(); 762 } 763 } 764 765 766 void SAL_CALL 767 BaseContent::setParent( 768 const Reference< XInterface >& ) 769 throw( lang::NoSupportException, 770 RuntimeException) 771 { 772 throw lang::NoSupportException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() ); 773 } 774 775 776 ////////////////////////////////////////////////////////////////////////////////////////// 777 // Private Methods 778 ////////////////////////////////////////////////////////////////////////////////////////// 779 780 781 Reference< XCommandInfo > SAL_CALL 782 BaseContent::getCommandInfo() 783 throw( RuntimeException ) 784 { 785 if( m_nState & Deleted ) 786 return Reference< XCommandInfo >(); 787 788 return m_pMyShell->info_c(); 789 } 790 791 792 Reference< beans::XPropertySetInfo > SAL_CALL 793 BaseContent::getPropertySetInfo( 794 sal_Int32 ) 795 throw( RuntimeException ) 796 { 797 if( m_nState & Deleted ) 798 return Reference< beans::XPropertySetInfo >(); 799 800 return m_pMyShell->info_p( m_aUncPath ); 801 } 802 803 804 805 806 Reference< sdbc::XRow > SAL_CALL 807 BaseContent::getPropertyValues( 808 sal_Int32 nMyCommandIdentifier, 809 const Sequence< beans::Property >& PropertySet ) 810 throw( RuntimeException ) 811 { 812 sal_Int32 nProps = PropertySet.getLength(); 813 if ( !nProps ) 814 return Reference< sdbc::XRow >(); 815 816 if( m_nState & Deleted ) 817 { 818 Sequence< Any > aValues( nProps ); 819 return Reference< sdbc::XRow >( new XRow_impl( m_pMyShell, aValues ) ); 820 } 821 822 if( m_nState & JustInserted ) 823 { 824 Sequence< Any > aValues( nProps ); 825 Any* pValues = aValues.getArray(); 826 827 const beans::Property* pProps = PropertySet.getConstArray(); 828 829 for ( sal_Int32 n = 0; n < nProps; ++n ) 830 { 831 const beans::Property& rProp = pProps[ n ]; 832 Any& rValue = pValues[ n ]; 833 834 if( rProp.Name.compareToAscii( "ContentType" ) == 0 ) 835 { 836 rValue <<= m_bFolder ? m_pMyShell->FolderContentType 837 : m_pMyShell->FileContentType; 838 } 839 else if( rProp.Name.compareToAscii( "IsFolder" ) == 0 ) 840 { 841 rValue <<= m_bFolder; 842 } 843 else if( rProp.Name.compareToAscii( "IsDocument" ) == 0 ) 844 { 845 rValue <<= sal_Bool( !m_bFolder ); 846 } 847 } 848 849 return Reference< sdbc::XRow >( 850 new XRow_impl( m_pMyShell, aValues ) ); 851 } 852 853 return m_pMyShell->getv( nMyCommandIdentifier, 854 m_aUncPath, 855 PropertySet ); 856 } 857 858 859 Sequence< Any > SAL_CALL 860 BaseContent::setPropertyValues( 861 sal_Int32 nMyCommandIdentifier, 862 const Sequence< beans::PropertyValue >& Values ) 863 throw() 864 { 865 if( m_nState & Deleted ) 866 { // To do 867 return Sequence< Any >( Values.getLength() ); 868 } 869 870 const rtl::OUString Title = rtl::OUString::createFromAscii( "Title" ); 871 872 // Special handling for files which have to be inserted 873 if( m_nState & JustInserted ) 874 { 875 for( sal_Int32 i = 0; i < Values.getLength(); ++i ) 876 { 877 if( Values[i].Name == Title ) 878 { 879 rtl::OUString NewTitle; 880 if( Values[i].Value >>= NewTitle ) 881 { 882 if ( m_nState & NameForInsertionSet ) 883 { 884 // User wants to set another Title before "insert". 885 // m_aUncPath contains previous own URI. 886 887 sal_Int32 nLastSlash = m_aUncPath.lastIndexOf( '/' ); 888 bool bTrailingSlash = false; 889 if ( nLastSlash == m_aUncPath.getLength() - 1 ) 890 { 891 bTrailingSlash = true; 892 nLastSlash 893 = m_aUncPath.lastIndexOf( '/', nLastSlash ); 894 } 895 896 OSL_ENSURE( nLastSlash != -1, 897 "BaseContent::setPropertyValues: " 898 "Invalid URL!" ); 899 900 rtl::OUStringBuffer aBuf( 901 m_aUncPath.copy( 0, nLastSlash + 1 ) ); 902 903 if ( NewTitle.getLength() > 0 ) 904 { 905 aBuf.append( NewTitle ); 906 if ( bTrailingSlash ) 907 aBuf.append( sal_Unicode( '/' ) ); 908 } 909 else 910 { 911 m_nState &= ~NameForInsertionSet; 912 } 913 914 m_aUncPath = aBuf.makeStringAndClear(); 915 } 916 else 917 { 918 if ( NewTitle.getLength() > 0 ) 919 { 920 // Initial Title before "insert". 921 // m_aUncPath contains parent's URI. 922 923 if( m_aUncPath.lastIndexOf( sal_Unicode('/') ) != m_aUncPath.getLength() - 1 ) 924 m_aUncPath += rtl::OUString::createFromAscii("/"); 925 926 m_aUncPath += rtl::Uri::encode( NewTitle, 927 rtl_UriCharClassPchar, 928 rtl_UriEncodeIgnoreEscapes, 929 RTL_TEXTENCODING_UTF8 ); 930 m_nState |= NameForInsertionSet; 931 } 932 } 933 } 934 } 935 } 936 937 return Sequence< Any >( Values.getLength() ); 938 } 939 else 940 { 941 Sequence< Any > ret = m_pMyShell->setv( m_aUncPath, // Does not handle Title 942 Values ); 943 944 // Special handling Title: Setting Title is equivalent to a renaming of the underlying file 945 for( sal_Int32 i = 0; i < Values.getLength(); ++i ) 946 { 947 if( Values[i].Name != Title ) 948 continue; // handled by setv 949 950 rtl::OUString NewTitle; 951 if( !( Values[i].Value >>= NewTitle ) ) 952 { 953 ret[i] <<= beans::IllegalTypeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() ); 954 break; 955 } 956 else if( ! NewTitle.getLength() ) 957 { 958 ret[i] <<= lang::IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >(), 0 ); 959 break; 960 } 961 962 963 rtl::OUString aDstName = getParentName( m_aUncPath ); 964 if( aDstName.lastIndexOf( sal_Unicode('/') ) != aDstName.getLength() - 1 ) 965 aDstName += rtl::OUString::createFromAscii("/"); 966 967 aDstName += rtl::Uri::encode( NewTitle, 968 rtl_UriCharClassPchar, 969 rtl_UriEncodeIgnoreEscapes, 970 RTL_TEXTENCODING_UTF8 ); 971 972 m_pMyShell->move( nMyCommandIdentifier, // move notifies the childs also; 973 m_aUncPath, 974 aDstName, 975 NameClash::KEEP ); 976 977 try 978 { 979 endTask( nMyCommandIdentifier ); 980 } 981 catch( const Exception& e ) 982 { 983 ret[i] <<= e; 984 } 985 986 // NameChanges come back through a ContentEvent 987 break; // only handling Title 988 } // end for 989 990 return ret; 991 } 992 } 993 994 995 996 Reference< XDynamicResultSet > SAL_CALL 997 BaseContent::open( 998 sal_Int32 nMyCommandIdentifier, 999 const OpenCommandArgument2& aCommandArgument ) 1000 throw() 1001 { 1002 Reference< XDynamicResultSet > retValue( 0 ); 1003 1004 if( ( m_nState & Deleted ) ) 1005 { 1006 m_pMyShell->installError( nMyCommandIdentifier, 1007 TASKHANDLING_DELETED_STATE_IN_OPEN_COMMAND ); 1008 } 1009 else if( m_nState & JustInserted ) 1010 { 1011 m_pMyShell->installError( nMyCommandIdentifier, 1012 TASKHANDLING_INSERTED_STATE_IN_OPEN_COMMAND ); 1013 } 1014 else 1015 { 1016 if( aCommandArgument.Mode == OpenMode::DOCUMENT || 1017 aCommandArgument.Mode == OpenMode::DOCUMENT_SHARE_DENY_NONE ) 1018 1019 { 1020 Reference< io::XOutputStream > outputStream( aCommandArgument.Sink,UNO_QUERY ); 1021 if( outputStream.is() ) 1022 { 1023 m_pMyShell->page( nMyCommandIdentifier, 1024 m_aUncPath, 1025 outputStream ); 1026 } 1027 1028 sal_Bool bLock = ( aCommandArgument.Mode != OpenMode::DOCUMENT_SHARE_DENY_NONE ); 1029 1030 Reference< io::XActiveDataSink > activeDataSink( aCommandArgument.Sink,UNO_QUERY ); 1031 if( activeDataSink.is() ) 1032 { 1033 activeDataSink->setInputStream( m_pMyShell->open( nMyCommandIdentifier, 1034 m_aUncPath, 1035 bLock ) ); 1036 } 1037 1038 Reference< io::XActiveDataStreamer > activeDataStreamer( aCommandArgument.Sink,UNO_QUERY ); 1039 if( activeDataStreamer.is() ) 1040 { 1041 activeDataStreamer->setStream( m_pMyShell->open_rw( nMyCommandIdentifier, 1042 m_aUncPath, 1043 bLock ) ); 1044 } 1045 } 1046 else if ( aCommandArgument.Mode == OpenMode::ALL || 1047 aCommandArgument.Mode == OpenMode::FOLDERS || 1048 aCommandArgument.Mode == OpenMode::DOCUMENTS ) 1049 { 1050 retValue = m_pMyShell->ls( nMyCommandIdentifier, 1051 m_aUncPath, 1052 aCommandArgument.Mode, 1053 aCommandArgument.Properties, 1054 aCommandArgument.SortingInfo ); 1055 } 1056 // else if( aCommandArgument.Mode == 1057 // OpenMode::DOCUMENT_SHARE_DENY_NONE || 1058 // aCommandArgument.Mode == 1059 // OpenMode::DOCUMENT_SHARE_DENY_WRITE ) 1060 // m_pMyShell->installError( nMyCommandIdentifier, 1061 // TASKHANDLING_UNSUPPORTED_OPEN_MODE, 1062 // aCommandArgument.Mode); 1063 else 1064 m_pMyShell->installError( nMyCommandIdentifier, 1065 TASKHANDLING_UNSUPPORTED_OPEN_MODE, 1066 aCommandArgument.Mode); 1067 } 1068 1069 return retValue; 1070 } 1071 1072 1073 1074 void SAL_CALL 1075 BaseContent::deleteContent( sal_Int32 nMyCommandIdentifier ) 1076 throw() 1077 { 1078 if( m_nState & Deleted ) 1079 return; 1080 1081 if( m_pMyShell->remove( nMyCommandIdentifier,m_aUncPath ) ) 1082 { 1083 osl::MutexGuard aGuard( m_aMutex ); 1084 m_nState |= Deleted; 1085 } 1086 } 1087 1088 1089 1090 void SAL_CALL 1091 BaseContent::transfer( sal_Int32 nMyCommandIdentifier, 1092 const TransferInfo& aTransferInfo ) 1093 throw() 1094 { 1095 if( m_nState & Deleted ) 1096 return; 1097 1098 if( aTransferInfo.SourceURL.compareToAscii( "file:",5 ) != 0 ) 1099 { 1100 m_pMyShell->installError( nMyCommandIdentifier, 1101 TASKHANDLING_TRANSFER_INVALIDSCHEME ); 1102 return; 1103 } 1104 1105 rtl::OUString srcUnc; 1106 if( m_pMyShell->getUnqFromUrl( aTransferInfo.SourceURL,srcUnc ) ) 1107 { 1108 m_pMyShell->installError( nMyCommandIdentifier, 1109 TASKHANDLING_TRANSFER_INVALIDURL ); 1110 return; 1111 } 1112 1113 rtl::OUString srcUncPath = srcUnc; 1114 1115 // Determine the new title ! 1116 rtl::OUString NewTitle; 1117 if( aTransferInfo.NewTitle.getLength() ) 1118 NewTitle = rtl::Uri::encode( aTransferInfo.NewTitle, 1119 rtl_UriCharClassPchar, 1120 rtl_UriEncodeIgnoreEscapes, 1121 RTL_TEXTENCODING_UTF8 ); 1122 else 1123 NewTitle = srcUncPath.copy( 1 + srcUncPath.lastIndexOf( sal_Unicode('/') ) ); 1124 1125 // Is destination a document or a folder ? 1126 Sequence< beans::Property > seq(1); 1127 seq[0] = beans::Property( rtl::OUString::createFromAscii("IsDocument"), 1128 -1, 1129 getCppuType( static_cast< sal_Bool* >(0) ), 1130 0 ); 1131 Reference< sdbc::XRow > xRow = getPropertyValues( nMyCommandIdentifier,seq ); 1132 sal_Bool IsDocument = xRow->getBoolean( 1 ); 1133 if( xRow->wasNull() ) 1134 { // Destination file type could not be determined 1135 m_pMyShell->installError( nMyCommandIdentifier, 1136 TASKHANDLING_TRANSFER_DESTFILETYPE ); 1137 return; 1138 } 1139 1140 rtl::OUString dstUncPath; 1141 if( IsDocument ) 1142 { // as sibling 1143 sal_Int32 lastSlash = m_aUncPath.lastIndexOf( sal_Unicode('/') ); 1144 dstUncPath = m_aUncPath.copy(0,lastSlash ); 1145 } 1146 else 1147 // as child 1148 dstUncPath = m_aUncPath; 1149 1150 dstUncPath += ( rtl::OUString::createFromAscii( "/" ) + NewTitle ); 1151 1152 sal_Int32 NameClash = aTransferInfo.NameClash; 1153 1154 if( aTransferInfo.MoveData ) 1155 m_pMyShell->move( nMyCommandIdentifier,srcUncPath,dstUncPath,NameClash ); 1156 else 1157 m_pMyShell->copy( nMyCommandIdentifier,srcUncPath,dstUncPath,NameClash ); 1158 } 1159 1160 1161 1162 1163 void SAL_CALL BaseContent::insert( sal_Int32 nMyCommandIdentifier, 1164 const InsertCommandArgument& aInsertArgument ) 1165 throw() 1166 { 1167 if( m_nState & FullFeatured ) 1168 { 1169 m_pMyShell->write( nMyCommandIdentifier, 1170 m_aUncPath, 1171 aInsertArgument.ReplaceExisting, 1172 aInsertArgument.Data ); 1173 return; 1174 } 1175 1176 if( ! ( m_nState & JustInserted ) ) 1177 { 1178 m_pMyShell->installError( nMyCommandIdentifier, 1179 TASKHANDLING_NOFRESHINSERT_IN_INSERT_COMMAND ); 1180 return; 1181 } 1182 1183 // Inserts the content, which has the flag m_bIsFresh 1184 1185 if ( !m_nState | !NameForInsertionSet ) 1186 { 1187 m_pMyShell->installError( nMyCommandIdentifier, 1188 TASKHANDLING_NONAMESET_INSERT_COMMAND ); 1189 return; 1190 } 1191 1192 // Inserting a document or a file? 1193 sal_Bool bDocument = false; 1194 1195 Sequence< beans::Property > seq(1); 1196 seq[0] = beans::Property( rtl::OUString::createFromAscii("IsDocument"), 1197 -1, 1198 getCppuType( static_cast< sal_Bool* >(0) ), 1199 0 ); 1200 1201 Reference< sdbc::XRow > xRow = getPropertyValues( -1,seq ); 1202 1203 bool contentTypeSet = true; // is set to false, if contentType not set 1204 try 1205 { 1206 bDocument = xRow->getBoolean( 1 ); 1207 if( xRow->wasNull() ) 1208 contentTypeSet = false; 1209 1210 } 1211 catch ( sdbc::SQLException const & ) 1212 { 1213 OSL_ENSURE( false, 1214 "BaseContent::insert - Caught SQLException!" ); 1215 contentTypeSet = false; 1216 } 1217 1218 if( ! contentTypeSet ) 1219 { 1220 m_pMyShell->installError( nMyCommandIdentifier, 1221 TASKHANDLING_NOCONTENTTYPE_INSERT_COMMAND ); 1222 return; 1223 } 1224 1225 1226 sal_Bool success = false; 1227 if( bDocument ) 1228 success = m_pMyShell->mkfil( nMyCommandIdentifier, 1229 m_aUncPath, 1230 aInsertArgument.ReplaceExisting, 1231 aInsertArgument.Data ); 1232 else 1233 { 1234 while( ! success ) 1235 { 1236 success = m_pMyShell->mkdir( nMyCommandIdentifier, 1237 m_aUncPath, 1238 aInsertArgument.ReplaceExisting ); 1239 if( success ) 1240 break; 1241 1242 XInteractionRequestImpl *aRequestImpl = 1243 new XInteractionRequestImpl( 1244 rtl::Uri::decode( 1245 getTitle(m_aUncPath), 1246 rtl_UriDecodeWithCharset, 1247 RTL_TEXTENCODING_UTF8), 1248 (cppu::OWeakObject*)this, 1249 m_pMyShell,nMyCommandIdentifier); 1250 uno::Reference< task::XInteractionRequest > aReq( aRequestImpl ); 1251 1252 m_pMyShell->handleTask( nMyCommandIdentifier,aReq ); 1253 if( aRequestImpl->aborted() || 1254 !aRequestImpl->newName().getLength() ) 1255 // means aborting 1256 break; 1257 1258 // determine new uncpath 1259 m_pMyShell->clearError( nMyCommandIdentifier ); 1260 m_aUncPath = getParentName( m_aUncPath ); 1261 if( m_aUncPath.lastIndexOf( sal_Unicode('/') ) != m_aUncPath.getLength() - 1 ) 1262 m_aUncPath += rtl::OUString::createFromAscii("/"); 1263 1264 m_aUncPath += rtl::Uri::encode( aRequestImpl->newName(), 1265 rtl_UriCharClassPchar, 1266 rtl_UriEncodeIgnoreEscapes, 1267 RTL_TEXTENCODING_UTF8 ); 1268 } 1269 } 1270 1271 if ( ! success ) 1272 return; 1273 1274 FileContentIdentifier* p = new FileContentIdentifier( m_pMyShell,m_aUncPath ); 1275 m_xContentIdentifier = Reference< XContentIdentifier >( p ); 1276 1277 m_pMyShell->registerNotifier( m_aUncPath,this ); 1278 m_pMyShell->insertDefaultProperties( m_aUncPath ); 1279 1280 osl::MutexGuard aGuard( m_aMutex ); 1281 m_nState = FullFeatured; 1282 } 1283 1284 1285 1286 void SAL_CALL BaseContent::endTask( sal_Int32 CommandId ) 1287 { 1288 // This is the only function allowed to throw an exception 1289 m_pMyShell->endTask( CommandId,m_aUncPath,this ); 1290 } 1291 1292 1293 1294 ContentEventNotifier* 1295 BaseContent::cDEL( void ) 1296 { 1297 osl::MutexGuard aGuard( m_aMutex ); 1298 1299 m_nState |= Deleted; 1300 1301 ContentEventNotifier* p; 1302 if( m_pContentEventListeners ) 1303 p = new ContentEventNotifier( m_pMyShell, 1304 this, 1305 m_xContentIdentifier, 1306 m_pContentEventListeners->getElements() ); 1307 else 1308 p = 0; 1309 1310 return p; 1311 } 1312 1313 1314 ContentEventNotifier* 1315 BaseContent::cEXC( const rtl::OUString aNewName ) 1316 { 1317 osl::MutexGuard aGuard( m_aMutex ); 1318 1319 Reference< XContentIdentifier > xOldRef = m_xContentIdentifier; 1320 m_aUncPath = aNewName; 1321 FileContentIdentifier* pp = new FileContentIdentifier( m_pMyShell,aNewName ); 1322 m_xContentIdentifier = Reference< XContentIdentifier >( pp ); 1323 1324 ContentEventNotifier* p = 0; 1325 if( m_pContentEventListeners ) 1326 p = new ContentEventNotifier( m_pMyShell, 1327 this, 1328 m_xContentIdentifier, 1329 xOldRef, 1330 m_pContentEventListeners->getElements() ); 1331 1332 return p; 1333 } 1334 1335 1336 ContentEventNotifier* 1337 BaseContent::cCEL( void ) 1338 { 1339 osl::MutexGuard aGuard( m_aMutex ); 1340 ContentEventNotifier* p = 0; 1341 if( m_pContentEventListeners ) 1342 p = new ContentEventNotifier( m_pMyShell, 1343 this, 1344 m_xContentIdentifier, 1345 m_pContentEventListeners->getElements() ); 1346 1347 return p; 1348 } 1349 1350 PropertySetInfoChangeNotifier* 1351 BaseContent::cPSL( void ) 1352 { 1353 osl::MutexGuard aGuard( m_aMutex ); 1354 PropertySetInfoChangeNotifier* p = 0; 1355 if( m_pPropertySetInfoChangeListeners ) 1356 p = new PropertySetInfoChangeNotifier( m_pMyShell, 1357 this, 1358 m_xContentIdentifier, 1359 m_pPropertySetInfoChangeListeners->getElements() ); 1360 1361 return p; 1362 } 1363 1364 1365 1366 PropertyChangeNotifier* 1367 BaseContent::cPCL( void ) 1368 { 1369 osl::MutexGuard aGuard( m_aMutex ); 1370 1371 Sequence< rtl::OUString > seqNames; 1372 1373 if( m_pPropertyListener ) 1374 seqNames = m_pPropertyListener->getContainedTypes(); 1375 1376 PropertyChangeNotifier* p = 0; 1377 1378 sal_Int32 length = seqNames.getLength(); 1379 1380 if( length ) 1381 { 1382 ListenerMap* listener = new ListenerMap(); 1383 for( sal_Int32 i = 0; i < length; ++i ) 1384 { 1385 (*listener)[seqNames[i]] = m_pPropertyListener->getContainer( seqNames[i] )->getElements(); 1386 } 1387 1388 p = new PropertyChangeNotifier( m_pMyShell, 1389 this, 1390 m_xContentIdentifier, 1391 listener ); 1392 } 1393 1394 return p; 1395 } 1396 1397 1398 rtl::OUString BaseContent::getKey( void ) 1399 { 1400 return m_aUncPath; 1401 } 1402 1403 /* vim: set noet sw=4 ts=4: */ 1404