org.openxdata.designer.client.view.SkipRulesView.java Source code

Java tutorial

Introduction

Here is the source code for org.openxdata.designer.client.view.SkipRulesView.java

Source

package org.openxdata.designer.client.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.openxdata.designer.client.controller.IConditionController;
import org.openxdata.designer.client.controller.QuestionSelectionListener;
import org.openxdata.designer.client.widget.skiprule.ConditionWidget;
import org.openxdata.designer.client.widget.skiprule.GroupOperationWidget;
import org.openxdata.sharedlib.client.model.Condition;
import org.openxdata.sharedlib.client.model.FormDef;
import org.openxdata.sharedlib.client.model.ModelConstants;
import org.openxdata.sharedlib.client.model.QuestionDef;
import org.openxdata.sharedlib.client.model.SkipRule;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import org.openxdata.sharedlib.client.locale.FormsConstants;

/**
 * This widget enables creation of skip rules.
 * 
 *  www.openxdata.org - Licensed as written in license.txt and original sources of this file and its authors are found in sources.txt.
 *
 */
public class SkipRulesView extends Composite implements IConditionController, QuestionSelectionListener {
    final FormsConstants formsConstants = GWT.create(FormsConstants.class);

    private static SkipRulesViewUiBinder uiBinder = GWT.create(SkipRulesViewUiBinder.class);

    interface SkipRulesViewUiBinder extends UiBinder<VerticalPanel, SkipRulesView> {
    }

    /** The main or root widget. */
    private VerticalPanel verticalPanel;

    /** Widget for adding new conditions. */
    @UiField
    Button addConditionButton;

    /** Widget for grouping conditions. Has all,any, none, and not all. */
    @UiField
    GroupOperationWidget groupOperatorWidget;

    /** The form definition object that this skip rule belongs to. */
    private FormDef formDef;

    /** The question definition object which is the target of the skip rule. 
     *  As for now, the form designer supports only one skip rule target. But the
     *  skip rule object supports an un limited number.
     */
    private QuestionDef questionDef;

    /** The skip rule definition object. */
    private SkipRule skipRule;

    /** Flag determining whether to enable this widget or not. */
    private boolean enabled;

    /** Widget for the skip rule action to enable a question. */
    @UiField
    RadioButton rdEnable;

    /** Widget for the skip rule action to disable a question. */
    @UiField
    RadioButton rdDisable;

    /** Widget for the skip rule action to show a question. */
    @UiField
    RadioButton rdShow;

    /** Widget for the skip rule action to hide a question. */
    @UiField
    RadioButton rdHide;

    /** Widget for the skip rule action to make a question required. */
    @UiField
    CheckBox chkMakeRequired;

    /** Widget for Label "for question". */
    @UiField
    SpanElement lblAction;

    /** Widget for Label "and". */
    @UiField
    SpanElement lblAnd;

    /** Button to show other questions */
    @UiField
    Button otherQts;

    /** Panel containing text and link for when to apply condition */
    @UiField
    HTMLPanel conditionPanel;

    /** A list of condition widgets applied to the current selected question */
    private final List<ConditionWidget> conditions = new ArrayList<ConditionWidget>();

    /**
     * Creates a new instance of the skip logic widget.
     */
    public SkipRulesView() {
        verticalPanel = uiBinder.createAndBindUi(this);

        initWidget(verticalPanel);

        // set localized text on some of the widgets
        // TODO(droberge): Determine if there is a way to set localized text in XML
        lblAction.setInnerText(formsConstants.forQuestion());
        lblAnd.setInnerText(formsConstants.and());
        otherQts.setText(formsConstants.clickForOtherQuestions());
        addConditionButton.setText(formsConstants.addCondition());

        SpanElement conditionSpan = SpanElement.as(conditionPanel.getElementById("whenSpan"));
        conditionSpan.setInnerText(formsConstants.when());

        conditionSpan = SpanElement.as(conditionPanel.getElementById("followingSpan"));
        conditionSpan.setInnerText(formsConstants.ofTheFollowingApply());
    }

    @UiFactory
    GroupOperationWidget makeGroupHyperlink() {
        return new GroupOperationWidget();
    }

    @UiHandler("addConditionButton")
    protected void handleConditionLinkClick(ClickEvent event) {
        addCondition();
    }

    @UiHandler("otherQts")
    protected void handleOtherQtsClick(ClickEvent event) {
        showOtherQuestions();
    }

    @UiHandler({ "rdEnable", "rdDisable", "rdShow", "rdHide" })
    protected void handleRadioClick(ClickEvent event) {
        updateMakeRequired();
    }

