package org.syrup.helpers;
import org.syrup.Link;
import org.syrup.Task;
import org.syrup.Port;
import org.syrup.OutPort;
import org.syrup.Workflow;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
import java.util.logging.XMLFormatter;
/**
* Utility functions to aid the testing of Syrup objects.
*
* @author Robbert van Dalen
*/
public class Utils
{
static final String COPYRIGHT = "Copyright 2005 Robbert van Dalen."
+ "At your option, you may copy, distribute, or make derivative works under "
+ "the terms of The Artistic License. This License may be found at "
+ "http://www.opensource.org/licenses/artistic-license.php. "
+ "THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.";
private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd-HHmmss");
/**
* Returns true if the Link has content.
*
* @param l
* The Link that is tested.
* @return true if the Link has content.
*/
public final static boolean isFull(Link l)
{
if (l != null)
{
if (l.content() != null)
{
return true;
}
}
return false;
}
/**
* Validates the Workflow.
*
* @param workflow
* the Workflow to be validated.
* @throws Execption
* the invalid part of the Workflow
*/
public final static void validate(Workflow workflow) throws Exception
{
/**
* [TODO: validate if there are not more then two Links from and to
* Tasks (the same for Bindings and Links). Also validate that the
* Workflow doesn't contain Tasks with the same name. And finally check
* if there are not two Links with the same Port (the same for Bindings
* and Links also)]
*/
Task[] tasks = workflow.tasks();
Link[] links = workflow.links();
HashMap taskContexts = new HashMap();
HashMap taskNames = new HashMap();
/* Check for double naming and prepare datastructures */
for (int t = 0; t < tasks.length; t++)
{
Task tt = tasks[t];
if (taskNames.get(tt.name()) != null)
{
throw new Exception("more then one Task with name: "
+ tt.name());
}
else
{
taskNames.put(tt.name(), tt);
taskContexts.put(tt, new HashMap());
}
}
// Check if there are no Ports that have multiple Links attached
for (int l = 0; l < links.length; l++)
{
Link ll = links[l];
setAndCheckContext(taskContexts, ll.to());
setAndCheckContext(taskContexts, ll.from());
}
// Check if the binding's Ports don't collide with the Link's Ports.
setAndCheckContext(taskContexts, workflow.in_1_binding());
setAndCheckContext(taskContexts, workflow.in_2_binding());
setAndCheckContext(taskContexts, workflow.out_1_binding());
setAndCheckContext(taskContexts, workflow.out_2_binding());
}
private static void setAndCheckContext(HashMap taskContexts, Port p)
throws Exception
{
if (p != null)
{
Task task = p.task();
if (task != null)
{
HashMap context = (HashMap) taskContexts.get(task);
if (context != null)
{
// [TODO: optimize key generation]
String key = ""
+ p.isSecond();
if (p instanceof OutPort)
{
key = "o"
+ key;
}
if (context.get(key) == null)
{
context.put(key, key);
}
else
{
throw new Exception("more then one Link to same Port detected: " + p);
}
}
else
{
throw new Exception("illegal reference to external task: "
+ task);
}
}
}
}
/**
* Returns true if a Task is done based on type and if its inputs are also
* done.
*
* @param in1_done
* true if the first input is done.
* @param in2_done
* true if the second input is done.
* @param out1_done
* true if the first output is done.
* @param out2_done
* true if the second output is done.
* @param orType
* true if the Task is of type OR.
* @param outer_world
* true if the Task represents the outer world.
* @return true if a Task is done based on type and if its inputs and if
* not, the if the outputs are also done.
*/
public final static boolean isDone(boolean in1_done, boolean in2_done,
boolean out1_done, boolean out2_done, boolean in1_full,
boolean in2_full, boolean out1_full, boolean out2_full, boolean orType,
boolean outer_world)
{
boolean r = (orType
&& in1_done && !in1_full && in2_done && !in2_full)
|| (!orType && ((in1_done && !in1_full) || (in2_done && !in2_full)));
if (!r
&& !outer_world)
{
return ((out1_done && out2_done)
|| (out1_done && out1_full) || (out2_done && out2_full));
}
return r;
}
/**
* Returns true if a Task is executable based on type and whether its inputs
* are filled and all its outputs are empty.
*
* @param firstInFull
* true if the first input is full.
* @param secondInFull
* true if the second input is full.
* @param firstOutFull
* true if the first output is full.
* @param secondOutFull
* true if the second output is full.
* @param orType
* true if the Task is of type OR.
* @param isDone
* true if the Task is done.
* @return true if a Task is executable based on type and whether its inputs
* are filled and all its outputs are empty.
*/
public final static boolean isExecutable(boolean firstInFull,
boolean secondInFull, boolean firstOutFull, boolean secondOutFull,
boolean orType, boolean isDone)
{
// If the Task is done then it cannot be executable.
if (!isDone)
{
// Both outputs need to be empty, for execution to start.
if (!(firstOutFull || secondOutFull))
{
if (orType)
{
if (firstInFull
|| secondInFull)
{
// or_type = true and (firstInFull = true or
// secondInFull =
// true).
return true;
}
}
else
{
if (firstInFull
&& secondInFull)
{
// or_type = false and (firstInFull = true and
// secondInFull
// = true)
return true;
}
}
}
}
return false;
}
public static org.syrup.Data manageError(Logger logger, Throwable error,
String msg)
{
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Handler logHandler = new StreamHandler(buffer, new XMLFormatter());
logHandler.setLevel(Level.ALL);
logger.addHandler(logHandler);
try
{
logger.log(Level.SEVERE, msg, error);
logHandler.flush();
logHandler.close();
return new DataImpl(buffer.toByteArray());
}
finally
{
// Make sure it is always removed so that it won't leak.
logger.removeHandler(logHandler);
}
}
/**
* Utility function that maps a timestamp to a formatted date.
*
* @param t
* The timestamp to be formatted.
* @return The formatted date.
*/
public final static String asDate(long t)
{
Date d = new Date(t);
return dateFormatter.format(d);
}
/**
* Utility function that parses a formatted date to a timestamp.
*
* @param str
* The date to be parsed.
* @return The resulting timestamp.
*/
public final static long parseDate(String str) throws Exception
{
// [TODO: set the milliseconds to zero]
// [TODO: allow lenient parsing]
Date d = dateFormatter.parse(str);
return d.getTime();
}
}