blob: 3636525f25b5ed997a066c5f2bfbd72349a90822 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// __________ Imports __________
import com.sun.star.uno.UnoRuntime;
import java.lang.*;
import javax.swing.*;
import java.util.Vector;
// __________ Implementation __________
/**
* This class can be used to intercept dispatched URL's
* on any frame used in this demo application.
* It intercept all URL's wich try to create a new empty frame.
* (e.g. "private:factory/swriter")
* Nobody can guarantee that this interception will be realy 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 occure 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
*
* @author Andreas Schlüns
* @created 06.03.2002 09:38
*/
public class Interceptor implements com.sun.star.frame.XFrameActionListener,
com.sun.star.frame.XDispatchProviderInterceptor,
com.sun.star.frame.XDispatchProvider,
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 then 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 automaticly 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 realy.
* 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 interecptor
* 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==true)
return;
xFrame = m_xFrame;
}
m_xFrame.addFrameActionListener(this);
synchronized(this)
{
m_bIsActionListener=true;
}
}
//_____________________
/**
* In case we got an 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
* funtionality.
*
* @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*/ Vector lParams )
{
synchronized(this)
{
if (m_bDead)
return;
}
// was it frameAction()?
if (nRequest==OnewayExecutor.REQUEST_FRAMEACTION)
{
com.sun.star.frame.FrameActionEvent[] lOutAction = new com.sun.star.frame.FrameActionEvent[1];
Vector[] lInParams = new Vector[1];
lInParams[0] = lParams;
OnewayExecutor.codeFrameAction( OnewayExecutor.DECODE_PARAMS ,
lInParams ,
lOutAction );
impl_frameAction(lOutAction[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][];
Vector[] lInParams = new Vector[1];
lInParams[0] = lParams;
OnewayExecutor.codeDispatch( OnewayExecutor.DECODE_PARAMS ,
lInParams ,
lOutURL ,
lOutProps );
impl_dispatch(lOutURL[0],lOutProps[0]);
}
}
// ____________________
/**
* call back 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. Otherwhise 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
Vector[] lOutParams = new Vector[1];
com.sun.star.frame.FrameActionEvent[] lInAction = new com.sun.star.frame.FrameActionEvent[1];
lInAction[0] = aEvent;
OnewayExecutor.codeFrameAction( OnewayExecutor.ENCODE_PARAMS ,
lOutParams ,
lInAction );
OnewayExecutor aExecutor = new OnewayExecutor( (IOnewayLink)this ,
OnewayExecutor.REQUEST_FRAMEACTION ,
lOutParams[0] );
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;
}
Vector[] lOutParams = new Vector[1];
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;
OnewayExecutor.codeDispatch( OnewayExecutor.ENCODE_PARAMS ,
lOutParams ,
lInURL ,
lInArguments );
OnewayExecutor aExecutor = new OnewayExecutor( (IOnewayLink)this ,
OnewayExecutor.REQUEST_DISPATCH ,
lOutParams[0] );
aExecutor.start();
}
//_____________________
/**
* Internal call back 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
*/
public void impl_frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
{
synchronized(this)
{
if (m_bDead)
return;
}
// deregistration will be done everytime ...
// But may it's not neccessary 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 usefull
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 = (com.sun.star.frame.XDispatchProviderInterception)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 whish 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 willn't 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") == true)
)
{
System.out.println("intercept private:factory");
return this;
}
// intercept opening the SaveAs dialog
if (aURL.Complete.startsWith(".uno:SaveAs") == true)
{
System.out.println("intercept SaveAs by returning null!");
return null;
}
// intercept "File->Exit" inside the menu
if (
(aURL.Complete.startsWith("slot:5300") == true) ||
(aURL.Complete.startsWith(".uno:Quit") == true)
)
{
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 seperatly by using own "dispatch()" method.
// Note: Don't pack return list if "null" objects occure!
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
*/
public 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") == true) ||
(aURL.Complete.startsWith(".uno:Quit") == true)
)
{
System.exit(0);
}
else
if (aURL.Complete.startsWith("private:factory") == true)
{
// Create view frame for showing loaded documents on demand.
// The visible state is neccessary 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 realy ...
*/
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 SavAs 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 automaticly
* 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 whish to intercept same URL then this one
* This interceptor whish to intercept creation of new documents.
*/
public String[] getInterceptedURLs()
{
return INTERCEPTED_URLS;
}
// ____________________
/**
* This class listen on the intercepted frame to free all used ressources on closing.
* We forget the reference to the frame only here. Deregistration
* isn't neccessary here - because this frame dies and wish to forgoten.
*
* @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. Otherwhise 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 idead to cancel listening for frame action events
// before(!) we deregister us as an interceptor.
// Because registration and deregistratio nof interceptor objects
// will force sending of frame action events ...!
if (bIsActionListener)
xFrame.removeFrameActionListener(this);
if (bIsRegistered)
{
com.sun.star.frame.XDispatchProviderInterception xRegistration = (com.sun.star.frame.XDispatchProviderInterception)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;
}
}
}