Get the style color and number just once
[LibreOffice.git] / odk / examples / DevelopersGuide / OfficeDev / DesktopEnvironment / Interceptor.java
blob380f8da3cf5d0509f80277d463521cc2cc6b7cfa
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
4 * The Contents of this file are made available subject to the terms of
5 * the BSD license.
7 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
36 // Imports
38 import java.util.ArrayList;
40 import com.sun.star.frame.FrameActionEvent;
41 import com.sun.star.uno.UnoRuntime;
43 // Implementation
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,
63 IShutdownListener,
64 IOnewayLink
68 /**
69 * const
70 * All these URL's are intercepted by this implementation.
72 private static final String[] INTERCEPTED_URLS = { "private:factory/*" ,
73 ".uno:SaveAs" ,
74 "slot:5300" ,
75 ".uno:Quit" };
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 ;
95 * ctor
96 * Initialize the new interceptor. Given frame reference can be used to
97 * register this interceptor on it automatically later.
99 * @seealso startListening()
101 * @param xFrame
102 * this interceptor will register himself at this frame to intercept dispatched URLs
104 Interceptor(/*IN*/ com.sun.star.frame.XFrame xFrame)
106 m_xFrame = xFrame ;
107 m_xSlave = null ;
108 m_xMaster = null ;
109 m_bIsRegistered = false ;
110 m_bIsActionListener = false ;
111 m_bDead = 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;
126 synchronized(this)
128 if (m_bDead)
129 return;
130 if (m_xFrame==null)
131 return;
132 if (m_bIsActionListener)
133 return;
134 xFrame = m_xFrame;
136 m_xFrame.addFrameActionListener(this);
137 synchronized(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
151 * functionality.
153 * @seealso frameAction()
154 * @seealso dispatch()
156 * @param nRequest
157 * indicates, which was the original request (identifies the
158 * original called method)
160 * @param lParams
161 * the vector with all packed parameters of the original request
163 public void execOneway(/*IN*/ int nRequest,/*IN*/ ArrayList<Object> lParams )
165 synchronized(this)
167 if (m_bDead)
168 return;
171 // was it frameAction()?
172 if (nRequest==OnewayExecutor.REQUEST_FRAMEACTION)
174 impl_frameAction((FrameActionEvent) lParams.get(0));
176 else
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(
184 lParams ,
185 lOutURL ,
186 lOutProps );
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()
204 * @param aEvent
205 * describes the action
207 public /*ONEWAY*/ void frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
209 synchronized(this)
211 if (m_bDead)
212 return;
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
225 // and ignore it.
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 :-(");
228 bHandle=false;
229 break;
232 // ignore some events
233 if (! bHandle)
234 return;
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 ,
242 lOutParams );
243 aExecutor.start();
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()
256 * @param aURL
257 * describes the document, which should be loaded
259 * @param lArguments
260 * optional parameters for loading
262 public /*ONEWAY*/ void dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments)
264 synchronized(this)
266 if (m_bDead)
267 return;
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][];
272 lInURL[0] = aURL ;
273 lInArguments[0] = lArguments;
275 ArrayList<Object> lOutParams = OnewayExecutor.encodeDispatch(
276 lInURL ,
277 lInArguments );
278 OnewayExecutor aExecutor = new OnewayExecutor( this ,
279 OnewayExecutor.REQUEST_DISPATCH ,
280 lOutParams );
281 aExecutor.start();
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.
292 * @param aEvent
293 * describes the action
295 private void impl_frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
297 synchronized(this)
299 if (m_bDead)
300 return;
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;
318 synchronized(this)
320 bIsRegistered = m_bIsRegistered;
321 m_bIsRegistered = false;
322 xFrame = m_xFrame;
325 com.sun.star.frame.XDispatchProviderInterception xRegistration = UnoRuntime.queryInterface(
326 com.sun.star.frame.XDispatchProviderInterception.class,
327 xFrame);
329 if(xRegistration==null)
330 return;
332 if (bIsRegistered)
333 xRegistration.releaseDispatchProviderInterceptor(this);
335 if (! bRegister)
336 return;
338 xRegistration.registerDispatchProviderInterceptor(this);
339 synchronized(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()
358 synchronized(this)
360 return m_xSlave;
366 public void setSlaveDispatchProvider(com.sun.star.frame.XDispatchProvider xSlave)
368 synchronized(this)
370 m_xSlave = xSlave;
376 public com.sun.star.frame.XDispatchProvider getMasterDispatchProvider()
378 synchronized(this)
380 return m_xMaster;
386 public void setMasterDispatchProvider(com.sun.star.frame.XDispatchProvider xMaster)
388 synchronized(this)
390 m_xMaster = 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
401 * to the slave.
403 * @param aURL
404 * describes the request, which should be handled
406 * @param sTarget
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
414 * May be NULL!
416 public com.sun.star.frame.XDispatch queryDispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ String sTarget,/*IN*/ int nSearchFlags)
418 synchronized(this)
420 if (m_bDead)
421 return null;
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");
431 return this;
434 // intercept opening the SaveAs dialog
435 if (aURL.Complete.startsWith(".uno:SaveAs"))
437 System.out.println("intercept SaveAs by returning null!");
438 return null;
441 // intercept "File->Exit" inside the menu
442 if (
443 (aURL.Complete.startsWith("slot:5300")) ||
444 (aURL.Complete.startsWith(".uno:Quit"))
447 System.out.println("intercept File->Exit");
448 return this;
451 synchronized(this)
453 if (m_xSlave!=null)
454 return m_xSlave.queryDispatch(aURL, sTarget, nSearchFlags);
457 return null;
462 public com.sun.star.frame.XDispatch[] queryDispatches(/*IN*/ com.sun.star.frame.DispatchDescriptor[] lDescriptor)
464 synchronized(this)
466 if (m_bDead)
467 return null;
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);
479 return lDispatcher;
485 * This method is called if this interceptor "wins the request".
486 * We intercepted creation of new frames and loading of empty documents.
487 * Do it now.
489 * @param aURL
490 * describes the document
492 * @param lArguments
493 * optional arguments for loading
495 private void impl_dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments)
497 synchronized(this)
499 if (m_bDead)
500 return;
503 if (
504 (aURL.Complete.startsWith("slot:5300")) ||
505 (aURL.Complete.startsWith(".uno:Quit"))
508 System.exit(0);
510 else
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(
534 this,
535 aURL,
537 false,
538 false,
539 null);
540 if (xListener!=null)
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.
578 * @param aSource
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)
583 synchronized(this)
585 if (m_bDead)
586 return;
587 if (m_xFrame!=null && UnoRuntime.areSame(aSource.Source,m_xFrame))
589 m_bIsActionListener = false;
590 m_xFrame = null ;
593 shutdown();
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.
604 * So we can react.
606 public void shutdown()
608 com.sun.star.frame.XFrame xFrame = null ;
609 boolean bIsRegistered = false;
610 boolean bIsActionListener = false;
611 synchronized(this)
613 // don't react a second time here!
614 if (m_bDead)
615 return;
616 m_bDead = true;
618 bIsRegistered = m_bIsRegistered;
619 m_bIsRegistered = false;
621 bIsActionListener = m_bIsActionListener;
622 m_bIsActionListener = false;
624 xFrame = m_xFrame;
625 m_xFrame = null;
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);
635 if (bIsRegistered)
637 com.sun.star.frame.XDispatchProviderInterception xRegistration = UnoRuntime.queryInterface(
638 com.sun.star.frame.XDispatchProviderInterception.class,
639 xFrame);
641 if(xRegistration!=null)
642 xRegistration.releaseDispatchProviderInterceptor(this);
645 xFrame = null;
647 synchronized(this)
649 m_xMaster = null;
650 m_xSlave = null;
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */