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