summaryrefslogtreecommitdiffstats
path: root/odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java')
-rw-r--r--odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java655
1 files changed, 655 insertions, 0 deletions
diff --git a/odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java b/odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java
new file mode 100644
index 000000000..380f8da3c
--- /dev/null
+++ b/odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java
@@ -0,0 +1,655 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ *
+ * The Contents of this file are made available subject to the terms of
+ * the BSD license.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+// Imports
+
+import java.util.ArrayList;
+
+import com.sun.star.frame.FrameActionEvent;
+import com.sun.star.uno.UnoRuntime;
+
+// Implementation
+
+/*
+ * This class can be used to intercept dispatched URL's
+ * on any frame used in this demo application.
+ * It intercept all URL's which try to create a new empty frame.
+ * (e.g. "private:factory/swriter")
+ * Nobody can guarantee that this interception will be really used -
+ * because another interceptor (registered at a later time then this one!)
+ * will be called before this one.
+ * Implementation is executed inside a new thread to prevent application
+ * against possible deadlocks. This deadlocks can occur if
+ * synchronous/asynchronous ... normal ones and oneway calls are mixed.
+ * Notifications of listener will be oneway mostly - her reactions can
+ * be synchronous then. => deadlocks are possible
+ */
+public class Interceptor implements com.sun.star.frame.XFrameActionListener,
+ com.sun.star.frame.XDispatchProviderInterceptor,
+ com.sun.star.frame.XDispatch,
+ com.sun.star.frame.XInterceptorInfo,
+ IShutdownListener,
+ IOnewayLink
+{
+
+
+ /**
+ * const
+ * All these URL's are intercepted by this implementation.
+ */
+ private static final String[] INTERCEPTED_URLS = { "private:factory/*" ,
+ ".uno:SaveAs" ,
+ "slot:5300" ,
+ ".uno:Quit" };
+
+
+
+ /*
+ * @member m_xMaster use this interceptor if he doesn't handle queried dispatch request
+ * @member m_xSlave we can forward all unhandled requests to this slave interceptor
+ * @member m_xFrame intercepted frame
+ * @member m_bDead there exist more than one way to finish an object of this class - we must know it sometimes
+ */
+ private com.sun.star.frame.XDispatchProvider m_xMaster ;
+ private com.sun.star.frame.XDispatchProvider m_xSlave ;
+ private com.sun.star.frame.XFrame m_xFrame ;
+ private boolean m_bIsActionListener ;
+ private boolean m_bIsRegistered ;
+ private boolean m_bDead ;
+
+
+
+ /*
+ * ctor
+ * Initialize the new interceptor. Given frame reference can be used to
+ * register this interceptor on it automatically later.
+ *
+ * @seealso startListening()
+ *
+ * @param xFrame
+ * this interceptor will register himself at this frame to intercept dispatched URLs
+ */
+ Interceptor(/*IN*/ com.sun.star.frame.XFrame xFrame)
+ {
+ m_xFrame = xFrame ;
+ m_xSlave = null ;
+ m_xMaster = null ;
+ m_bIsRegistered = false ;
+ m_bIsActionListener = false ;
+ m_bDead = false ;
+ }
+
+
+
+ /*
+ * start working as frame action listener really.
+ * We will be frame action listener here. In case
+ * we get a frame action which indicates, that we should
+ * update our interception. Because such using of an interceptor
+ * isn't guaranteed - in case a newer one was registered...
+ */
+ public void startListening()
+ {
+ com.sun.star.frame.XFrame xFrame = null;
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ if (m_xFrame==null)
+ return;
+ if (m_bIsActionListener)
+ return;
+ xFrame = m_xFrame;
+ }
+ m_xFrame.addFrameActionListener(this);
+ synchronized(this)
+ {
+ m_bIsActionListener=true;
+ }
+ }
+
+
+
+ /*
+ * In case we got a oneway listener callback - we had to use the office
+ * asynchronous then. This method is the callback from the started thread
+ * (started inside the original oneway method). We found all parameters of
+ * the original request packed inside a vector. Here we unpack it and
+ * call the right internal helper method, which implements the right
+ * functionality.
+ *
+ * @seealso frameAction()
+ * @seealso dispatch()
+ *
+ * @param nRequest
+ * indicates, which was the original request (identifies the
+ * original called method)
+ *
+ * @param lParams
+ * the vector with all packed parameters of the original request
+ */
+ public void execOneway(/*IN*/ int nRequest,/*IN*/ ArrayList<Object> lParams )
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ }
+
+ // was it frameAction()?
+ if (nRequest==OnewayExecutor.REQUEST_FRAMEACTION)
+ {
+ impl_frameAction((FrameActionEvent) lParams.get(0));
+ }
+ else
+ // was it dispatch()?
+ if (nRequest==OnewayExecutor.REQUEST_DISPATCH)
+ {
+ com.sun.star.util.URL[] lOutURL = new com.sun.star.util.URL[1];
+ com.sun.star.beans.PropertyValue[][] lOutProps = new com.sun.star.beans.PropertyValue[1][];
+
+ OnewayExecutor.decodeDispatch(
+ lParams ,
+ lOutURL ,
+ lOutProps );
+ impl_dispatch(lOutURL[0],lOutProps[0]);
+ }
+ }
+
+
+
+ /*
+ * callback for frame action events
+ * We use it to update our interception. Because if a new component was loaded into
+ * the frame or another interceptor was registered, we should refresh our connection
+ * to the frame. Otherwise we can't guarantee full functionality here.
+ *
+ * Note: Don't react synchronous in an asynchronous listener callback. So use a thread
+ * here to update anything.
+ *
+ * @seealso impl_frameAction()
+ *
+ * @param aEvent
+ * describes the action
+ */
+ public /*ONEWAY*/ void frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ }
+
+ boolean bHandle = false;
+ switch(aEvent.Action.getValue())
+ {
+ case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value : bHandle=true; break;
+ case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value : bHandle=true; break;
+ case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bHandle=true; break;
+ // Don't react for CONTEXT_CHANGED here. Ok it indicates, that may another interceptor
+ // was registered at the frame ... but if we register ourself there - we get a context
+ // changed too :-( Best way to produce a never ending recursion ...
+ // May be that somewhere find a safe mechanism to detect own produced frame action events
+ // and ignore it.
+ case com.sun.star.frame.FrameAction.CONTEXT_CHANGED_value :
+ System.out.println("Time to update interception ... but may it will start a recursion. So I let it :-(");
+ bHandle=false;
+ break;
+ }
+
+ // ignore some events
+ if (! bHandle)
+ return;
+
+ // pack the event and start thread - which call us back later
+ ArrayList<Object> lOutParams = new ArrayList<Object>();
+ lOutParams.add(aEvent);
+
+ OnewayExecutor aExecutor = new OnewayExecutor( this ,
+ OnewayExecutor.REQUEST_FRAMEACTION ,
+ lOutParams );
+ aExecutor.start();
+ }
+
+
+
+ /*
+ * Indicates using of us as an interceptor.
+ * Now we have to react for the requests, we are registered.
+ * That means: load new empty documents - triggered by the new menu of the office.
+ * Because it's oneway - use thread for loading!
+ *
+ * @seealso impl_dispatch()
+ *
+ * @param aURL
+ * describes the document, which should be loaded
+ *
+ * @param lArguments
+ * optional parameters for loading
+ */
+ public /*ONEWAY*/ void dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ }
+
+ com.sun.star.util.URL[] lInURL = new com.sun.star.util.URL[1];
+ com.sun.star.beans.PropertyValue[][] lInArguments = new com.sun.star.beans.PropertyValue[1][];
+ lInURL[0] = aURL ;
+ lInArguments[0] = lArguments;
+
+ ArrayList<Object> lOutParams = OnewayExecutor.encodeDispatch(
+ lInURL ,
+ lInArguments );
+ OnewayExecutor aExecutor = new OnewayExecutor( this ,
+ OnewayExecutor.REQUEST_DISPATCH ,
+ lOutParams );
+ aExecutor.start();
+ }
+
+
+
+
+ /*
+ * Internal callback for frame action events, triggered by the used
+ * OnewayExecutor thread we started in frameAction().
+ * We use it to update our interception on the internal saved frame.
+ *
+ * @param aEvent
+ * describes the action
+ */
+ private void impl_frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ }
+
+ // deregistration will be done every time...
+ // But may it's not necessary to establish a new registration!
+ // Don't look for ignoring actions - it was done already inside original frameAction() call!
+ boolean bRegister = false;
+
+ // analyze the event and decide which reaction is useful
+ switch(aEvent.Action.getValue())
+ {
+ case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value : bRegister = true ; break;
+ case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bRegister = true ; break;
+ case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value : bRegister = false; break;
+ }
+
+ com.sun.star.frame.XFrame xFrame = null ;
+ boolean bIsRegistered = false;
+ synchronized(this)
+ {
+ bIsRegistered = m_bIsRegistered;
+ m_bIsRegistered = false;
+ xFrame = m_xFrame;
+ }
+
+ com.sun.star.frame.XDispatchProviderInterception xRegistration = UnoRuntime.queryInterface(
+ com.sun.star.frame.XDispatchProviderInterception.class,
+ xFrame);
+
+ if(xRegistration==null)
+ return;
+
+ if (bIsRegistered)
+ xRegistration.releaseDispatchProviderInterceptor(this);
+
+ if (! bRegister)
+ return;
+
+ xRegistration.registerDispatchProviderInterceptor(this);
+ synchronized(this)
+ {
+ m_bIsRegistered = true;
+ }
+ }
+
+
+
+ /*
+ * Implementation of interface XDispatchProviderInterceptor
+ * These functions are used to build a list of interceptor objects
+ * connected in both ways.
+ * Searching for a right interceptor is made by forwarding any request
+ * from toppest master to lowest slave of this hierarchy.
+ * If an interceptor wish to handle the request he can break that
+ * and return himself as a dispatcher.
+ */
+ public com.sun.star.frame.XDispatchProvider getSlaveDispatchProvider()
+ {
+ synchronized(this)
+ {
+ return m_xSlave;
+ }
+ }
+
+
+
+ public void setSlaveDispatchProvider(com.sun.star.frame.XDispatchProvider xSlave)
+ {
+ synchronized(this)
+ {
+ m_xSlave = xSlave;
+ }
+ }
+
+
+
+ public com.sun.star.frame.XDispatchProvider getMasterDispatchProvider()
+ {
+ synchronized(this)
+ {
+ return m_xMaster;
+ }
+ }
+
+
+
+ public void setMasterDispatchProvider(com.sun.star.frame.XDispatchProvider xMaster)
+ {
+ synchronized(this)
+ {
+ m_xMaster = xMaster;
+ }
+ }
+
+
+
+ /*
+ * Implementation of interface XDispatchProvider
+ * These functions are called from our master if he will not handle the outstanding request.
+ * Given parameter should be checked if they are right for us. If it's true, the returned
+ * dispatcher should be this implementation himself; otherwise call should be forwarded
+ * to the slave.
+ *
+ * @param aURL
+ * describes the request, which should be handled
+ *
+ * @param sTarget
+ * specifies the target frame for this request
+ *
+ * @param nSearchFlags
+ * optional search flags, if sTarget isn't a special one
+ *
+ * @return [XDispatch]
+ * a dispatch object, which can handle the given URL
+ * May be NULL!
+ */
+ public com.sun.star.frame.XDispatch queryDispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ String sTarget,/*IN*/ int nSearchFlags)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return null;
+ }
+
+ // intercept loading empty documents into new created frames
+ if(
+ (sTarget.compareTo ("_blank" ) == 0 ) &&
+ (aURL.Complete.startsWith("private:factory"))
+ )
+ {
+ System.out.println("intercept private:factory");
+ return this;
+ }
+
+ // intercept opening the SaveAs dialog
+ if (aURL.Complete.startsWith(".uno:SaveAs"))
+ {
+ System.out.println("intercept SaveAs by returning null!");
+ return null;
+ }
+
+ // intercept "File->Exit" inside the menu
+ if (
+ (aURL.Complete.startsWith("slot:5300")) ||
+ (aURL.Complete.startsWith(".uno:Quit"))
+ )
+ {
+ System.out.println("intercept File->Exit");
+ return this;
+ }
+
+ synchronized(this)
+ {
+ if (m_xSlave!=null)
+ return m_xSlave.queryDispatch(aURL, sTarget, nSearchFlags);
+ }
+
+ return null;
+ }
+
+
+
+ public com.sun.star.frame.XDispatch[] queryDispatches(/*IN*/ com.sun.star.frame.DispatchDescriptor[] lDescriptor)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return null;
+ }
+ // Resolve any request separately by using own "dispatch()" method.
+ // Note: Don't pack return list if "null" objects occur!
+ int nCount = lDescriptor.length;
+ com.sun.star.frame.XDispatch[] lDispatcher = new com.sun.star.frame.XDispatch[nCount];
+ for(int i=0; i<nCount; ++i)
+ {
+ lDispatcher[i] = queryDispatch(lDescriptor[i].FeatureURL ,
+ lDescriptor[i].FrameName ,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+ }
+
+
+
+ /*
+ * This method is called if this interceptor "wins the request".
+ * We intercepted creation of new frames and loading of empty documents.
+ * Do it now.
+ *
+ * @param aURL
+ * describes the document
+ *
+ * @param lArguments
+ * optional arguments for loading
+ */
+ private void impl_dispatch(/*IN*/ com.sun.star.util.URL aURL,/*IN*/ com.sun.star.beans.PropertyValue[] lArguments)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ }
+
+ if (
+ (aURL.Complete.startsWith("slot:5300")) ||
+ (aURL.Complete.startsWith(".uno:Quit"))
+ )
+ {
+ System.exit(0);
+ }
+ else
+ if (aURL.Complete.startsWith("private:factory"))
+ {
+ // Create view frame for showing loaded documents on demand.
+ // The visible state is necessary for JNI functionality to get the HWND and plug office
+ // inside a java window hierarchy!
+ DocumentView aNewView = new DocumentView();
+ aNewView.setVisible(true);
+ aNewView.createFrame();
+ aNewView.load(aURL.Complete,lArguments);
+ }
+ }
+
+
+
+ /*
+ * Notification of status listener isn't guaranteed (instead of listener on XNotifyingDispatch interface).
+ * So this interceptor doesn't support that really...
+ */
+ public /*ONEWAY*/ void addStatusListener(/*IN*/ com.sun.star.frame.XStatusListener xListener,/*IN*/ com.sun.star.util.URL aURL)
+ {
+/* if (aURL.Complete.startsWith(".uno:SaveAs")==true)
+ {
+ com.sun.star.frame.FeatureStateEvent aEvent = new com.sun.star.frame.FeatureStateEvent(
+ this,
+ aURL,
+ "",
+ false,
+ false,
+ null);
+ if (xListener!=null)
+ {
+ System.out.println("interceptor disable SaveAs by listener notify");
+ xListener.statusChanged(aEvent);
+ }
+ }*/
+ }
+
+
+
+ public /*ONEWAY*/ void removeStatusListener(/*IN*/ com.sun.star.frame.XStatusListener xListener,/*IN*/ com.sun.star.util.URL aURL)
+ {
+ }
+
+
+
+ /*
+ * Implements (optional!) optimization for interceptor mechanism.
+ * Any interceptor which provides this special interface is called automatically
+ * at registration time on this method. Returned URL's will be used to
+ * call this interceptor directly without calling his masters before, IF(!)
+ * following rules will be true:
+ * (1) every master supports this optional interface too
+ * (2) nobody of these masters wish to intercept same URL then this one
+ * This interceptor wish to intercept creation of new documents.
+ */
+ public String[] getInterceptedURLs()
+ {
+ return INTERCEPTED_URLS;
+ }
+
+
+
+ /*
+ * This class listen on the intercepted frame to free all used resources on closing.
+ * We forget the reference to the frame only here. Deregistration
+ * isn't necessary here - because this frame dies and wish to be forgotten.
+ *
+ * @param aSource
+ * must be our internal saved frame, on which we listen for frame action events
+ */
+ public /*ONEAY*/ void disposing(/*IN*/ com.sun.star.lang.EventObject aSource)
+ {
+ synchronized(this)
+ {
+ if (m_bDead)
+ return;
+ if (m_xFrame!=null && UnoRuntime.areSame(aSource.Source,m_xFrame))
+ {
+ m_bIsActionListener = false;
+ m_xFrame = null ;
+ }
+ }
+ shutdown();
+ }
+
+
+
+ /*
+ * If this java application shutdown - we must cancel all current existing
+ * listener connections. Otherwise the office will run into some
+ * DisposedExceptions if it tries to use these forgotten listener references.
+ * And of course it can die doing that.
+ * We are registered at a central object to be informed if the VM will exit.
+ * So we can react.
+ */
+ public void shutdown()
+ {
+ com.sun.star.frame.XFrame xFrame = null ;
+ boolean bIsRegistered = false;
+ boolean bIsActionListener = false;
+ synchronized(this)
+ {
+ // don't react a second time here!
+ if (m_bDead)
+ return;
+ m_bDead = true;
+
+ bIsRegistered = m_bIsRegistered;
+ m_bIsRegistered = false;
+
+ bIsActionListener = m_bIsActionListener;
+ m_bIsActionListener = false;
+
+ xFrame = m_xFrame;
+ m_xFrame = null;
+ }
+
+ // it's a good idea to cancel listening for frame action events
+ // before(!) we deregister us as an interceptor.
+ // Because registration and deregistration of interceptor objects
+ // will force sending of frame action events...!
+ if (bIsActionListener)
+ xFrame.removeFrameActionListener(this);
+
+ if (bIsRegistered)
+ {
+ com.sun.star.frame.XDispatchProviderInterception xRegistration = UnoRuntime.queryInterface(
+ com.sun.star.frame.XDispatchProviderInterception.class,
+ xFrame);
+
+ if(xRegistration!=null)
+ xRegistration.releaseDispatchProviderInterceptor(this);
+ }
+
+ xFrame = null;
+
+ synchronized(this)
+ {
+ m_xMaster = null;
+ m_xSlave = null;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */