package org.openrosa.client.view; import java.util.Iterator; import java.util.Vector; import org.openrosa.client.controller.IConditionController; import org.openrosa.client.model.Condition; import org.openrosa.client.model.FormDef; import org.openrosa.client.model.IFormElement; import org.openrosa.client.model.ModelConstants; import org.openrosa.client.model.OptionDef; import org.openrosa.client.model.QuestionDef; import org.openrosa.client.model.SkipRule; import org.openrosa.client.model.ValidationRule; import org.openrosa.client.widget.skiprule.ConditionWidget; import org.openrosa.client.widget.skiprule.GroupHyperlink; import org.openrosa.client.xforms.ConstraintBuilder; import org.openrosa.client.xforms.ConstraintParser; import org.openrosa.client.xforms.RelevantBuilder; import org.openrosa.client.xforms.RelevantParser; import org.openrosa.client.PurcConstants; import org.openrosa.client.locale.LocaleText; import org.openrosa.client.util.FormUtil; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Hyperlink; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** * This widget enables creation of validation rules. * * @author daniel * */ public class ValidationRulesView extends Composite implements IConditionController{ /** The widget horizontal spacing in horizontal panels. */ private static final int HORIZONTAL_SPACING = 5; /** The widget vertical spacing in vertical panels. */ private static final int VERTICAL_SPACING = 5; /** The main or root widget. */ private VerticalPanel verticalPanel = new VerticalPanel(); /** Widget for adding new conditions. */ private Hyperlink addConditionLink = new Hyperlink(LocaleText.get("clickToAddNewCondition"),""); /** Widget for grouping conditions. Has all,any, none, and not all. */ private GroupHyperlink groupHyperlink = new GroupHyperlink(GroupHyperlink.CONDITIONS_OPERATOR_TEXT_ALL,""); /** The form definition object that this validation rule belongs to. */ private FormDef formDef; /** The question definition object which is the target of the validation rule. */ private IFormElement questionDef; /** The validation rule definition object. */ private ValidationRule validationRule; /** Flag determining whether to enable this widget or not. */ private boolean enabled; /** Widget for the validation rule error message. */ private TextArea txtErrorMessage; /** Widget for Label "Question: ". */ private Label lblAction = new Label(LocaleText.get("question")+": " /*"Question: "*/); private HorizontalPanel regularPanel1,regularPanel2; private FlexTable advancedPanel; private TextArea advtxtconstraint; private Label advlblconstraint; private CheckBox chkUseAdvanced; /** * Creates a new instance of the validation rule widget. */ public ValidationRulesView(){ setupWidgets(); } /** * Sets up the widgets. */ private void setupWidgets(){ advancedPanel = new FlexTable(); advtxtconstraint = new TextArea(); advtxtconstraint.setText(""); advlblconstraint = new Label("Advanced Validation Text"); chkUseAdvanced = new CheckBox("Use Advanced Validation Logic"); verticalPanel.setSpacing(5); verticalPanel.setVisible(true); advancedPanel.setWidget(0, 0, advlblconstraint); advancedPanel.setWidget(0, 1, advtxtconstraint); verticalPanel.add(chkUseAdvanced); verticalPanel.add(advancedPanel); advancedPanel.setVisible(false); txtErrorMessage = new TextArea(); regularPanel2 = new HorizontalPanel(); regularPanel2.setVisible(true); regularPanel2.setWidth("100%"); // FormUtil.maximizeWidget(txtErrorMessage); regularPanel2.add(new Label("Validation Error Message:")); regularPanel2.add(txtErrorMessage); regularPanel2.setSpacing(5); verticalPanel.add(regularPanel2); verticalPanel.add(lblAction); regularPanel1 = new HorizontalPanel(); regularPanel1.setSpacing(HORIZONTAL_SPACING); regularPanel1.add(groupHyperlink); regularPanel1.add(new Label(LocaleText.get("ofTheFollowingApply"))); verticalPanel.add(regularPanel1); //verticalPanel.add(new ConditionWidget(FormDefTest.getPatientFormDef(),this)); verticalPanel.add(addConditionLink); setupHandlers(); verticalPanel.setSpacing(VERTICAL_SPACING); initWidget(verticalPanel); } private void setupHandlers(){ chkUseAdvanced.addValueChangeHandler(new ValueChangeHandler<Boolean>() { public void onValueChange(ValueChangeEvent<Boolean> event) { questionDef.setHasAdvancedConstraint(chkUseAdvanced.getValue()); setAdvancedMode(chkUseAdvanced.getValue()); if(event.getValue()){ fromSimpleToAdvancedConstraint(); }else{ fromAdvancedToSimpleConstraint(); } setQuestionDef(questionDef); } }); advtxtconstraint.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { questionDef.setAdvancedConstraint(advtxtconstraint.getText()); //and if we've done that the hasAdvanced() should definitely return true so: questionDef.setHasAdvancedConstraint(true); } }); addConditionLink.addClickHandler(new ClickHandler(){ public void onClick(ClickEvent event){ addCondition(); } }); txtErrorMessage.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { ValidationRule currentRule = formDef.getValidationRule(questionDef); if(currentRule != null){ currentRule.setErrorMessage(txtErrorMessage.getText()); }else{ //create the rull since we'll be needing it anyway, and add to the formDef currentRule = new ValidationRule(questionDef.getId(), formDef); formDef.addValidationRule(currentRule); } } }); } /** * Converts from and advanced to simple relevant * (simple uses SkipRules). * Returns false if not successfully converted * Returns true if succesful. * * Will keep data in advanced if succesful. * Will always overwrite existing SkipRules * if they exist. On return false, will * blank out existing SkipRules * @return */ public boolean fromAdvancedToSimpleConstraint(){ questionDef.setHasAdvancedConstraint(false); String currentAdvCons = questionDef.getAdvancedConstraint(); boolean hasAdv = (currentAdvCons != null && !currentAdvCons.isEmpty()); ValidationRule currentVR = formDef.getValidationRule(questionDef); if(currentVR != null){ formDef.removeValidationRule(currentVR); } if(!hasAdv){ return true; //the 'conversion' was a success, because there was nothing to convert. }else{ //do the conversion using the existing SR parser. currentVR = ConstraintParser.buildValidationRule(formDef, questionDef.getId(), currentAdvCons, questionDef.getId(), ModelConstants.ACTION_ENABLE); if(currentVR != null){ formDef.addValidationRule(currentVR); return true; }else{ return false; } } } /** * Converts a simple relevant (that uses SkipRules) * and converts to Advanced relevant (if possible). * * Returns false if unsuccesful * Returns true if the succesful. * * Leaves the old skipRules intact * * If no SkipRules are present, will use * data stored in AdvancedRelevant in the QuestionDef * (if possible). * * Will OVERWRITE any existing advanced relevant data * if SkipRules are present * @return */ public boolean fromSimpleToAdvancedConstraint(){ ValidationRule currentRule = formDef.getValidationRule(questionDef); String currentAdvVR = questionDef.getAdvancedConstraint(); questionDef.setHasAdvancedConstraint(true); boolean currentAdvConsExists = (currentAdvVR != null && !currentAdvVR.isEmpty()); if(currentRule == null){ //start from scratch, use existing AdvRel (if it exists). currentRule = createNewValidationRuleForQtn(questionDef); formDef.addValidationRule(currentRule); return true; } //if we get here, it implies we already have a skip rule pointing to this question //in the FormDef, so no need to worry about it. String newAdvCons = ConstraintBuilder.fromValidationRule2String(currentRule, formDef); boolean newAdvConsExists = (newAdvCons != null && !newAdvCons.isEmpty()); if(!newAdvConsExists){ if(currentAdvConsExists){ //we already have advanced text that isn't empty, so stick with that and return false to indicate something went wrong with conversion. return false; }else{ questionDef.setAdvancedConstraint(""); //we've converted nothing and had nothing, so everything went well. return true; } }else{ questionDef.setAdvancedConstraint(newAdvCons); return true; //great success. } } public ValidationRule createNewValidationRuleForQtn(IFormElement qtn){ ValidationRule vr = new ValidationRule(qtn.getId(),new Vector(), txtErrorMessage.getText()); // ValidationRule sr = new ValidationRule(formDef.getNextSkipRuleId(), new Vector(), ModelConstants.ACTION_ENABLE, new Vector()); // vr.addActionTarget(qtn.getId()); return vr; } private void setAdvancedMode(boolean enabled){ advancedPanel.setVisible(enabled); addConditionLink.setVisible(!enabled); lblAction.setVisible(!enabled); regularPanel1.setVisible(!enabled); regularPanel2.setVisible(true); setConditionWidgetVisible(!enabled); } private void setConditionWidgetVisible(boolean enable){ int count = verticalPanel.getWidgetCount(); for(int i=0; i<count; i++){ Widget widget = verticalPanel.getWidget(i); if(widget instanceof ConditionWidget){ widget.setVisible(enable); } } } /** * Adds a new condition. */ public void addCondition(){ if(formDef != null && enabled){ verticalPanel.remove(addConditionLink); ConditionWidget conditionWidget = new ConditionWidget(formDef,this,false,questionDef); conditionWidget.setQuestionDef(questionDef); verticalPanel.add(conditionWidget); verticalPanel.add(addConditionLink); // txtErrorMessage.setFocus(true); /*String text = txtErrorMessage.getText(); if(text != null && text.trim().length() == 0){ txtErrorMessage.setText(LocaleText.get("errorMessage")); //txtErrorMessage.selectAll(); txtErrorMessage.setFocus(true); }*/ } } /** * 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(validationRule != null) validationRule.removeCondition(conditionWidget.getCondition()); verticalPanel.remove(conditionWidget); } /** * Sets or updates the values of the validation rule object from the user's widget selections. */ public void updateValidationRule(){ if(questionDef == null){ validationRule = null; return; } if(validationRule == null) validationRule = new ValidationRule(questionDef.getId(),formDef); validationRule.setErrorMessage(txtErrorMessage.getText()); int count = verticalPanel.getWidgetCount(); for(int i=0; i<count; i++){ Widget widget = verticalPanel.getWidget(i); if(widget instanceof ConditionWidget){ Condition condition = ((ConditionWidget)widget).getCondition(); if(condition != null && !validationRule.containsCondition(condition) && condition.getValue() != null) validationRule.addCondition(condition); else if(condition != null && validationRule.containsCondition(condition)){ if(condition.getValue() != null) validationRule.updateCondition(condition); else validationRule.removeCondition(condition); } } } if(validationRule.getConditions() == null || validationRule.getConditionCount() == 0){ // formDef.removeValidationRule(validationRule); validationRule = null; } else validationRule.setConditionsOperator(groupHyperlink.getConditionsOperator()); if(validationRule != null && !formDef.containsValidationRule(validationRule)) formDef.addValidationRule(validationRule); } /** * Sets the question definition object which is the target of the validation rule. * * @param questionDef the question definition object. */ public void setQuestionDef(IFormElement questionDef){ clearConditions(); if(questionDef != null) formDef = questionDef.getParentFormDef(); else formDef = null; /*if(questionDef.getParent() instanceof PageDef) formDef = ((PageDef)questionDef.getParent()).getParent(); else formDef = ((PageDef)((QuestionDef)questionDef.getParent()).getParent()).getParent();*/ if(questionDef != null) lblAction.setText(questionDef.getDisplayText() + " "+LocaleText.get("isValidWhen")); else lblAction.setText("No question selected!"); this.questionDef = questionDef; if(formDef != null){ validationRule = formDef.getValidationRule(questionDef); if(validationRule != null){ groupHyperlink.setCondionsOperator(validationRule.getConditionsOperator()); txtErrorMessage.setText(validationRule.getErrorMessage()); verticalPanel.remove(addConditionLink); Vector conditions = validationRule.getConditions(); Vector lostConditions = new Vector(); for(int i=0; i<conditions.size(); i++){ ConditionWidget conditionWidget = new ConditionWidget(formDef,this,false,questionDef); if(conditionWidget.setCondition((Condition)conditions.elementAt(i))) verticalPanel.add(conditionWidget); else lostConditions.add((Condition)conditions.elementAt(i)); } for(int i=0; i<lostConditions.size(); i++) validationRule.removeCondition((Condition)lostConditions.elementAt(i)); if(validationRule.getConditionCount() == 0){ // formDef.removeValidationRule(validationRule); validationRule = null; } verticalPanel.add(addConditionLink); // chkUseAdvanced.setValue(questionDef.hasAdvancedDConstraint()); // advtxtconstraint.setText(questionDef.advancedConstraintText()); } } checkSetAdvanced(); } /** * Checks to see if this question has an advanced relevant and sets the widgets * on this view accordingly */ private void checkSetAdvanced(){ if(questionDef instanceof OptionDef || questionDef instanceof FormDef || questionDef == null){ return; } chkUseAdvanced.setValue(questionDef.hasAdvancedConstraint()); if(questionDef.hasAdvancedConstraint()){ advtxtconstraint.setText(questionDef.getAdvancedConstraint()); } ValidationRule vr = formDef.getValidationRule(questionDef); if(vr != null){ txtErrorMessage.setText(vr.getErrorMessage()); }else{ txtErrorMessage.setText(""); } setAdvancedMode(chkUseAdvanced.getValue()); } /** * Sets the form definition object to which this validation rule belongs. * * @param formDef the form definition object. */ public void setFormDef(FormDef formDef){ updateValidationRule(); this.formDef = formDef; this.questionDef = null; clearConditions(); } /** * Removes all validation rule conditions. */ private void clearConditions(){ if(questionDef != null) updateValidationRule(); questionDef = null; lblAction.setText(LocaleText.get("question")+": "); Iterator widgetIter = verticalPanel.iterator(); while(widgetIter.hasNext()){ Widget widget = (Widget)widgetIter.next(); if(widget instanceof ConditionWidget){ widgetIter.remove(); } } txtErrorMessage.setText(null); } /** * 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; this.groupHyperlink.setEnabled(enabled); txtErrorMessage.setEnabled(enabled); if(!enabled) clearConditions(); } /** * @see com.google.gwt.user.client.WindowResizeListener#onWindowResized(int, int) */ public void onWindowResized(int width, int height){ // if(width - 700 > 0) // txtErrorMessage.setWidth(width - 700 + PurcConstants.UNITS); } }