diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /odk/examples/DevelopersGuide/OfficeDev/DesktopEnvironment/Interceptor.java | |
parent | Initial commit. (diff) | |
download | libreoffice-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.java | 655 |
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: */ |