1 /************************************************************************* 2 * 3 * The Contents of this file are made available subject to the terms of 4 * the BSD license. 5 * 6 * Copyright 2000, 2010 Oracle and/or its affiliates. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of Sun Microsystems, Inc. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 30 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 31 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 *************************************************************************/ 34 35 // __________ Imports __________ 36 37 import com.sun.star.uno.UnoRuntime; 38 39 import java.lang.*; 40 import javax.swing.*; 41 import java.util.Vector; 42 43 // __________ Implementation __________ 44 45 /** 46 * This class can be used to intercept dispatched URL's 47 * on any frame used in this demo application. 48 * It intercept all URL's wich try to create a new empty frame. 49 * (e.g. "private:factory/swriter") 50 * Nobody can guarantee that this interception will be realy used - 51 * because another interceptor (registered at a later time then this one!) 52 * will be called before this one. 53 * Implementation is executed inside a new thread to prevent application 54 * against possible deadlocks. This deadlocks can occure if 55 * synchronous/asynchronous ... normal ones and oneway calls are mixed. 56 * Notifications of listener will be oneway mostly - her reactions can 57 * be synchronous then. => deadlocks are possible 58 * 59 * @author Andreas Schlüns 60 * @created 06.03.2002 09:38 61 */ 62 public class Interceptor implements com.sun.star.frame.XFrameActionListener, 63 com.sun.star.frame.XDispatchProviderInterceptor, 64 com.sun.star.frame.XDispatchProvider, 65 com.sun.star.frame.XDispatch, 66 com.sun.star.frame.XInterceptorInfo, 67 IShutdownListener, 68 IOnewayLink 69 { 70 // ____________________ 71 72 /** 73 * const 74 * All these URL's are intercepted by this implementation. 75 */ 76 private static final String[] INTERCEPTED_URLS = { "private:factory/*" , 77 ".uno:SaveAs" , 78 "slot:5300" , 79 ".uno:Quit" }; 80 81 // ____________________ 82 83 /** 84 * @member m_xMaster use this interceptor if he doesn't handle queried dispatch request 85 * @member m_xSlave we can forward all unhandled requests to this slave interceptor 86 * @member m_xFrame intercepted frame 87 * @member m_bDead there exist more then one way to finish an object of this class - we must know it sometimes 88 */ 89 private com.sun.star.frame.XDispatchProvider m_xMaster ; 90 private com.sun.star.frame.XDispatchProvider m_xSlave ; 91 private com.sun.star.frame.XFrame m_xFrame ; 92 private boolean m_bIsActionListener ; 93 private boolean m_bIsRegistered ; 94 private boolean m_bDead ; 95 96 // ____________________ 97 98 /** 99 * ctor 100 * Initialize the new interceptor. Given frame reference can be used to 101 * register this interceptor on it automaticly later. 102 * 103 * @seealso startListening() 104 * 105 * @param xFrame 106 * this interceptor will register himself at this frame to intercept dispatched URLs 107 */ 108 Interceptor(/*IN*/ com.sun.star.frame.XFrame xFrame) 109 { 110 m_xFrame = xFrame ; 111 m_xSlave = null ; 112 m_xMaster = null ; 113 m_bIsRegistered = false ; 114 m_bIsActionListener = false ; 115 m_bDead = false ; 116 } 117 118 //_____________________ 119 120 /** 121 * start working as frame action listener realy. 122 * We will be frame action listener here. In case 123 * we get a frame action which indicates, that we should 124 * update our interception. Because such using of an interecptor 125 * isn't guaranteed - in case a newer one was registered ... 126 */ 127 public void startListening() 128 { 129 com.sun.star.frame.XFrame xFrame = null; 130 synchronized(this) 131 { 132 if (m_bDead) 133 return; 134 if (m_xFrame==null) 135 return; 136 if (m_bIsActionListener==true) 137 return; 138 xFrame = m_xFrame; 139 } 140 m_xFrame.addFrameActionListener(this); 141 synchronized(this) 142 { 143 m_bIsActionListener=true; 144 } 145 } 146 147 //_____________________ 148 149 /** 150 * In case we got an oneway listener callback - we had to use the office 151 * asynchronous then. This method is the callback from the started thread 152 * (started inside the original oneway method). We found all parameters of 153 * the original request packed inside a vector. Here we unpack it and 154 * call the right internal helper method, which implements the right 155 * funtionality. 156 * 157 * @seealso frameAction() 158 * @seealso dispatch() 159 * 160 * @param nRequest 161 * indicates, which was the original request (identifies the 162 * original called method) 163 * 164 * @param lParams 165 * the vector with all packed parameters of the original request 166 */ 167 public void execOneway(/*IN*/ int nRequest,/*IN*/ Vector lParams ) 168 { 169 synchronized(this) 170 { 171 if (m_bDead) 172 return; 173 } 174 175 // was it frameAction()? 176 if (nRequest==OnewayExecutor.REQUEST_FRAMEACTION) 177 { 178 com.sun.star.frame.FrameActionEvent[] lOutAction = new com.sun.star.frame.FrameActionEvent[1]; 179 Vector[] lInParams = new Vector[1]; 180 lInParams[0] = lParams; 181 182 OnewayExecutor.codeFrameAction( OnewayExecutor.DECODE_PARAMS , 183 lInParams , 184 lOutAction ); 185 impl_frameAction(lOutAction[0]); 186 } 187 else 188 // was it dispatch()? 189 if (nRequest==OnewayExecutor.REQUEST_DISPATCH) 190 { 191 com.sun.star.util.URL[] lOutURL = new com.sun.star.util.URL[1]; 192 com.sun.star.beans.PropertyValue[][] lOutProps = new com.sun.star.beans.PropertyValue[1][]; 193 Vector[] lInParams = new Vector[1]; 194 lInParams[0] = lParams; 195 196 OnewayExecutor.codeDispatch( OnewayExecutor.DECODE_PARAMS , 197 lInParams , 198 lOutURL , 199 lOutProps ); 200 impl_dispatch(lOutURL[0],lOutProps[0]); 201 } 202 } 203 204 // ____________________ 205 206 /** 207 * call back for frame action events 208 * We use it to update our interception. Because if a new component was loaded into 209 * the frame or another interceptor was registered, we should refresh our connection 210 * to the frame. Otherwhise we can't guarantee full functionality here. 211 * 212 * Note: Don't react synchronous in an asynchronous listener callback. So use a thread 213 * here to update anything. 214 * 215 * @seealso impl_frameAction() 216 * 217 * @param aEvent 218 * describes the action 219 */ 220 public /*ONEWAY*/ void frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent) 221 { 222 synchronized(this) 223 { 224 if (m_bDead) 225 return; 226 } 227 228 boolean bHandle = false; 229 switch(aEvent.Action.getValue()) 230 { 231 case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value : bHandle=true; break; 232 case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value : bHandle=true; break; 233 case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bHandle=true; break; 234 // Don't react for CONTEXT_CHANGED here. Ok it indicates, that may another interceptor 235 // was registered at the frame ... but if we register ourself there - we get a context 236 // changed too :-( Best way to produce a never ending recursion ... 237 // May be that somewhere find a safe mechanism to detect own produced frame action events 238 // and ignore it. 239 case com.sun.star.frame.FrameAction.CONTEXT_CHANGED_value : 240 System.out.println("Time to update interception ... but may it will start a recursion. So I let it :-("); 241 bHandle=false; 242 break; 243 } 244 245 // ignore some events 246 if (! bHandle) 247 return; 248 249 // pack the event and start thread - which call us back later 250 Vector[] lOutParams = new Vector[1]; 251 com.sun.star.frame.FrameActionEvent[] lInAction = new com.sun.star.frame.FrameActionEvent[1]; 252 lInAction[0] = aEvent; 253 254 OnewayExecutor.codeFrameAction( OnewayExecutor.ENCODE_PARAMS , 255 lOutParams , 256 lInAction ); 257 OnewayExecutor aExecutor = new OnewayExecutor( (IOnewayLink)this , 258 OnewayExecutor.REQUEST_FRAMEACTION , 259 lOutParams[0] ); 260 aExecutor.start(); 261 } 262 263 // ____________________ 264 265 /** 266 * Indicates using of us as an interceptor. 267 * Now we have to react for the requests, we are registered. 268 * That means: load new empty documents - triggered by the new menu of the office. 269 * Because it's oneway - use thread for loading! 270 * 271 * @seealso impl_dispatch() 272 * 273 * @param aURL 274 * describes the document, which should be loaded 275 * 276 * @param lArguments 277 * optional parameters for loading 278 */ 279 public /*ONEWAY*/ void dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments) 280 { 281 synchronized(this) 282 { 283 if (m_bDead) 284 return; 285 } 286 287 Vector[] lOutParams = new Vector[1]; 288 com.sun.star.util.URL[] lInURL = new com.sun.star.util.URL[1]; 289 com.sun.star.beans.PropertyValue[][] lInArguments = new com.sun.star.beans.PropertyValue[1][]; 290 lInURL[0] = aURL ; 291 lInArguments[0] = lArguments; 292 293 OnewayExecutor.codeDispatch( OnewayExecutor.ENCODE_PARAMS , 294 lOutParams , 295 lInURL , 296 lInArguments ); 297 OnewayExecutor aExecutor = new OnewayExecutor( (IOnewayLink)this , 298 OnewayExecutor.REQUEST_DISPATCH , 299 lOutParams[0] ); 300 aExecutor.start(); 301 } 302 303 304 //_____________________ 305 306 /** 307 * Internal call back for frame action events, triggered by the used 308 * OnewayExecutor thread we started in frameAction(). 309 * We use it to update our interception on the internal saved frame. 310 * 311 * @param aEvent 312 * describes the action 313 */ 314 public void impl_frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent) 315 { 316 synchronized(this) 317 { 318 if (m_bDead) 319 return; 320 } 321 322 // deregistration will be done everytime ... 323 // But may it's not neccessary to establish a new registration! 324 // Don't look for ignoring actions - it was done already inside original frameAction() call! 325 boolean bRegister = false; 326 327 // analyze the event and decide which reaction is usefull 328 switch(aEvent.Action.getValue()) 329 { 330 case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value : bRegister = true ; break; 331 case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bRegister = true ; break; 332 case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value : bRegister = false; break; 333 } 334 335 com.sun.star.frame.XFrame xFrame = null ; 336 boolean bIsRegistered = false; 337 synchronized(this) 338 { 339 bIsRegistered = m_bIsRegistered; 340 m_bIsRegistered = false; 341 xFrame = m_xFrame; 342 } 343 344 com.sun.star.frame.XDispatchProviderInterception xRegistration = (com.sun.star.frame.XDispatchProviderInterception)UnoRuntime.queryInterface( 345 com.sun.star.frame.XDispatchProviderInterception.class, 346 xFrame); 347 348 if(xRegistration==null) 349 return; 350 351 if (bIsRegistered) 352 xRegistration.releaseDispatchProviderInterceptor(this); 353 354 if (! bRegister) 355 return; 356 357 xRegistration.registerDispatchProviderInterceptor(this); 358 synchronized(this) 359 { 360 m_bIsRegistered = true; 361 } 362 } 363 364 // ____________________ 365 366 /** 367 * Implementation of interface XDispatchProviderInterceptor 368 * These functions are used to build a list of interceptor objects 369 * connected in both ways. 370 * Searching for a right interceptor is made by forwarding any request 371 * from toppest master to lowest slave of this hierarchy. 372 * If an interceptor whish to handle the request he can break that 373 * and return himself as a dispatcher. 374 */ 375 public com.sun.star.frame.XDispatchProvider getSlaveDispatchProvider() 376 { 377 synchronized(this) 378 { 379 return m_xSlave; 380 } 381 } 382 383 // ____________________ 384 385 public void setSlaveDispatchProvider(com.sun.star.frame.XDispatchProvider xSlave) 386 { 387 synchronized(this) 388 { 389 m_xSlave = xSlave; 390 } 391 } 392 393 // ____________________ 394 395 public com.sun.star.frame.XDispatchProvider getMasterDispatchProvider() 396 { 397 synchronized(this) 398 { 399 return m_xMaster; 400 } 401 } 402 403 // ____________________ 404 405 public void setMasterDispatchProvider(com.sun.star.frame.XDispatchProvider xMaster) 406 { 407 synchronized(this) 408 { 409 m_xMaster = xMaster; 410 } 411 } 412 413 // ____________________ 414 415 /** 416 * Implementation of interface XDispatchProvider 417 * These functions are called from our master if he willn't handle the outstanding request. 418 * Given parameter should be checked if they are right for us. If it's true, the returned 419 * dispatcher should be this implementation himself; otherwise call should be forwarded 420 * to the slave. 421 * 422 * @param aURL 423 * describes the request, which should be handled 424 * 425 * @param sTarget 426 * specifies the target frame for this request 427 * 428 * @param nSearchFlags 429 * optional search flags, if sTarget isn't a special one 430 * 431 * @return [XDispatch] 432 * a dispatch object, which can handle the given URL 433 * May be NULL! 434 */ 435 public com.sun.star.frame.XDispatch queryDispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ String sTarget,/*IN*/ int nSearchFlags) 436 { 437 synchronized(this) 438 { 439 if (m_bDead) 440 return null; 441 } 442 443 // intercept loading empty documents into new created frames 444 if( 445 (sTarget.compareTo ("_blank" ) == 0 ) && 446 (aURL.Complete.startsWith("private:factory") == true) 447 ) 448 { 449 System.out.println("intercept private:factory"); 450 return this; 451 } 452 453 // intercept opening the SaveAs dialog 454 if (aURL.Complete.startsWith(".uno:SaveAs") == true) 455 { 456 System.out.println("intercept SaveAs by returning null!"); 457 return null; 458 } 459 460 // intercept "File->Exit" inside the menu 461 if ( 462 (aURL.Complete.startsWith("slot:5300") == true) || 463 (aURL.Complete.startsWith(".uno:Quit") == true) 464 ) 465 { 466 System.out.println("intercept File->Exit"); 467 return this; 468 } 469 470 synchronized(this) 471 { 472 if (m_xSlave!=null) 473 return m_xSlave.queryDispatch(aURL, sTarget, nSearchFlags); 474 } 475 476 return null; 477 } 478 479 // ____________________ 480 481 public com.sun.star.frame.XDispatch[] queryDispatches(/*IN*/ com.sun.star.frame.DispatchDescriptor[] lDescriptor) 482 { 483 synchronized(this) 484 { 485 if (m_bDead) 486 return null; 487 } 488 // Resolve any request seperatly by using own "dispatch()" method. 489 // Note: Don't pack return list if "null" objects occure! 490 int nCount = lDescriptor.length; 491 com.sun.star.frame.XDispatch[] lDispatcher = new com.sun.star.frame.XDispatch[nCount]; 492 for(int i=0; i<nCount; ++i) 493 { 494 lDispatcher[i] = queryDispatch(lDescriptor[i].FeatureURL , 495 lDescriptor[i].FrameName , 496 lDescriptor[i].SearchFlags); 497 } 498 return lDispatcher; 499 } 500 501 // ____________________ 502 503 /** 504 * This method is called if this interceptor "wins the request". 505 * We intercepted creation of new frames and loading of empty documents. 506 * Do it now. 507 * 508 * @param aURL 509 * describes the document 510 * 511 * @param lArguments 512 * optional arguments for loading 513 */ 514 public void impl_dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments) 515 { 516 synchronized(this) 517 { 518 if (m_bDead) 519 return; 520 } 521 522 if ( 523 (aURL.Complete.startsWith("slot:5300") == true) || 524 (aURL.Complete.startsWith(".uno:Quit") == true) 525 ) 526 { 527 System.exit(0); 528 } 529 else 530 if (aURL.Complete.startsWith("private:factory") == true) 531 { 532 // Create view frame for showing loaded documents on demand. 533 // The visible state is neccessary for JNI functionality to get the HWND and plug office 534 // inside a java window hierarchy! 535 DocumentView aNewView = new DocumentView(); 536 aNewView.setVisible(true); 537 aNewView.createFrame(); 538 aNewView.load(aURL.Complete,lArguments); 539 } 540 } 541 542 // ____________________ 543 544 /** 545 * Notification of status listener isn't guaranteed (instead of listener on XNotifyingDispatch interface). 546 * So this interceptor doesn't support that realy ... 547 */ 548 public /*ONEWAY*/ void addStatusListener(/*IN*/ com.sun.star.frame.XStatusListener xListener,/*IN*/ com.sun.star.util.URL aURL) 549 { 550 /* if (aURL.Complete.startsWith(".uno:SaveAs")==true) 551 { 552 com.sun.star.frame.FeatureStateEvent aEvent = new com.sun.star.frame.FeatureStateEvent( 553 this, 554 aURL, 555 "", 556 false, 557 false, 558 null); 559 if (xListener!=null) 560 { 561 System.out.println("interceptor disable SavAs by listener notify"); 562 xListener.statusChanged(aEvent); 563 } 564 }*/ 565 } 566 567 // ____________________ 568 569 public /*ONEWAY*/ void removeStatusListener(/*IN*/ com.sun.star.frame.XStatusListener xListener,/*IN*/ com.sun.star.util.URL aURL) 570 { 571 } 572 573 // ____________________ 574 575 /** 576 * Implements (optional!) optimization for interceptor mechanism. 577 * Any interceptor which provides this special interface is called automaticly 578 * at registration time on this method. Returned URL's will be used to 579 * call this interceptor directly without calling his masters before, IF(!) 580 * following rules will be true: 581 * (1) every master supports this optional interface too 582 * (2) nobody of these masters whish to intercept same URL then this one 583 * This interceptor whish to intercept creation of new documents. 584 */ 585 public String[] getInterceptedURLs() 586 { 587 return INTERCEPTED_URLS; 588 } 589 590 // ____________________ 591 592 /** 593 * This class listen on the intercepted frame to free all used ressources on closing. 594 * We forget the reference to the frame only here. Deregistration 595 * isn't neccessary here - because this frame dies and wish to forgoten. 596 * 597 * @param aSource 598 * must be our internal saved frame, on which we listen for frame action events 599 */ 600 public /*ONEAY*/ void disposing(/*IN*/ com.sun.star.lang.EventObject aSource) 601 { 602 synchronized(this) 603 { 604 if (m_bDead) 605 return; 606 if (m_xFrame!=null && UnoRuntime.areSame(aSource.Source,m_xFrame)) 607 { 608 m_bIsActionListener = false; 609 m_xFrame = null ; 610 } 611 } 612 shutdown(); 613 } 614 615 // ____________________ 616 617 /** 618 * If this java application shutdown - we must cancel all current existing 619 * listener connections. Otherwhise the office will run into some 620 * DisposedExceptions if it tries to use these forgotten listener references. 621 * And of course it can die doing that. 622 * We are registered at a central object to be informed if the VM will exit. 623 * So we can react. 624 */ 625 public void shutdown() 626 { 627 com.sun.star.frame.XFrame xFrame = null ; 628 boolean bIsRegistered = false; 629 boolean bIsActionListener = false; 630 synchronized(this) 631 { 632 // don't react a second time here! 633 if (m_bDead) 634 return; 635 m_bDead = true; 636 637 bIsRegistered = m_bIsRegistered; 638 m_bIsRegistered = false; 639 640 bIsActionListener = m_bIsActionListener; 641 m_bIsActionListener = false; 642 643 xFrame = m_xFrame; 644 m_xFrame = null; 645 } 646 647 // it's a good idead to cancel listening for frame action events 648 // before(!) we deregister us as an interceptor. 649 // Because registration and deregistratio nof interceptor objects 650 // will force sending of frame action events ...! 651 if (bIsActionListener) 652 xFrame.removeFrameActionListener(this); 653 654 if (bIsRegistered) 655 { 656 com.sun.star.frame.XDispatchProviderInterception xRegistration = (com.sun.star.frame.XDispatchProviderInterception)UnoRuntime.queryInterface( 657 com.sun.star.frame.XDispatchProviderInterception.class, 658 xFrame); 659 660 if(xRegistration!=null) 661 xRegistration.releaseDispatchProviderInterceptor(this); 662 } 663 664 xFrame = null; 665 666 synchronized(this) 667 { 668 m_xMaster = null; 669 m_xSlave = null; 670 } 671 } 672 } 673