package edu.neu.ccs.task.dialogue;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import edu.neu.ccs.task.Prioritized;
import edu.neu.ccs.task.Priority;
import edu.neu.ccs.task.Script;
import edu.neu.ccs.task.Slot;
import edu.neu.ccs.task.Task;
import edu.neu.ccs.task.TaskModelSet;
import edu.neu.ccs.task.TaskType;
import edu.neu.ccs.task.Type;
import edu.neu.ccs.task.util.Bool;
/**
* A representation of a dialogue turn specified by a task/dialogue model.
* Essentially a runtime representation of a d:turn element.
*
* @author Dan Schulman
* @version $Id: Turn.java 797 2009-12-09 20:20:30Z schulman $
*/
public class Turn extends Type implements Prioritized {
private final QName task;
private final String applicable;
private final int priority;
private final Display display;
private final List<Output> outputs;
private final List<Output> altOutputs;
private final TurnUserAction userAction;
private final List<Result> bindings;
private final List<Script> scripts;
public static class Builder extends Type.Builder {
public QName task;
public String applicable;
public int priority;
public Display display;
public List<Output> outputs = new ArrayList<Output>();
public List<Output> altOutputs = new ArrayList<Output>();
public TurnUserAction userAction;
public List<Result> bindings = new ArrayList<Result>();
public List<Script> scripts = new ArrayList<Script>();
}
public Turn(Builder b) {
super(b);
this.task = b.task;
this.applicable = b.applicable;
this.priority = b.priority;
this.display = b.display;
this.outputs = b.outputs;
this.altOutputs = b.altOutputs;
this.userAction = b.userAction;
this.bindings = b.bindings;
this.scripts = b.scripts;
}
/**
* @return the identifier for the task this dialogue realizes.
*/
public QName getTask() {
return task;
}
/**
* @param task a task instance
* @return whether this dialogue is applicable to the task (true, false,
* or null for unknown).
*/
public Boolean isApplicable(Task task) {
if ( ! task.getType().getQName().equals(this.task))
return false;
return task.maybeEvalBool(applicable,
getPrefixedName() + " applicable");
}
public int getPriority() {
return priority;
}
public boolean hasDisplay() {
return display != null;
}
public Display getDisplay() {
return display;
}
/**
* @return true if this contains any agent output.
*/
public boolean hasOutputText() {
return (outputs != null) && !outputs.isEmpty();
}
/**
* @return a randomly selected agent output.
*/
public Output getOutputText() {
return outputs.get((int) (Math.random() * outputs.size()));
}
/**
* @param task a task instance
* @return a randomly selected agent output, with substitution
* performed, and no annotations.
*/
public String getPlainOutput(Task task) {
return Template.subst(getOutputText().getPlainText(), task);
}
/**
* @param task a task instance
* @return a randomly selected agent output, with substitutions
* performed, and all annotations.
*/
public String getAnnotatedOutput(Task task) {
return Template.subst(getOutputText().getAnnotatedText(), task);
}
/**
* @return a randomly selected alternate agent output.
*/
public Output getAltOutputText() {
if (altOutputs.isEmpty())
return getOutputText();
return altOutputs.get((int) (Priority.random() * altOutputs.size()));
}
/**
* @param task a task instance
* @return a randomly selected alt. agent output, with substitution
* performed, and no annotations.
*/
public String getPlainAltOutput(Task task) {
return Template.subst(getAltOutputText().getPlainText(), task);
}
/**
* @param task a task instance
* @return a randomly selected alt. agent output, with substitutions
* performed, and all annotations.
*/
public String getAnnotatedAltOutput(Task task) {
return Template.subst(getAltOutputText().getAnnotatedText(), task);
}
public boolean hasUserAction() {
return userAction != null;
}
public boolean hasUserMenu() {
return userAction instanceof MenuUserAction;
}
public boolean hasUserText() {
return userAction instanceof TextUserAction;
}
public boolean hasUserWidget() {
return userAction instanceof WidgetUserAction;
}
public TurnUserAction getUserAction() {
return userAction;
}
public MenuUserAction getUserMenu() {
if (userAction instanceof MenuUserAction)
return (MenuUserAction) userAction;
return null;
}
public TextUserAction getUserText() {
if (userAction instanceof TextUserAction)
return (TextUserAction) userAction;
return null;
}
public WidgetUserAction getUserWidget() {
if (userAction instanceof WidgetUserAction)
return (WidgetUserAction) userAction;
return null;
}
/**
* Apply a user dialogue choice to a task.
*
* @param task a task instance
* @param choice a zero-indexed user dialogue choice
*/
public void applyChoice(Task task, int choice) {
if (userAction != null)
userAction.applyChoice(task, choice);
apply(task);
}
public void applyInput(Task task, String input) {
if (userAction != null)
userAction.applyInput(task, input);
apply(task);
}
/**
* Apply any results not specific to any user dialogue choice.
*
* @param task a task instance
*/
public void apply(Task task) {
for (Result b : bindings)
b.apply(task);
for (Script s : scripts)
s.apply(task, false);
if (!Bool.isFalse(task.getSuccess()) && !task.isDefinedOutputs())
throw new RuntimeException(
getPrefixedName() + " did not set all outputs");
}
@Override
public void check(List<String> errors) {
String pname = getPrefixedName();
TaskModelSet e = getModelSet();
if ( ! checkTaskType(e, errors))
return;
TaskType tt = e.getTaskType(task);
Set<String> outs = new HashSet<String>();
for (Slot out : tt.getDeclaredOutputs())
outs.add(out.getName());
for (Result r : bindings) {
if (r.check(e, pname, tt, errors))
if (! outs.remove(r.getDestination()))
errors.add(getPrefixedName() + ": duplicate binding " +
r.getDestination());
}
if ((userAction==null) && scripts.isEmpty())
for (String out : outs)
errors.add(getPrefixedName() + ": unbound output " + out);
if (userAction != null)
userAction.check(e, tt, outs, pname, !scripts.isEmpty(), errors);
}
private boolean checkTaskType(TaskModelSet e, List<String> errors) {
String taskModel = task.getNamespaceURI();
if (e.getModelForURI(taskModel) == null) {
errors.add(getPrefixedName() + ": unknown model " + taskModel);
return false;
} else if (e.getTaskType(task) == null) {
errors.add(getPrefixedName() + ": unknown task " +
e.asPrefixedName(task));
return false;
} else
return true;
}
}