/*
Copyright (C) 2002-2004 Renaud Pawlak <renaud@aopsys.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA */
package org.objectweb.jac.aspects.gui;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.objectweb.jac.core.Collaboration;
import org.objectweb.jac.core.rtti.AbstractMethodItem;
import org.objectweb.jac.core.rtti.ConstructorItem;
import org.objectweb.jac.core.rtti.MethodItem;
import org.objectweb.jac.util.Exceptions;
/**
* This class allows the programmer to invoke a given method in a new
* thread.
*
* <p>JAC programmers should use JAC to pass some attibutes of the
* current thread to the new thread.
*/
public class InvokeThread extends Thread {
static final Logger logger = Logger.getLogger("gui.threads");
static final Logger loggerInput = Logger.getLogger("gui.input");
InvokeEvent invoke;
Object returnValue;
Map parentAttributes;
Map attributes;
Map localAttributes;
String application;
boolean showResult = true;
/**
* Runs a method in a new thread and sets a display for this
* thread.
*
* @param invoke the invocation to perform
* @param attributes the attribute to set into the new thread
* collaboration
* @param localAttributes the local attribute to set into the new
* thread collaboration
*/
public static InvokeThread run(InvokeEvent invoke,
Map attributes,
Map localAttributes)
{
InvokeThread ret = new InvokeThread(invoke,attributes,localAttributes);
ret.start();
return ret;
}
/**
* Runs a method in a new thread and sets a display for this
* thread. Do not show any results on the display.
*
* @param invoke the invocation to perform
* @param attributes the attribute to set into the new thread
* collaboration
* @param localAttributes the local attribute to set into the new
* thread collaboration
*/
public static InvokeThread quietRun(InvokeEvent invoke,
Map attributes,
Map localAttributes)
{
InvokeThread ret = new InvokeThread(invoke,attributes,localAttributes);
ret.showResult = false;
ret.start();
return ret;
}
/**
* Runs a method in a new thread.
*
* @param invoke the invocation to perform
*/
public static InvokeThread run(InvokeEvent invoke) {
InvokeThread ret = new InvokeThread(invoke);
ret.start();
return ret;
}
private InvokeThread() {
setName("InvokeThread");
}
/**
* Creates a new thread that will invoke a method when started.
*
* <p>The programmer should use the static <code>run</code>
* methods.
*
* @param invoke the invocation to perform
*/
public InvokeThread(InvokeEvent invoke) {
this(invoke,null,null);
}
/**
* Creates a new thread that will invoke a method when started.
*
* <p>The programmer should use the static <code>run</code>
* methods.
*
* @param invoke the invocation to perform
* @param attributes attributes to add to the context
* before invoking the method (may be null)
* @param localAttributes local attributes to add to the context (may be null)
*/
public InvokeThread(InvokeEvent invoke,
Map attributes,
Map localAttributes)
{
this();
this.invoke = invoke;
this.parentAttributes = new HashMap(Collaboration.get().getAttributes());
this.application = Collaboration.get().getCurApp();
this.attributes = attributes;
this.localAttributes = localAttributes;
}
/**
* Runs the thread (and invoke the method that was given to the
* constructor with the right display in the collaboration).
*
* <p>Do not call this method directly.
*/
public void run() {
Collaboration collab = Collaboration.get();
Object[] parameters = invoke.getParameters();
AbstractMethodItem method = invoke.getMethod();
Object substance = invoke.getSubstance();
logger.info("invokeThread "+this+": "+invoke);
collab.addAttributes(parentAttributes);
logger.debug("application = "+application);
collab.setCurApp(application);
if (attributes != null) {
collab.addAttributes(attributes);
}
if (localAttributes != null) {
collab.addAttributes(localAttributes);
}
DisplayContext context = (DisplayContext)collab
.getAttribute(GuiAC.DISPLAY_CONTEXT);
CustomizedDisplay display = context.getDisplay();
try {
logger.debug("InvokeThread " + invoke);
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != invoke.getParameters().length)
throw new RuntimeException("Wrong number of parameters ("+
parameters.length+") for "+method);
for (int i=0; i<parameters.length; i++) {
if (parameters[i]==null) {
if (paramTypes[i] == float.class) {
parameters[i] = new Float(0.0);
} else if (paramTypes[i] == long.class) {
parameters[i] = new Long(0);
} else if (paramTypes[i] == double.class) {
parameters[i] = new Double(0.0);
} else if (paramTypes[i] == byte.class) {
parameters[i] = new Byte((byte)0);
} else if (paramTypes[i] == char.class) {
parameters[i] = new Character(' ');
} else if (paramTypes[i] == short.class) {
parameters[i] = new Short((short)0);
} else if (paramTypes[i] == int.class) {
parameters[i] = new Integer(0);
} else if (paramTypes[i] == boolean.class) {
parameters[i] = Boolean.FALSE;
}
}
}
if (method instanceof ConstructorItem) {
returnValue = ((ConstructorItem)method).newInstance(parameters);
} else {
returnValue = ((MethodItem)method).invoke(substance, parameters);
}
if (display != null) {
display.onInvocationReturn(substance,method);
}
List hooks = (List)method.getAttribute(GuiAC.POST_INVOKE_HOOKS);
if (hooks!=null) {
Iterator i = hooks.iterator();
while (i.hasNext()) {
AbstractMethodItem hook = (AbstractMethodItem)i.next();
try {
loggerInput.debug("Invoking post hook "+hook.getName());
hook.invoke(
null,
new Object[] {invoke});
} catch (Exception e) {
loggerInput.error("Post invoke hook for "+
substance+"."+
method.getFullName()+" failed",e);
}
}
}
boolean ignoreReturnedValue = GuiAC.getIgnoreReturnedValue(method);
if (method.getType() != void.class && !ignoreReturnedValue) {
if (display != null && showResult) {
if (collab.getAttribute(GuiAC.OPEN_VIEW)!=null) {
display.openView(returnValue);
} else {
if(method.getAttribute(GuiAC.SMALL_TARGET_CONTAINER)!=null) {
EventHandler.get().onSelection(context,method,returnValue,null,null,false);
} else if (returnValue instanceof HandlerResult) {
EventHandler.get().handleResult(context,(HandlerResult)returnValue);
} else {
display.show(returnValue);
}
}
}
} else {
if (display != null && showResult)
display.refresh();
}
} catch (Exception e) {
Throwable te = Exceptions.getTargetException(e);
logger.debug(this+" TargetException is "+te);
if (te instanceof TimeoutException) {
logger.debug(this+" Timeout");
DialogView dialog = ((TimeoutException)te).getDialog();
display.closeWindow(dialog,false);
display.addTimedoutDialog(dialog);
} else if (display != null && showResult) {
if (!(te instanceof org.objectweb.jac.util.VoidException))
display.showModal(
te,
"Error: "+te.getClass().getName(),
null,
context.getWindow(),
false,false,true);
else
display.refresh();
}
}
logger.debug("invokeThread done "+this+": "+
substance+"."+method+Arrays.asList(parameters));
}
Object getReturnValue() {
return returnValue;
}
}
class NotAvailableException extends Exception {}