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