    /**
     * Enables the make required widget if the enable or show widget is ticked, 
     * else disables and unticks it.
     */
    private void updateMakeRequired() {
        chkMakeRequired.setEnabled(rdEnable.getValue() == true || rdShow.getValue() == true);
        if (!chkMakeRequired.isEnabled())
            chkMakeRequired.setValue(false);
    }

    /**
     * Adds a new condition.
     */
    public void addCondition() {
        if (formDef != null && enabled) {
            ConditionWidget conditionWidget = new ConditionWidget(formDef, this, true, questionDef);
            conditions.add(conditionWidget);
            verticalPanel.add(conditionWidget);

            if (!(rdEnable.getValue() == true || rdDisable.getValue() == true || rdShow.getValue() == true
                    || rdHide.getValue() == true)) {
                rdEnable.setValue(true);
                updateMakeRequired();
            }
        }
    }

    /**
     * Supposed to add a bracket or nested set of related conditions which are 
     * currently not supported.
     */
    public void addBracket() {

    }

    /**
     * Deletes a condition.
     * 
     * @param conditionWidget the widget having the condition to delete.
     */
    public void deleteCondition(ConditionWidget conditionWidget) {
        if (skipRule != null) {
            Condition condition = conditionWidget.getCondition();
            if (condition != null) {
                if (skipRule.getConditionCount() == 1 && skipRule.getActionTargetCount() > 1)
                    skipRule.removeActionTarget(questionDef);
                else
                    skipRule.removeCondition(condition);
            }
        }
        conditions.remove(conditionWidget);
        verticalPanel.remove(conditionWidget);
    }

    /**
     * Sets or updates the values of the skip rule object from the user's widget selections.
     */
    public void updateSkipRule() {
        if (questionDef == null) {
            skipRule = null;
            return;
        }

        if (skipRule == null)
            skipRule = new SkipRule();

        for (int i = 0; i < conditions.size(); i++) {
            Condition condition = conditions.get(i).getCondition();

            if (condition != null && !skipRule.containsCondition(condition)) {
                skipRule.addCondition(condition);
            } else if (condition != null && skipRule.containsCondition(condition)) {
                skipRule.updateCondition(condition);
            }
        }

        if (skipRule.getConditions() == null || conditions.size() == 0)
            skipRule = null;
        else {
            skipRule.setConditionsOperator(groupOperatorWidget.getConditionsOperator());
            skipRule.setAction(getAction());

            if (!skipRule.containsActionTarget(questionDef.getId()))
                skipRule.addActionTarget(questionDef.getId());
        }

        if (skipRule != null && !formDef.containsSkipRule(skipRule))
            formDef.addSkipRule(skipRule);
    }

    /**
     * Gets the skip rule action based on the user's widget selections.
     * 
     * @return the skip rule action.
     */
    private int getAction() {
        int action = 0;
        if (rdEnable.getValue() == true)
            action |= ModelConstants.ACTION_ENABLE;
        else if (rdShow.getValue() == true)
            action |= ModelConstants.ACTION_SHOW;
        else if (rdHide.getValue() == true)
            action |= ModelConstants.ACTION_HIDE;
        else
            action |= ModelConstants.ACTION_DISABLE;

        if (chkMakeRequired.getValue() == true)
            action |= ModelConstants.ACTION_MAKE_MANDATORY;
        else
            action |= ModelConstants.ACTION_MAKE_OPTIONAL;

        return action;
    }

    /**
     * Updates the widgets basing on a given skip rule action.
     * 
     * @param action the skip rule action.
     */
    private void setAction(int action) {
        rdEnable.setValue((action & ModelConstants.ACTION_ENABLE) != 0);
        rdDisable.setValue((action & ModelConstants.ACTION_DISABLE) != 0);
        rdShow.setValue((action & ModelConstants.ACTION_SHOW) != 0);
        rdHide.setValue((action & ModelConstants.ACTION_HIDE) != 0);
        chkMakeRequired.setValue((action & ModelConstants.ACTION_MAKE_MANDATORY) != 0);
        updateMakeRequired();
    }

