1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * The Contents of this file are made available subject to the terms of
7 * Copyright 2000, 2010 Oracle and/or its affiliates.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of Sun Microsystems, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
32 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 import java
.util
.ArrayList
;
40 import com
.sun
.star
.frame
.FrameActionEvent
;
41 import com
.sun
.star
.uno
.UnoRuntime
;
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 which try to create a new empty frame.
49 * (e.g. "private:factory/swriter")
50 * Nobody can guarantee that this interception will be really 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 occur 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
59 public class Interceptor
implements com
.sun
.star
.frame
.XFrameActionListener
,
60 com
.sun
.star
.frame
.XDispatchProviderInterceptor
,
61 com
.sun
.star
.frame
.XDispatch
,
62 com
.sun
.star
.frame
.XInterceptorInfo
,
70 * All these URL's are intercepted by this implementation.
72 private static final String
[] INTERCEPTED_URLS
= { "private:factory/*" ,
80 * @member m_xMaster use this interceptor if he doesn't handle queried dispatch request
81 * @member m_xSlave we can forward all unhandled requests to this slave interceptor
82 * @member m_xFrame intercepted frame
83 * @member m_bDead there exist more than one way to finish an object of this class - we must know it sometimes
85 private com
.sun
.star
.frame
.XDispatchProvider m_xMaster
;
86 private com
.sun
.star
.frame
.XDispatchProvider m_xSlave
;
87 private com
.sun
.star
.frame
.XFrame m_xFrame
;
88 private boolean m_bIsActionListener
;
89 private boolean m_bIsRegistered
;
90 private boolean m_bDead
;
96 * Initialize the new interceptor. Given frame reference can be used to
97 * register this interceptor on it automatically later.
99 * @seealso startListening()
102 * this interceptor will register himself at this frame to intercept dispatched URLs
104 Interceptor(/*IN*/ com
.sun
.star
.frame
.XFrame xFrame
)
109 m_bIsRegistered
= false ;
110 m_bIsActionListener
= false ;
117 * start working as frame action listener really.
118 * We will be frame action listener here. In case
119 * we get a frame action which indicates, that we should
120 * update our interception. Because such using of an interceptor
121 * isn't guaranteed - in case a newer one was registered...
123 public void startListening()
125 com
.sun
.star
.frame
.XFrame xFrame
= null;
132 if (m_bIsActionListener
)
136 m_xFrame
.addFrameActionListener(this);
139 m_bIsActionListener
=true;
146 * In case we got a oneway listener callback - we had to use the office
147 * asynchronous then. This method is the callback from the started thread
148 * (started inside the original oneway method). We found all parameters of
149 * the original request packed inside a vector. Here we unpack it and
150 * call the right internal helper method, which implements the right
153 * @seealso frameAction()
154 * @seealso dispatch()
157 * indicates, which was the original request (identifies the
158 * original called method)
161 * the vector with all packed parameters of the original request
163 public void execOneway(/*IN*/ int nRequest
,/*IN*/ ArrayList
<Object
> lParams
)
171 // was it frameAction()?
172 if (nRequest
==OnewayExecutor
.REQUEST_FRAMEACTION
)
174 impl_frameAction((FrameActionEvent
) lParams
.get(0));
177 // was it dispatch()?
178 if (nRequest
==OnewayExecutor
.REQUEST_DISPATCH
)
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][];
183 OnewayExecutor
.decodeDispatch(
187 impl_dispatch(lOutURL
[0],lOutProps
[0]);
194 * callback for frame action events
195 * We use it to update our interception. Because if a new component was loaded into
196 * the frame or another interceptor was registered, we should refresh our connection
197 * to the frame. Otherwise we can't guarantee full functionality here.
199 * Note: Don't react synchronous in an asynchronous listener callback. So use a thread
200 * here to update anything.
202 * @seealso impl_frameAction()
205 * describes the action
207 public /*ONEWAY*/ void frameAction(/*IN*/ com
.sun
.star
.frame
.FrameActionEvent aEvent
)
215 boolean bHandle
= false;
216 switch(aEvent
.Action
.getValue())
218 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_ATTACHED_value
: bHandle
=true; break;
219 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_DETACHING_value
: bHandle
=true; break;
220 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_REATTACHED_value
: bHandle
=true; break;
221 // Don't react for CONTEXT_CHANGED here. Ok it indicates, that may another interceptor
222 // was registered at the frame ... but if we register ourself there - we get a context
223 // changed too :-( Best way to produce a never ending recursion ...
224 // May be that somewhere find a safe mechanism to detect own produced frame action events
226 case com
.sun
.star
.frame
.FrameAction
.CONTEXT_CHANGED_value
:
227 System
.out
.println("Time to update interception ... but may it will start a recursion. So I let it :-(");
232 // ignore some events
236 // pack the event and start thread - which call us back later
237 ArrayList
<Object
> lOutParams
= new ArrayList
<Object
>();
238 lOutParams
.add(aEvent
);
240 OnewayExecutor aExecutor
= new OnewayExecutor( this ,
241 OnewayExecutor
.REQUEST_FRAMEACTION
,
249 * Indicates using of us as an interceptor.
250 * Now we have to react for the requests, we are registered.
251 * That means: load new empty documents - triggered by the new menu of the office.
252 * Because it's oneway - use thread for loading!
254 * @seealso impl_dispatch()
257 * describes the document, which should be loaded
260 * optional parameters for loading
262 public /*ONEWAY*/ void dispatch(/*IN*/ com
.sun
.star
.util
.URL aURL
,/*IN*/ com
.sun
.star
.beans
.PropertyValue
[] lArguments
)
270 com
.sun
.star
.util
.URL
[] lInURL
= new com
.sun
.star
.util
.URL
[1];
271 com
.sun
.star
.beans
.PropertyValue
[][] lInArguments
= new com
.sun
.star
.beans
.PropertyValue
[1][];
273 lInArguments
[0] = lArguments
;
275 ArrayList
<Object
> lOutParams
= OnewayExecutor
.encodeDispatch(
278 OnewayExecutor aExecutor
= new OnewayExecutor( this ,
279 OnewayExecutor
.REQUEST_DISPATCH
,
288 * Internal callback for frame action events, triggered by the used
289 * OnewayExecutor thread we started in frameAction().
290 * We use it to update our interception on the internal saved frame.
293 * describes the action
295 private void impl_frameAction(/*IN*/ com
.sun
.star
.frame
.FrameActionEvent aEvent
)
303 // deregistration will be done every time...
304 // But may it's not necessary to establish a new registration!
305 // Don't look for ignoring actions - it was done already inside original frameAction() call!
306 boolean bRegister
= false;
308 // analyze the event and decide which reaction is useful
309 switch(aEvent
.Action
.getValue())
311 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_ATTACHED_value
: bRegister
= true ; break;
312 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_REATTACHED_value
: bRegister
= true ; break;
313 case com
.sun
.star
.frame
.FrameAction
.COMPONENT_DETACHING_value
: bRegister
= false; break;
316 com
.sun
.star
.frame
.XFrame xFrame
= null ;
317 boolean bIsRegistered
= false;
320 bIsRegistered
= m_bIsRegistered
;
321 m_bIsRegistered
= false;
325 com
.sun
.star
.frame
.XDispatchProviderInterception xRegistration
= UnoRuntime
.queryInterface(
326 com
.sun
.star
.frame
.XDispatchProviderInterception
.class,
329 if(xRegistration
==null)
333 xRegistration
.releaseDispatchProviderInterceptor(this);
338 xRegistration
.registerDispatchProviderInterceptor(this);
341 m_bIsRegistered
= true;
348 * Implementation of interface XDispatchProviderInterceptor
349 * These functions are used to build a list of interceptor objects
350 * connected in both ways.
351 * Searching for a right interceptor is made by forwarding any request
352 * from toppest master to lowest slave of this hierarchy.
353 * If an interceptor wish to handle the request he can break that
354 * and return himself as a dispatcher.
356 public com
.sun
.star
.frame
.XDispatchProvider
getSlaveDispatchProvider()
366 public void setSlaveDispatchProvider(com
.sun
.star
.frame
.XDispatchProvider xSlave
)
376 public com
.sun
.star
.frame
.XDispatchProvider
getMasterDispatchProvider()
386 public void setMasterDispatchProvider(com
.sun
.star
.frame
.XDispatchProvider xMaster
)
397 * Implementation of interface XDispatchProvider
398 * These functions are called from our master if he will not handle the outstanding request.
399 * Given parameter should be checked if they are right for us. If it's true, the returned
400 * dispatcher should be this implementation himself; otherwise call should be forwarded
404 * describes the request, which should be handled
407 * specifies the target frame for this request
409 * @param nSearchFlags
410 * optional search flags, if sTarget isn't a special one
412 * @return [XDispatch]
413 * a dispatch object, which can handle the given URL
416 public com
.sun
.star
.frame
.XDispatch
queryDispatch(/*IN*/ com
.sun
.star
.util
.URL aURL
,/*IN*/ String sTarget
,/*IN*/ int nSearchFlags
)
424 // intercept loading empty documents into new created frames
426 (sTarget
.compareTo ("_blank" ) == 0 ) &&
427 (aURL
.Complete
.startsWith("private:factory"))
430 System
.out
.println("intercept private:factory");
434 // intercept opening the SaveAs dialog
435 if (aURL
.Complete
.startsWith(".uno:SaveAs"))
437 System
.out
.println("intercept SaveAs by returning null!");
441 // intercept "File->Exit" inside the menu
443 (aURL
.Complete
.startsWith("slot:5300")) ||
444 (aURL
.Complete
.startsWith(".uno:Quit"))
447 System
.out
.println("intercept File->Exit");
454 return m_xSlave
.queryDispatch(aURL
, sTarget
, nSearchFlags
);
462 public com
.sun
.star
.frame
.XDispatch
[] queryDispatches(/*IN*/ com
.sun
.star
.frame
.DispatchDescriptor
[] lDescriptor
)
469 // Resolve any request separately by using own "dispatch()" method.
470 // Note: Don't pack return list if "null" objects occur!
471 int nCount
= lDescriptor
.length
;
472 com
.sun
.star
.frame
.XDispatch
[] lDispatcher
= new com
.sun
.star
.frame
.XDispatch
[nCount
];
473 for(int i
=0; i
<nCount
; ++i
)
475 lDispatcher
[i
] = queryDispatch(lDescriptor
[i
].FeatureURL
,
476 lDescriptor
[i
].FrameName
,
477 lDescriptor
[i
].SearchFlags
);
485 * This method is called if this interceptor "wins the request".
486 * We intercepted creation of new frames and loading of empty documents.
490 * describes the document
493 * optional arguments for loading
495 private void impl_dispatch(/*IN*/ com
.sun
.star
.util
.URL aURL
,/*IN*/ com
.sun
.star
.beans
.PropertyValue
[] lArguments
)
504 (aURL
.Complete
.startsWith("slot:5300")) ||
505 (aURL
.Complete
.startsWith(".uno:Quit"))
511 if (aURL
.Complete
.startsWith("private:factory"))
513 // Create view frame for showing loaded documents on demand.
514 // The visible state is necessary for JNI functionality to get the HWND and plug office
515 // inside a java window hierarchy!
516 DocumentView aNewView
= new DocumentView();
517 aNewView
.setVisible(true);
518 aNewView
.createFrame();
519 aNewView
.load(aURL
.Complete
,lArguments
);
526 * Notification of status listener isn't guaranteed (instead of listener on XNotifyingDispatch interface).
527 * So this interceptor doesn't support that really...
529 public /*ONEWAY*/ void addStatusListener(/*IN*/ com
.sun
.star
.frame
.XStatusListener xListener
,/*IN*/ com
.sun
.star
.util
.URL aURL
)
531 /* if (aURL.Complete.startsWith(".uno:SaveAs")==true)
533 com.sun.star.frame.FeatureStateEvent aEvent = new com.sun.star.frame.FeatureStateEvent(
542 System.out.println("interceptor disable SaveAs by listener notify");
543 xListener.statusChanged(aEvent);
550 public /*ONEWAY*/ void removeStatusListener(/*IN*/ com
.sun
.star
.frame
.XStatusListener xListener
,/*IN*/ com
.sun
.star
.util
.URL aURL
)
557 * Implements (optional!) optimization for interceptor mechanism.
558 * Any interceptor which provides this special interface is called automatically
559 * at registration time on this method. Returned URL's will be used to
560 * call this interceptor directly without calling his masters before, IF(!)
561 * following rules will be true:
562 * (1) every master supports this optional interface too
563 * (2) nobody of these masters wish to intercept same URL then this one
564 * This interceptor wish to intercept creation of new documents.
566 public String
[] getInterceptedURLs()
568 return INTERCEPTED_URLS
;
574 * This class listen on the intercepted frame to free all used resources on closing.
575 * We forget the reference to the frame only here. Deregistration
576 * isn't necessary here - because this frame dies and wish to be forgotten.
579 * must be our internal saved frame, on which we listen for frame action events
581 public /*ONEAY*/ void disposing(/*IN*/ com
.sun
.star
.lang
.EventObject aSource
)
587 if (m_xFrame
!=null && UnoRuntime
.areSame(aSource
.Source
,m_xFrame
))
589 m_bIsActionListener
= false;
599 * If this java application shutdown - we must cancel all current existing
600 * listener connections. Otherwise the office will run into some
601 * DisposedExceptions if it tries to use these forgotten listener references.
602 * And of course it can die doing that.
603 * We are registered at a central object to be informed if the VM will exit.
606 public void shutdown()
608 com
.sun
.star
.frame
.XFrame xFrame
= null ;
609 boolean bIsRegistered
= false;
610 boolean bIsActionListener
= false;
613 // don't react a second time here!
618 bIsRegistered
= m_bIsRegistered
;
619 m_bIsRegistered
= false;
621 bIsActionListener
= m_bIsActionListener
;
622 m_bIsActionListener
= false;
628 // it's a good idea to cancel listening for frame action events
629 // before(!) we deregister us as an interceptor.
630 // Because registration and deregistration of interceptor objects
631 // will force sending of frame action events...!
632 if (bIsActionListener
)
633 xFrame
.removeFrameActionListener(this);
637 com
.sun
.star
.frame
.XDispatchProviderInterception xRegistration
= UnoRuntime
.queryInterface(
638 com
.sun
.star
.frame
.XDispatchProviderInterception
.class,
641 if(xRegistration
!=null)
642 xRegistration
.releaseDispatchProviderInterceptor(this);
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */