package jadex.bpmn.runtime;
import jadex.bpmn.model.MBpmnModel;
import jadex.bpmn.model.MIdElement;
import jadex.bpmn.model.MSubProcess;
import jadex.commons.SReflect;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* The thread context represents the current execution context of a thread
* including sibling threads and parent contexts (for nested sub processes).
*/
public class ThreadContext
{
//-------- attributes --------
/** The model element (process or sub process). */
protected MIdElement model;
/** The initiating thread (if any). */
protected ProcessThread initiator;
/** The currently running threads (thread -> context or null, if leaf thread). */
protected Map threads;
//-------- constructors --------
/**
* Create a new top-level thread context.
* @param model The process model element.
*/
public ThreadContext(MBpmnModel model)
{
this.model = model;
this.initiator = null;
}
/**
* Create a new sub-level thread context.
* Should not be invoked from the outside
* @param model The sub process model element.
* @param initiator The initiating thread.
*/
public ThreadContext(MSubProcess model, ProcessThread initiator)
{
this.model = model;
this.initiator = initiator;
}
//-------- methods --------
/**
* Get the model element.
* @return The process or sub process element.
*/
public MIdElement getModelElement()
{
return model;
}
/**
* Get the parent context.
* @return The parent context, if any.
*/
public ThreadContext getParent()
{
return initiator!=null ? initiator.getThreadContext() : null;
}
/**
* Get the initiating thread.
* @return The initiator, if any.
*/
public ProcessThread getInitiator()
{
return initiator;
}
/**
* Add a thread to this context.
* @param thread The thread to be added.
*/
public void addThread(ProcessThread thread)
{
if(threads==null)
threads = new LinkedHashMap();
threads.put(thread, null);
// System.out.println("add: "+thread);
}
/**
* Remove a thread from this context.
* @param thread The thread to be removed.
*/
public void removeThread(ProcessThread thread)
{
if(threads!=null)
{
threads.remove(thread);
if(threads.isEmpty())
threads = null;
}
// System.out.println("remove: "+thread);
}
/**
* Get all threads of this context.
* @return All threads of this context, but not from sub context.
*/
public Set getThreads()
{
return threads!=null ? threads.keySet() : null;
}
/**
* Get all threads of the context and all subcontexts.
*/
public Set getAllThreads()
{
Set ret = new HashSet();
if(threads!=null)
{
for(Iterator it=threads.keySet().iterator(); it.hasNext(); )
{
ProcessThread pc = (ProcessThread)it.next();
ret.add(pc);
ThreadContext tc = (ThreadContext)threads.get(pc);
if(tc!=null)
ret.addAll(tc.getAllThreads());
}
}
return ret;
}
/**
* Get the sub context corresponding to a given thread.
* @param thread The thread.
* @return The corresponding thread context.
* /
public ThreadContext getThreadContext(ProcessThread thread)
{
List contexts = new ArrayList();
contexts.add(this);
ThreadContext ret = null;
while(ret==null && !contexts.isEmpty())
{
ThreadContext context = (ThreadContext)contexts.remove(0);
if(context.threads!=null && context.threads.containsKey(thread))
{
ret = context;
}
else if(context.threads!=null)
{
for(Iterator it=context.threads.values().iterator(); it.hasNext(); )
{
Object subcontext = it.next();
if(subcontext!=null)
contexts.add(subcontext);
}
}
}
return ret;
}*/
/**
* Add a sub context.
* @param context The sub context to be added.
*/
public void addSubcontext(ThreadContext context)
{
assert threads!=null && threads.containsKey(context.getInitiator());
threads.put(context.getInitiator(), context);
}
/**
* Remove a sub context but keep the corresponding thread.
* E.g. when a sub process terminates, the sub context is removed
* and the initiating thread continues in the outer context.
* @param context The sub context to be removed.
*/
public void removeSubcontext(ThreadContext context)
{
assert threads!=null && threads.containsKey(context.getInitiator());
threads.put(context.getInitiator(), null);
}
/**
* Get the subcontext of a thread.
* @param thread The thread which owns the subcontext.
* @return The subcontext (if any).
*/
public ThreadContext getSubcontext(ProcessThread thread)
{
return (ThreadContext)threads.get(thread);
}
/**
* The context is finished, when there are no (more) threads to execute.
* @param pool The pool to be executed or null for any.
* @param lane The lane to be executed or null for any. Nested lanes may be addressed by dot-notation, e.g. 'OuterLane.InnerLane'.
* @return True, when the process instance is finished with regards to the specified pool/lane. When both pool and lane are null, true is returned only when all pools/lanes are finished.
*/
public boolean isFinished(String pool, String lane)
{
boolean finished = true;
if(threads!=null && !threads.isEmpty())
{
for(Iterator it=threads.keySet().iterator(); finished && it.hasNext(); )
{
finished = !((ProcessThread)it.next()).belongsTo(pool, lane);
}
}
return finished;
}
/**
* Get an executable thread in the context or its sub contexts.
* @param pool The pool to be executed or null for any.
* @param lane The lane to be executed or null for any. Nested lanes may be addressed by dot-notation, e.g. 'OuterLane.InnerLane'.
* @return An executable thread (if any).
*/
public ProcessThread getExecutableThread(String pool, String lane)
{
ProcessThread ret = null;
if(threads!=null)
{
for(Iterator it=threads.keySet().iterator(); ret==null && it.hasNext(); )
{
ProcessThread thread = (ProcessThread)it.next();
if(threads.get(thread)!=null)
{
ThreadContext context = (ThreadContext)threads.get(thread);
ret = context.getExecutableThread(pool, lane);
}
else if(!thread.isWaiting() && thread.belongsTo(pool, lane))
{
ret = thread;
}
}
}
return ret;
}
/**
* Get the string representation.
* @return The string representation.
*/
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append(SReflect.getInnerClassName(this.getClass()));
buf.append("(model=");
buf.append(model);
buf.append(", threads=");
buf.append(threads);
buf.append(")");
return buf.toString();
}
}