package org.openrosa.client.model;
import java.io.Serializable;
import java.util.Vector;
import org.openrosa.client.xforms.RelevantBuilder;
import org.openrosa.client.xforms.XformConstants;
import com.google.gwt.xml.client.Document;
/**
* A definition for skipping or branching rules.
* These could for example be enabling or disabling, hiding or showing, making mandatory or optional
* of questions basing on values of others. In xforms, this is a relevant atttribute.
*
* @author Daniel Kayiwa
*
*/
public class SkipRule implements Serializable{
/** The numeric identifier of a rule. This is assigned in code and hence
* is not known by the user.
*/
private int id = ModelConstants.NULL_ID;
/** A list of conditions (Condition object) to be tested for a rule.
* E.g. If sex is Male. If age is greater than 4. etc
*/
private Vector conditions;
/** The action taken when conditions are true.
* Example of actions are Disable, Hide, Show, etc
*/
private int action = ModelConstants.ACTION_NONE;
/** A list of question identifiers (int) acted upon when conditions for the rule are true. */
private Vector actionTargets;
/** Operator for combining more than one condition. (And, Or) only these two for now. */
private int conditionsOperator = ModelConstants.CONDITIONS_OPERATOR_NULL;
/** Constructs a rule object ready to be initialized. */
public SkipRule(){
}
/** Copy constructor. */
public SkipRule(SkipRule skipRule){
setId(skipRule.getId());
setAction(skipRule.getAction());
setConditionsOperator(skipRule.getConditionsOperator());
copyConditions(skipRule.getConditions());
copyActionTargets(skipRule.getActionTargets());
}
/** Construct a Rule object from parameters.
*
* @param ruleId
* @param conditions
* @param action
* @param actionTargets
*/
public SkipRule(int ruleId, Vector conditions, int action, Vector actionTargets /*, String name*/) {
setId(ruleId);
setConditions(conditions);
setAction(action);
setActionTargets(actionTargets);
}
public int getAction() {
return action;
}
public void setAction(int action) {
this.action = action;
}
public Vector getActionTargets() {
return actionTargets;
}
public void setActionTargets(Vector actionTargets) {
this.actionTargets = actionTargets;
}
public Vector getConditions() {
return conditions;
}
public void setConditions(Vector conditions) {
this.conditions = conditions;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getConditionsOperator() {
return conditionsOperator;
}
public void setConditionsOperator(int conditionsOperator) {
this.conditionsOperator = conditionsOperator;
}
public Condition getConditionAt(int index) {
if(conditions == null)
return null;
return (Condition)conditions.elementAt(index);
}
public int getConditionCount() {
if(conditions == null)
return 0;
return conditions.size();
}
public int getActionTargetCount() {
if(actionTargets == null)
return 0;
return actionTargets.size();
}
public Integer getActionTargetAt(int index) {
if(actionTargets == null)
return null;
return (Integer)actionTargets.elementAt(index);
}
public void addActionTarget(int id){
if(actionTargets == null)
actionTargets = new Vector();
actionTargets.add(new Integer(id));
}
public void clearActionTargets(){
if(actionTargets != null)
actionTargets.clear();
}
public boolean containsActionTarget(int id){
if(actionTargets == null)
return false;
for(int i=0; i<actionTargets.size(); i++){
if(((Integer)actionTargets.elementAt(i)).intValue() == id)
return true;
}
return false;
}
public void addCondition(Condition condition){
if(conditions == null)
conditions = new Vector();
conditions.add(condition);
}
public boolean containsCondition(Condition condition){
if(conditions == null)
return false;
return conditions.contains(condition);
}
public void updateCondition(Condition condition){
for(int i=0; i<conditions.size(); i++){
Condition cond = (Condition)conditions.elementAt(i);
if(cond.getId() == condition.getId()){
conditions.remove(i);
conditions.add(condition);
break;
}
}
}
public void removeCondition(Condition condition){
conditions.remove(condition);
}
public void removeActionTarget(IFormElement questionDef){
if(questionDef.getBindNode() != null){
questionDef.getBindNode().removeAttribute(XformConstants.ATTRIBUTE_NAME_RELEVANT);
questionDef.getBindNode().removeAttribute(XformConstants.ATTRIBUTE_NAME_ACTION);
}
for(int index = 0; index < getActionTargetCount(); index++){
Integer id = getActionTargetAt(index);
if(id.intValue() == questionDef.getId()){
this.actionTargets.remove(id);
index++;
}
}
}
public void removeQuestion(IFormElement questionDef){
for(int index = 0; index < getConditionCount(); index++){
Condition condition = getConditionAt(index);
if(condition.getQuestionId() == questionDef.getId()){
removeCondition(condition);
index++;
}
}
/*for(int index = 0; index < getActionTargetCount(); index++){
Integer id = getActionTargetAt(index);
if(id.intValue() == questionDef.getId()){
this.actionTargets.remove(id);
index++;
}
}*/
removeActionTarget(questionDef);
}
/**
* Checks conditions of a rule and executes the corresponding actions
*
* @param data
*/
public void fire(FormDef formDef){
boolean trueFound = false, falseFound = false;
for(int i=0; i<getConditions().size(); i++){
Condition condition = (Condition)this.getConditions().elementAt(i);
if(condition.isTrue(formDef,false))
trueFound = true;
else
falseFound = true;
}
if(getConditions().size() == 1 || getConditionsOperator() == ModelConstants.CONDITIONS_OPERATOR_AND)
ExecuteAction(formDef,!falseFound);
else if(getConditionsOperator() == ModelConstants.CONDITIONS_OPERATOR_OR)
ExecuteAction(formDef,trueFound);
//else do nothing
}
/** Executes the action of a rule for its conditition's true or false value. */
public void ExecuteAction(FormDef formDef,boolean conditionTrue){
Vector qtns = this.getActionTargets();
for(int i=0; i<qtns.size(); i++)
ExecuteAction((QuestionDef)formDef.getElement(Integer.parseInt(qtns.elementAt(i).toString())),conditionTrue);
}
/** Executes the rule action on the supplied question. */
public void ExecuteAction(QuestionDef qtn,boolean conditionTrue){
qtn.setEnabled(true);
qtn.setRequired(false);
if((action & ModelConstants.ACTION_ENABLE) != 0)
qtn.setEnabled(conditionTrue);
else if((action & ModelConstants.ACTION_DISABLE) != 0)
qtn.setEnabled(!conditionTrue);
if((action & ModelConstants.ACTION_MAKE_MANDATORY) != 0)
qtn.setRequired(conditionTrue);
}
private void copyConditions(Vector conditions){
this.conditions = new Vector();
for(int i=0; i<conditions.size(); i++)
this.conditions.addElement(new Condition((Condition)conditions.elementAt(i)));
}
private void copyActionTargets(Vector actionTargets){
this.actionTargets = new Vector();
for(int i=0; i<actionTargets.size(); i++)
this.actionTargets.addElement(new Integer(((Integer)actionTargets.elementAt(i)).intValue()));
}
public void updateDoc(FormDef formDef, Document doc){
RelevantBuilder.fromSkipRule2Xform(this,formDef, doc);
}
public void refresh(FormDef dstFormDef, FormDef srcFormDef){
SkipRule skipRule = new SkipRule();;
skipRule.setConditionsOperator(getConditionsOperator());
skipRule.setAction(getAction());
skipRule.setId(getId());
for(int index = 0; index < this.getConditionCount(); index++){
Condition condition = getConditionAt(index);
QuestionDef qtn = srcFormDef.getQuestion(condition.getQuestionId());
if(qtn == null)
continue;
QuestionDef questionDef = dstFormDef.getQuestion(qtn.getQuestionID());
if(questionDef == null)
continue;
condition.setQuestionId(questionDef.getId());
skipRule.addCondition(new Condition(condition));
}
if(skipRule.getConditionCount() == 0)
return; //No matching condition found.
for(int index = 0; index < this.getActionTargetCount(); index++){
Integer actionTarget = getActionTargetAt(index);
QuestionDef qtn = srcFormDef.getQuestion(actionTarget);
if(qtn == null)
continue;
QuestionDef questionDef = dstFormDef.getQuestion(qtn.getQuestionID());
if(questionDef == null)
continue;
skipRule.addActionTarget(questionDef.getId());
}
if(skipRule.getActionTargetCount() > 0)
dstFormDef.addSkipRule(skipRule);
}
public void updateConditionValue(String origValue, String newValue){
for(int index = 0; index < this.getConditionCount(); index++)
getConditionAt(index).updateValue(origValue, newValue);
}
}