package tudelft.healthpsychology.traumaontologies.answerstate;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import tudelft.healthpsychology.traumaontologies.questiontypes.Bool;
import tudelft.healthpsychology.traumaontologies.questiontypes.TypedQuestion;
import tudelft.utilities.tree.Tree;
/**
* {@link #node} points to a type of object of which there may be multiple
* relevant instances , eg "Persoon". This keeps track of asking the user all
* details about all instances.
*
* So this contains the state of a set of answers for a given OntologyNode,
* handling breath first:
*
* - The top level node eg #Persoon contains a main question (with the name as
* indicated in the constructor).
*
*
- We repeat asking for this main question until user says he has no more
* relevant items of this type (so no more relevant person)
*
*
- for each item, we determine the best fitting ontology class and ask the
* questions for this class.
*
*
*/
public class AnswersBreathFirstState implements AnswerState {
private final String nodelabel; // root node that this answer is about
private final List answerstates;
// the main properties of the nodes.
// We keep asking for more of these as long as this is not null.
private final Property mainProperty;
private final boolean askForMore;
private final Bool morequestion;
/**
* Construct new state with a list of OntologyAnswerState, the last being
* possibly nonfinal If the last is final and askedForMore=true
*
* @param node the node label that this answer is about.
* @param answerstates the list of answer states about node/mainprop.
* @param mainprop the main question, eg the property related to "Naam".
* This is the property to be asked first. node must
* have a property with this question. If not null, the
* user wants to add a mainprop. If mainprop=null, it
* means the user does not want to add more items. This
* can be null only if answerstates contains at least 1
* item.
* @param askMore true if we need to ask {@link #MOREANSWER}. Can only
* be true if mainprop is not null. If false, we look at
* mainprop to see if we need to add more.
* @param morequestion the question to ask for more, eg "zijn er nog meer
* relevante personen?".
*/
@JsonCreator
public AnswersBreathFirstState(@JsonProperty("nodelabel") String node,
@JsonProperty("answerstates") List answerstates,
@JsonProperty("mainProperty") Property mainprop,
@JsonProperty("askForMore") boolean askMore,
@JsonProperty("morequestion") Bool morequestion) {
if (answerstates.isEmpty() && mainprop == null) {
throw new IllegalArgumentException(
"mainprop must be not null if answerstates is empty");
}
if (askMore && mainprop == null) {
throw new IllegalArgumentException(
"If askForMore then mainprop must be not null");
}
this.nodelabel = node;
this.answerstates = answerstates;
this.mainProperty = mainprop;
this.askForMore = askMore;
this.morequestion = morequestion;
}
/**
* Convenience constructor : new state with one open OntologyAnswerState
*
* @param node the node that this answer is about.
* @param mainproperty the main question property, eg "Naam". This is the
* question of the property to be asked first. The node
* must have a property with this question.
* @param havemore the question to as if there are more items like this.
*/
public AnswersBreathFirstState(OntologyNode node, String mainproperty,
String havemore) {
this(node.getLabel(), new LinkedList<>(),
getMainProp(node, mainproperty), false,
new Bool(havemore, "internal"));
}
@Override
public TypedQuestion getOptions(
Tree, OntologyNode> tree) {
if (askForMore) {
return morequestion;
}
if (mainProperty != null) {
return mainProperty.getQuestionType();
}
// now handle each one depth-first.
for (OntoPropAnswerState state : answerstates) {
TypedQuestion opts = state.getOptions(tree);
if (opts != null)
return opts;
}
return null;
}
@Override
public AnswersBreathFirstState with(String answer,
Tree, OntologyNode> tree) {
TypedQuestion opts = getOptions(tree);
if (opts == null)
return this;
if (!opts.fits(answer)) {
throw new IllegalArgumentException(
"Answer " + answer + " does not fit " + opts);
}
OntologyNode node = tree.get(nodelabel);
if (askForMore) {
/**
* question was MOREANSWER. Just handle the yes/no answer. if answer
* is no, set mainProperty to null
*/
return new AnswersBreathFirstState(nodelabel, answerstates,
Bool.matchesYes(answer) ? mainProperty : null, false,
morequestion);
}
// askForMore may be true or false if we get here. Check mainproperty
if (mainProperty != null) {
/*
* User answered main question. Add new OntoPropAnswerState with the
* first answer already there . NOTICE we create node here with
* WRONG NODE (Typically "Persoon" ipv eg "Collega" // as we do not
* know the correct node type at this point
*/
LinkedList newAnswerStates = new LinkedList<>(
answerstates);
Map answer1 = new HashMap<>();
answer1.put(mainProperty, answer);
PropertiesAnswerState firstAnswer = new PropertiesAnswerState(
nodelabel, answer1);
newAnswerStates.add(new OntoPropAnswerState(
new OntologyAnswerState(node, answer), firstAnswer));
return new AnswersBreathFirstState(nodelabel, newAnswerStates,
mainProperty, true, morequestion);
}
/*
* If we get here, mainProperty=null. We're done collecting the main
* properties. Continue depth first now on collected answerstates
*/
int activeanswer = 0;
while (answerstates.get(activeanswer).getOptions(tree) == null)
activeanswer++;
LinkedList newAnswerStates = new LinkedList<>(
answerstates);
newAnswerStates.set(activeanswer,
answerstates.get(activeanswer).with(answer, tree));
return new AnswersBreathFirstState(nodelabel, newAnswerStates, null,
false, morequestion);
}
/**
*
* @param node
* @param mainquestion
* @return the {@link Property} of the node of which the ID equals to the
* given mainquestion.
*/
private static Property getMainProp(OntologyNode node,
String mainquestion) {
Optional mainprop = node.getAttribute().stream().filter(
prop -> mainquestion.equals(prop.getQuestionType().getId()))
.findFirst();
if (!mainprop.isPresent()) {
throw new IllegalArgumentException("Node " + node
+ " must have a property/attribute with the main question '"
+ mainquestion + "'");
}
return mainprop.get();
}
}