    /**
     * Sets the question definition object which is the target of the skip rule.
     * For now we support only one target for the skip rule.
     * 
     * @param questionDef the question definition object.
     */
    public void setQuestionDef(QuestionDef questionDef) {
        clearConditions();

        if (questionDef != null) {
            formDef = questionDef.getParentFormDef();
            lblAction.setInnerText(formsConstants.forQuestion() + questionDef.getDisplayText());

            // only enable grouping operator and add condition widgets
            // if the form definition contains more than 1 question
            // it doesn't really make sense to allow these to be enabled
            // when the form definition has only 1 question
            if (questionDef.getParentFormDef().getQuestionCount() > 1) {
                addConditionButton.setEnabled(true);
                groupOperatorWidget.setEnabled(true);
            } else {
                addConditionButton.setEnabled(false);
                groupOperatorWidget.setEnabled(false);
            }
        } else {
            lblAction.setInnerText(formsConstants.forQuestion());
            addConditionButton.setEnabled(false);
            groupOperatorWidget.setEnabled(false);
        }

        this.questionDef = questionDef;

        skipRule = formDef.getSkipRule(questionDef);
        if (skipRule != null) {
            groupOperatorWidget.setCondionsOperator(skipRule.getConditionsOperator());
            setAction(skipRule.getAction());
            Vector<Condition> conditions = skipRule.getConditions();
            Vector<Condition> lostConditions = new Vector<Condition>();
            for (int i = 0; i < conditions.size(); i++) {
                ConditionWidget conditionWidget = new ConditionWidget(formDef, this, true, questionDef);

                if (conditionWidget.setCondition((Condition) conditions.elementAt(i))) {
                    verticalPanel.add(conditionWidget);
                    this.conditions.add(conditionWidget);
                } else
                    lostConditions.add((Condition) conditions.elementAt(i));
            }
            for (int i = 0; i < lostConditions.size(); i++)
                skipRule.removeCondition((Condition) lostConditions.elementAt(i));
            if (skipRule.getConditionCount() == 0) {
                formDef.removeSkipRule(skipRule);
                skipRule = null;
            }

        }
    }

    /**
     * Sets the form definition object to which this skip rule belongs.
     * 
     * @param formDef the form definition object.
     */
    public void setFormDef(FormDef formDef) {
        updateSkipRule();
        this.formDef = formDef;
        this.questionDef = null;
        clearConditions();
    }

    /**
     * Removes all skip rule conditions.
     */
    private void clearConditions() {
        if (questionDef != null)
            updateSkipRule();

        questionDef = null;
        lblAction.setInnerText(formsConstants.forQuestion());

        // clear condition widgets from ui
        for (int i = 0; i < conditions.size(); i++) {
            verticalPanel.remove(conditions.get(i));
        }

        conditions.clear();

        groupOperatorWidget.reset();

        rdEnable.setValue(false);
        rdDisable.setValue(false);
        rdShow.setValue(false);
        rdHide.setValue(false);
        chkMakeRequired.setValue(false);
        updateMakeRequired();
    }

    /**
     * Sets whether to enable this widget or not.
     * 
     * @param enabled set to true to enable, else false.
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;

        rdEnable.setEnabled(enabled);
        rdDisable.setEnabled(enabled);
        rdShow.setEnabled(enabled);
        rdHide.setEnabled(enabled);
        chkMakeRequired.setEnabled(enabled);

        if (!enabled)
            clearConditions();
    }

    /**
     * Shows a list of other questions that are targets of the current skip rule.
     */
    private void showOtherQuestions() {
        if (enabled) {
            SkipQtnsDialog dialog = new SkipQtnsDialog(this);
            dialog.setData(formDef, questionDef, skipRule);
            dialog.center();
        }
    }

    /**
     * @see org.openxdata.designer.client.controller.QuestionSelectionListener#onQuestionsSelected(List)
     */
    public void onQuestionsSelected(List<String> questions) {
        if (skipRule == null)
            skipRule = new SkipRule();

        //Check if we have any action targets. If we do not, just add all as new.
        List<Integer> actnTargets = skipRule.getActionTargets();
        if (actnTargets == null) {
            for (String varName : questions)
                skipRule.addActionTarget(formDef.getQuestion(varName).getId());

            return;
        }

        //Remove any de selected action targets from the skip rule.
        for (int index = 0; index < actnTargets.size(); index++) {
            Integer qtnId = actnTargets.get(index);

            QuestionDef qtnDef = formDef.getQuestion(qtnId);
            if (qtnDef == questionDef)
                continue; //Ignore the question for which we are editing the skip rule.

            if (qtnDef == null || !questions.contains(qtnDef.getBinding())) {
                actnTargets.remove(index);
                index = index - 1;
            }
        }

        //Add any newly added questions as action targets.
        for (String varName : questions) {
            QuestionDef qtnDef = formDef.getQuestion(varName);
            if (!skipRule.containsActionTarget(qtnDef.getId()))
                skipRule.addActionTarget(qtnDef.getId());
        }
    }
}