package org.openrosa.client.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.openrosa.client.util.Itext; import org.openrosa.client.util.ItextParser; import org.openrosa.client.xforms.XformBuilder; import org.openrosa.client.locale.LocaleText; import org.openrosa.client.model.ModelConstants; import org.openrosa.client.util.FormUtil; import org.openrosa.client.xforms.UiElementBuilder; import org.openrosa.client.xforms.XformConstants; import org.openrosa.client.xforms.XformUtil; import org.openrosa.client.xforms.XmlUtil; import com.google.gwt.user.client.Window; import com.google.gwt.xml.client.Document; import com.google.gwt.xml.client.Element; import com.google.gwt.xml.client.Node; /** The definition of a group of questions on a questionaire. * * @author Daniel Kayiwa * */ public class GroupDef implements IFormElement, Serializable{ /** List of children for this group. */ private List<IFormElement> children; /** The name of the group. */ private String defaultLabel = ModelConstants.EMPTY_STRING; /** The help text of the group. */ private String helpText = ModelConstants.EMPTY_STRING; /** The xforms label node for this group. */ private Element labelNode; /** The xforms hint node for this group. */ private Element hintNode; /** The xforms group node for this page. */ private Element groupNode; private Element bindNode; private Element dataNode; /** The parent definition to which this group belongs. */ private IFormElement parent; private String binding; private int id; private String itextId; private int dataType = QuestionDef.QTN_TYPE_GROUP; private boolean required,enabled; private boolean hasAdvancedCalculate, hasAdvancedConstraint, hasAdvancedRelevant; private String advancedCalculate, advancedConstraint, advancedRelevant; public GroupDef(){ } /** * Constructs a new page. * * @param parent the parent element to which the page belongs. */ public GroupDef(IFormElement parent) { this.parent = parent; required = false; enabled = true; } /** * Creates a new copy of a page from an existing one. * * @param pageDef the page to copy. * @param parent the form to which the page belongs. */ public GroupDef(GroupDef pageDef,IFormElement parent) { this(parent); setQuestionID(pageDef.getQuestionID()); setChildren(pageDef.getChildren()); setItextId(pageDef.getItextId()); setItextId(getQuestionID()); } /** * Constructs a page object with the following parameters. * * @param name the name of the page. * @param pageNo the number of the page. * @param parent the form to which the page belongs. */ public GroupDef(String name,IFormElement parent) { this(parent); setQuestionID(name); setChildren(children); setItextId(name); } /** * Constructs a page with the following parameters. * * @param name the name of the page. * @param pageNo the number of the page. * @param questions a list of questions in the page. * @param parent the form to which the page belongs. */ public GroupDef(String name,List<IFormElement> children, IFormElement parent) { this(parent); setQuestionID(name); setChildren(children); setItextId(name); } /** * Gets the form to which this question belongs. * * @return the form. */ public FormDef getParentFormDef(){ return (FormDef)getParentFormDef(this); } private IFormElement getParentFormDef(IFormElement questionDef){ IFormElement parent = questionDef.getParent(); if(parent instanceof FormDef) return parent; return getParentFormDef(parent); } public IFormElement getParent() { return parent; } public void setParent(IFormElement parent) { this.parent = parent; } /** * @return the labelNode */ public Element getLabelNode() { return labelNode; } /** * @param labelNode the labelNode to set */ public void setLabelNode(Element labelNode) { this.labelNode = labelNode; // if(itextId == null) // setItextId(XmlUtil.getItextId(labelNode)); } /** * @return the groupNode */ public Element getGroupNode() { return groupNode; } /** * @param groupNode the groupNode to set */ public void setGroupNode(Element groupNode) { this.groupNode = groupNode; } /** * Gets the number of questions on this page. * * @return the number of questions. */ public int getChildCount(){ if(children == null) return 0; int count = 0; for(int index = 0; index < children.size(); index++){ IFormElement element = children.get(index); if(element instanceof GroupDef) count += ((GroupDef)element).getChildCount(); else{ assert(element instanceof QuestionDef); count += 1; } } return count; } /** * Gets the question at a given position on this page. * * @param index the position. * @return the question. */ public IFormElement getChildAt(int index){ if(children == null) return null; return (IFormElement)children.get(index); } /** * Adds a question to the page. * * @param qtn the question to add. */ public void addChild(IFormElement child){ if(children == null) children = new ArrayList<IFormElement>(); children.add(child); child.setParent(this); } public QuestionDef getQuestion(String varName){ return (QuestionDef)getElement(varName); } public QuestionDef getQuestion(int id){ return (QuestionDef)getElement(id); } public int getElementIndex(String varName){ if(children == null) return -1; for(int i=0; i<children.size(); i++){ IFormElement def = children.get(i); if(varName.equals(def.getQuestionID())) return i; } return -1; } /** * Gets a question with a given identifier. * * @param id the question identifier. * @return the question. */ public IFormElement getElement(int id){ if(children == null) return null; for(int i=0; i<children.size(); i++){ IFormElement def = children.get(i); if(def.getId() == id) return def; //Without this, then we have not validation and skip rules in repeat questions. if(def instanceof GroupDef){ IFormElement elem = ((GroupDef)def).getElement(id); if(elem != null) return elem; } /*if(def.getDataType() == QuestionDef.QTN_TYPE_REPEAT && def.getRepeatQtnsDef() != null){ def = def.getRepeatQtnsDef().getElement(id); if(def != null) return def; }*/ } return null; } @Override public String toString() { return getDisplayText(); } /** * Copies a list of questions into this page. * * @param questions the list of questions to copy. */ private void copyChildren(List<IFormElement> children){ if(children != null){ this.children = new ArrayList<IFormElement>(); for(int i=0; i<children.size(); i++){ IFormElement child = children.get(i); if(child instanceof QuestionDef) this.children.add((IFormElement)new QuestionDef((QuestionDef)children.get(i),this)); else if(child instanceof GroupDef) this.children.add((IFormElement)new GroupDef((GroupDef)children.get(i),this)); } } } /** * Removes a question from this page. * * @param qtnDef the question to remove. * @param formDef the form to which this page belongs. * @return true if the question was found and removed successfully, else false. */ public boolean removeElement(IFormElement qtnDef, FormDef formDef, boolean delete){ removeElement2(qtnDef,formDef, delete); //TODO Need to do recursive checks for group defs before remove. return children.remove(qtnDef); } public static void removeElement2(IFormElement qtnDef, FormDef formDef, boolean delete){ if(qtnDef.getControlNode() != null && qtnDef.getControlNode().getParentNode() != null){ if(qtnDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT) qtnDef.getControlNode().getParentNode().getParentNode().removeChild(qtnDef.getControlNode().getParentNode()); else qtnDef.getControlNode().getParentNode().removeChild(qtnDef.getControlNode()); } //Either no / or just one occurrence. More than one nestings are avoided to make things simple if(qtnDef.getQuestionID() != null && qtnDef.getQuestionID().indexOf('/') == qtnDef.getQuestionID().lastIndexOf('/')){ if(qtnDef.getDataNode() != null && qtnDef.getDataNode().getParentNode() != null) qtnDef.getDataNode().getParentNode().removeChild(qtnDef.getDataNode()); if(qtnDef.getBindNode() != null && qtnDef.getBindNode().getParentNode() != null) qtnDef.getBindNode().getParentNode().removeChild(qtnDef.getBindNode()); } if(formDef != null){ formDef.removeQtnFromRules(qtnDef); formDef.removeQtnFromDynamicLists(qtnDef); } } /** * Removes all questions from this page. * * @param formDef the form to which the page belongs. */ public void removeAllElements(FormDef formDef){ if(children == null) return; while(children.size() > 0) removeElement((QuestionDef)children.get(0),formDef, true); } /** * Gets the number of questions on this page. * * @return the number of questions. */ public int size(){ if(children == null) return 0; return children.size(); } /** * Moves a question up by one position in the page. * * @param questionDef the question to move. */ public void moveElementUp(IFormElement questionDef){ moveElementUp(children,questionDef); } /** * Moves a question up by one position in a list of questions. * * @param questions the list of questions. * @param questionDef the question to move. */ public static void moveElementUp(List<IFormElement> children, IFormElement questionDef){ int index = children.indexOf(questionDef); //Not relying on group node because some forms have no groups Element controlNode = questionDef.getControlNode(); Element parentNode = controlNode != null ? (Element)controlNode.getParentNode() : null; if(questionDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT && controlNode != null){ controlNode = (Element)controlNode.getParentNode(); parentNode = (Element)parentNode.getParentNode(); } children.remove(questionDef); //Store the question to replace IFormElement currentElement = children.get(index-1); if(controlNode != null && parentNode != null && currentElement.getControlNode() != null) parentNode.removeChild(controlNode); if(!(questionDef.getQuestionID().indexOf('/') > -1)){ if(questionDef.getDataNode() != null && questionDef.getDataNode().getParentNode() != null && currentElement.getDataNode() != null) questionDef.getDataNode().getParentNode().removeChild(questionDef.getDataNode()); } if(questionDef.getBindNode() != null && questionDef.getBindNode().getParentNode() != null && currentElement.getBindNode() != null) questionDef.getBindNode().getParentNode().removeChild(questionDef.getBindNode()); List<IFormElement> list = new ArrayList<IFormElement>(); while(children.size() >= index){ currentElement = (IFormElement)children.get(index-1); list.add(currentElement); children.remove(currentElement); } children.add(questionDef); for(int i=0; i<list.size(); i++){ if(i == 0 && controlNode != null){ QuestionDef qtnDef = (QuestionDef)list.get(i); if(qtnDef.getControlNode() != null && parentNode != null){ Node sibNode = qtnDef.getControlNode(); if(qtnDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT) sibNode = sibNode.getParentNode(); parentNode.insertBefore(controlNode, sibNode); } //move data node (We are not moving nested data nodes just to avoid complications if(!(questionDef.getQuestionID().indexOf('/') > -1 || qtnDef.getQuestionID().indexOf('/') > -1)){ if(qtnDef.getDataNode() != null && qtnDef.getDataNode().getParentNode() != null && questionDef.getDataNode() != null) qtnDef.getDataNode().getParentNode().insertBefore(questionDef.getDataNode(), qtnDef.getDataNode()); } //move binding node if(qtnDef.getBindNode() != null && qtnDef.getBindNode().getParentNode() != null && questionDef.getBindNode() != null) qtnDef.getBindNode().getParentNode().insertBefore(questionDef.getBindNode(), qtnDef.getBindNode()); } children.add(list.get(i)); } } /** * Moves a question down by one position in the page. * * @param questionDef the question to move. */ public void moveElementDown(IFormElement questionDef){ moveElementDown(children,questionDef); } /** * Moves a question down by one position in a list of questions. * * @param elements the list of questions. * @param element the question to move. */ public static void moveElementDown(List<IFormElement> elements, IFormElement element){ int index = elements.indexOf(element); //Not relying on group node because some forms have no groups Element controlNode = element.getControlNode(); Element parentNode = controlNode != null ? (Element)controlNode.getParentNode() : null; if(element.getDataType() == QuestionDef.QTN_TYPE_REPEAT && controlNode != null){ controlNode = (Element)controlNode.getParentNode(); parentNode = (Element)parentNode.getParentNode(); } elements.remove(element); Node parentDataNode = element.getDataNode() != null ? element.getDataNode().getParentNode() : null; Node parentBindNode = element.getBindNode() != null ? element.getBindNode().getParentNode() : null; /*if(controlNode != null && parentNode != null) parentNode.removeChild(questionDef.getControlNode()); if(questionDef.getDataNode() != null && questionDef.getDataNode().getParentNode() != null) questionDef.getDataNode().getParentNode().removeChild(questionDef.getDataNode()); if(questionDef.getBindNode() != null && questionDef.getBindNode().getParentNode() != null) questionDef.getBindNode().getParentNode().removeChild(questionDef.getBindNode());*/ IFormElement currentItem; // = parent.getChild(index - 1); List<IFormElement> list = new ArrayList<IFormElement>(); while(elements.size() > 0 && elements.size() > index){ currentItem = elements.get(index); list.add(currentItem); elements.remove(currentItem); } for(int i=0; i<list.size(); i++){ if(i == 1){ elements.add(element); //Add after the first item. if(controlNode != null){ if(controlNode != null && parentNode != null) parentNode.removeChild(controlNode); IFormElement qtnDef = getNextSavedElement(list,i); //(QuestionDef)list.get(i); if(qtnDef.getControlNode() != null){ Node sibNode = qtnDef.getControlNode(); if(qtnDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT) sibNode = sibNode.getParentNode(); parentNode.insertBefore(controlNode, sibNode); } else parentNode.appendChild(controlNode); //move data node (We are not moving nested data nodes just to avoid complications if(!(element.getQuestionID().indexOf('/') > -1 || qtnDef.getQuestionID().indexOf('/') > -1)) if(element.getDataNode() != null && element.getDataNode().getParentNode() != null){ parentDataNode.removeChild(element.getDataNode()); if(qtnDef.getDataNode() != null){ if(qtnDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT && qtnDef.getQuestionID().contains("/")) parentDataNode.insertBefore(element.getDataNode(), qtnDef.getDataNode().getParentNode()); else parentDataNode.insertBefore(element.getDataNode(), qtnDef.getDataNode()); } else parentDataNode.appendChild(element.getDataNode()); } //move binding node if(parentBindNode != null){ if(element.getBindNode() != null && element.getBindNode().getParentNode() != null) parentBindNode.removeChild(element.getBindNode()); if(qtnDef.getBindNode() != null) parentBindNode.insertBefore(element.getBindNode(), qtnDef.getBindNode()); else parentBindNode.appendChild(element.getBindNode()); } } } elements.add(list.get(i)); } if(list.size() == 1){ elements.add(element); if(controlNode != null){ if(element.getControlNode() != null && parentNode != null){ parentNode.removeChild(controlNode); parentNode.appendChild(controlNode); } if(!(element.getQuestionID().indexOf('/') > -1)){ if(element.getDataNode() != null && parentDataNode != null){ parentDataNode.removeChild(element.getDataNode()); parentDataNode.appendChild(element.getDataNode()); } } //parentDataNode.insertBefore(questionDef.getDataNode(), questionDef.getDataNode()); if(element.getBindNode() != null && parentBindNode != null){ parentBindNode.removeChild(element.getBindNode()); parentBindNode.appendChild(element.getBindNode()); } //parentBindNode.insertBefore(questionDef.getBindNode(), questionDef.getBindNode()); } } } /** * Gets the next question which has been converted to xforms and * hence attached to an xforms document node, starting at a given * index in a list of questions. * * @param questions the list of questions. * @param index the index to start from in the questions list. * @return the question. */ private static IFormElement getNextSavedElement(List<IFormElement> questions, int index){ int size = questions.size(); for(int i=index; i<size; i++){ IFormElement questionDef = questions.get(i); if(questionDef.getControlNode() != null) return questionDef; } return questions.get(index); } /** * Checks if this page contains a particular question. * * @param qtn the question to check. * @return true if it contains, else false. */ public boolean contains(IFormElement qtn){ return children.contains(qtn); } /** * Updates the xforms document nodes referenced by this page and all its children. * * @param doc the xforms document. * @param xformsNode the xforms document root node. * @param formDef the form to which this page belongs. * @param formNode the xforms instance data node. * @param modelNode the xforms model node. * @param withData set to true to also update the xforms instance data values from question answers. * @param rootDataNodeName the original form variable name before any updates were done. */ public void updateDoc(Document doc, Element xformsNode, FormDef formDef, Element formNode, Element modelNode, boolean withData, String rootDataNodeName){ boolean allQuestionsNew = areAllQuestionsNew(); boolean needsDOMNodes = false; Element groupNode = (this.getDataType() == QuestionDef.QTN_TYPE_GROUP ? getGroupNode() : getParent().getControlNode()); if(groupNode == null){ XformBuilder.fromGroupDef2Xform(this, doc, xformsNode, formDef, formNode, modelNode); groupNode = this.getGroupNode(); if(groupNode == null){ throw new RuntimeException("Problem with getting groupNode for question: "+this.getDisplayText()); } } Element labelNode = getLabelNode(); if(this.getDataType() == QuestionDef.QTN_TYPE_GROUP){ needsDOMNodes = (getLabelNode() == null && groupNode == null); }else if(this.getDataType() == QuestionDef.QTN_TYPE_REPEAT){ needsDOMNodes = (getParent().getControlNode() == null && getParent().getLabelNode() == null); } if(needsDOMNodes){ //Must be new page{ XformBuilder.fromGroupDef2Xform(this,doc,xformsNode,formDef,formNode,modelNode); } if(getDataType() == QuestionDef.QTN_TYPE_GROUP){ if(groupNode != null && !groupNode.getNodeName().contains(XformConstants.NODE_NAME_GROUP_MINUS_PREFIX)){ String nodeName = groupNode.getNodeName(); String xml = groupNode.toString(); xml = xml.replace(nodeName, XformConstants.NODE_NAME_GROUP); Element child = XformUtil.getNode(xml); child = (Element)groupNode.getOwnerDocument().importNode(child, true); groupNode.getParentNode().replaceChild(child, groupNode); groupNode = child; } groupNode.setAttribute("nodeset", getDataNodesetPath()); } if(labelNode != null){ XmlUtil.setTextNodeValue(getLabelNode(),getText()); }else{ Element label = doc.createElement(XformConstants.NODE_NAME_LABEL); label.appendChild(doc.createTextNode(getText())); groupNode.appendChild(label); this.setLabelNode(label); } UiElementBuilder.addItextRefs(labelNode, this); List<IFormElement> newElements = new ArrayList<IFormElement>(); if(children != null){ for(int i=0; i<children.size(); i++){ IFormElement questionDef = children.get(i); if(!allQuestionsNew && questionDef instanceof QuestionDef && questionDef.getDataNode() == null){ newElements.add(questionDef); } if(questionDef instanceof QuestionDef){ ((QuestionDef)questionDef).updateDoc(doc,xformsNode,formDef,formNode,modelNode,(groupNode == null) ? xformsNode : groupNode, dataType != QuestionDef.QTN_TYPE_REPEAT, withData, rootDataNodeName); }else{ ((GroupDef)questionDef).updateDoc(doc,xformsNode,formDef,formNode,modelNode,withData,rootDataNodeName); } } } for(int k = 0; k < newElements.size(); k++){ IFormElement element = newElements.get(k); int proposedIndex = children.size() - (newElements.size() - k); int currentIndex = children.indexOf(element); if(currentIndex == proposedIndex) continue; moveElementNodesUp(element,getRefElemement(children,newElements,currentIndex /*currentIndex+1*/)); } } public List<String> getAllChildrenItextIDs(){ ArrayList<String> list = new ArrayList<String>(); List<IFormElement> children = this.getChildren(); if(children == null){ return new ArrayList<String>(); } for(IFormElement child : children){ list.addAll(Itext.getFullAvailableTextForms(child.getItextId())); //get the child's ItextID(s) list.addAll(child.getAllChildrenItextIDs()); //recurse down } return list; } /** * Gets the first question which is not new, in a given list of questions, * starting at a given position. * * @param questions the list of questions to traverse. * @param newQuestions the list of new questions. * @param index the position to start from. * @return the question. */ private IFormElement getRefElemement(List<IFormElement> questions, List<IFormElement> newQuestions, int index){ IFormElement questionDef; int i = index + 1; while(i < questions.size()){ questionDef = questions.get(i); if(!newQuestions.contains(questionDef)) return questionDef; i++; } return null; } /** * Checks if all question on this page are new. * * @return true if all are new, else false. */ private boolean areAllQuestionsNew(){ if(children == null) return false; for(int i=0; i<children.size(); i++){ IFormElement questionDef = children.get(i); if(questionDef.getControlNode() != null){ return false; } } return true; } /** * Gets a question with a given text. * * @param text the text. * @return the question. */ public IFormElement getQuestionWithText(String text){ if(children == null) return null; for(int i=0; i<children.size(); i++){ IFormElement questionDef = children.get(i); if(text.equals(Itext.getDisplayText(questionDef))){ return questionDef; } if(questionDef instanceof GroupDef){ IFormElement elem = ((GroupDef)questionDef).getQuestionWithText(text); if(elem != null) return elem; } /*else if(questionDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT){ //TODO Need to make sure this new addition does not introduce bugs questionDef = questionDef.getRepeatQtnsDef().getQuestionWithText(text); if(questionDef != null) return questionDef; }*/ } return null; } /** * Updates this pageDef (as the main or new from a refresh xml) with the parameter one (existing or the one being refreshed) * * @param groupDef */ public void refresh(GroupDef groupDef){ //if(pageNo == pageDef.getPageNo()) // name = pageDef.getName(); int count = groupDef.getChildCount(); for(int index = 0; index < count; index++){ IFormElement qtn = groupDef.getChildAt(index); IFormElement element = this.getElement(qtn.getQuestionID()); if(element == null) continue; //Possibly this question was deleted on server element.refresh(qtn); /*int index1 = this.getQuestionIndex(qtn.getVariableName()); if(index != index1 && index1 != -1 && index < this.getQuestionCount() - 1){ this.getQuestions().removeElement(questionDef); this.getQuestions().insertElementAt(questionDef, index); }*/ } } /** * Moves the question xforms document nodes by one position upwards. * * @param element the question whose xforms nodes to move. * @param refElement the question immediately above which we are to move. */ public void moveElementNodesUp(IFormElement element, IFormElement refElement){ //Not relying on group node because some forms have no groups Element eControlNode = element.getControlNode(); Element eControlParentNode = eControlNode != null ? (Element)eControlNode.getParentNode() : null; Element eDataNode = element.getDataNode(); Element eBindNode = element.getBindNode(); Element rBindNode = refElement.getBindNode(); Element rControlNode = refElement.getControlNode(); Element rDataNode = refElement.getDataNode(); if(element.getDataType() == QuestionDef.QTN_TYPE_REPEAT && eControlNode != null){ eControlNode = (Element)eControlNode.getParentNode(); eControlParentNode = (Element)eControlParentNode.getParentNode(); } if(eControlNode != null){ eControlParentNode.removeChild(eControlNode); } if(eDataNode != null){ eDataNode.getParentNode().removeChild(eDataNode); } if(eBindNode != null){ eBindNode.getParentNode().removeChild(eBindNode); } if(rControlNode != null){ Node sibNode = rControlNode; if(refElement.getDataType() == QuestionDef.QTN_TYPE_REPEAT) sibNode = sibNode.getParentNode(); if(eControlParentNode != null){ eControlParentNode.insertBefore(eControlNode, sibNode); } } if(rDataNode != null){ rDataNode.getParentNode().insertBefore(eDataNode, rDataNode); } if(rBindNode != null){ Node parentBindOfRefElement = rBindNode.getParentNode(); if(parentBindOfRefElement != null){ parentBindOfRefElement.insertBefore(eBindNode, rBindNode); } } } /** * Returns the element specified by varName. * if varName matches the parent node, return self, * else go through children elements and return a match * If no match is found, return null. * @param varName * @return the IFormElement that matches varName or null if no match. */ public IFormElement getElement(String varName){ if(varName == null || children == null) return null; if(getQuestionID().equals(varName)){ return this; } IFormElement retElement; for(int i=0;i<children.size();i++){ IFormElement def = children.get(i); retElement = def.getElement(varName); if(retElement != null){ return retElement; //there should only ever be one match. } } return null; } /** * Updates the xforms instance data nodes referenced by this page's questions. * * @param parentDataNode the parent data node for this page. */ public void updateDataNodes(Element parentDataNode){ if(children == null) return; for(int i=0; i<children.size(); i++) ((QuestionDef)children.get(i)).updateDataNodes(parentDataNode); } /** * Builds the language translation nodes for text in this page and all its children. * * @param doc the language translation document. * @param parentLangNode the language parent node for the page language nodes. */ public void buildLanguageNodes(String xpath, com.google.gwt.xml.client.Document doc, Element parentLangNode){ if(labelNode == null || groupNode == null) return; if(xpath == null) xpath = FormUtil.getNodePath(groupNode); else xpath += "/" + FormUtil.getNodeName(groupNode); String id = groupNode.getAttribute(XformConstants.ATTRIBUTE_NAME_ID); if(id != null && id.trim().length() > 0) xpath += "[@" + XformConstants.ATTRIBUTE_NAME_ID + "='" + id + "']"; Element node = doc.createElement(XformConstants.NODE_NAME_TEXT); node.setAttribute(XformConstants.ATTRIBUTE_NAME_XPATH, xpath + "/" + FormUtil.getNodeName(labelNode)); node.setAttribute(XformConstants.ATTRIBUTE_NAME_VALUE, defaultLabel); node.setAttribute(XformConstants.ATTRIBUTE_NAME_ID, itextId); parentLangNode.appendChild(node); if(children == null) return; for(int i=0; i<children.size(); i++){ IFormElement element = children.get(i); if(element instanceof QuestionDef) ((QuestionDef)element).buildLanguageNodes(xpath,doc,groupNode,parentLangNode); else ((GroupDef)element).buildLanguageNodes(xpath, doc, parentLangNode); } } /** * Removes all question change event listeners. */ public void clearChangeListeners(){ if(children == null) return; for(int i=0; i<children.size(); i++) ((QuestionDef)children.get(i)).clearChangeListeners(); } public int getId(){ return id; } public void setId(int id){ this.id = id; } public String getText(){ return defaultLabel; } public void setText(String text){ this.defaultLabel = text; } public int getDataType(){ return dataType; } public void setDataType(int dataType){ if(dataType == QuestionDef.QTN_TYPE_REPEAT) throw new RuntimeException(); //this should never happen. this.dataType = dataType; } public String getQuestionID(){ return binding; } public void setQuestionID(String binding){ this.binding = binding; } public List<IFormElement> getChildren(){ return children; } public void setChildren(List<IFormElement> children){ this.children = children; } public Element getBindNode(){ return bindNode; } public void setBindNode(Element bindNode){ this.bindNode = bindNode; } public Element getDataNode(){ return dataNode; } public void setDataNode(Element dataNode){ this.dataNode = dataNode; } public void refresh(IFormElement element){ } public Element getControlNode(){ return groupNode; } public void setControlNode(Element controlNode){ groupNode = controlNode; } public IFormElement copy(IFormElement parent){ return new GroupDef(this, parent); } public String getDisplayText(){ if(getText() != null && !getText().isEmpty()){ return getText(); }else{ return getQuestionID(); } } public String getItextId() { return itextId; } public void setItextId(String itextId) { this.itextId = itextId; } public String getHelpText(){ return helpText; } public void setHelpText(String helpText){ this.helpText = helpText; } public Element getHintNode(){ return hintNode; } public void setHintNode(Element hintNode){ this.hintNode = hintNode; } public boolean removeChild(IFormElement element){ if(children == null) return false; if(children.remove(element)) return true; for(IFormElement child : children){ if(child.removeChild(element)) return true; } return false; //return this.removeElement(qtnDef, formDef); } public FormDef getFormDef(){ IFormElement element = getParent(); if(parent instanceof FormDef) return (FormDef)element; return element.getFormDef(); } /** * Get the Nodeset ref that points to the data node where the question's answer will be stored. * @return */ public String getDataNodesetPath(){ if(getParent() == null){ return "/"+getQuestionID(); }else{ return getParent().getDataNodesetPath() + "/"+getQuestionID(); } } public boolean isLocked(){ return false; } public boolean isRequired(){ return required; } public boolean isEnabled(){ return enabled; } public String getDefaultValue(){ return null; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public void setLocked(boolean locked) { return; } @Override public void setRequired(boolean required) { this.required = required; } public boolean hasUINode() { return true; } public void setHasUINode(boolean hasUINode) { return; //groups always have some kind of UI node. If it doesn't, you've screwed up. } public void moveChildToIndex(IFormElement child, int index) throws Exception{ if(!children.contains(child)){ throw new Exception("Child not in Children list!"); } children.remove(child); int i = (index > children.size()) ? children.size() : index; try{ children.add(i, child); }catch(Exception e){ FormUtil.displayException(e); } } public boolean hasAdvancedCalculate() { return hasAdvancedCalculate; } public boolean hasAdvancedConstraint() { return hasAdvancedConstraint; } public boolean hasAdvancedRelevant() { return hasAdvancedRelevant; } public void setHasAdvancedCalculate(boolean enabled) { hasAdvancedCalculate = enabled; } public void setHasAdvancedConstraint(boolean enabled) { hasAdvancedConstraint = enabled; } public void setHasAdvancedRelevant(boolean enabled) { hasAdvancedRelevant = enabled; } public String getAdvancedCalculate() { return advancedCalculate; } public String getAdvancedConstraint() { return advancedConstraint; } public String getAdvancedRelevant() { return advancedRelevant; } public void setAdvancedCalculate(String calcValue) { advancedCalculate = calcValue; } public void setAdvancedConstraint(String constValue) { advancedConstraint = constValue; } public void setAdvancedRelevant(String releValue) { advancedRelevant = releValue; } public boolean insertChildAfter(IFormElement child, IFormElement target) { boolean isChildQuestionOrGroupDef = (child instanceof GroupDef || child instanceof QuestionDef); if(!isChildQuestionOrGroupDef){ return false; } //we don't want to insert OptionDefs into this list. return FormDef.insertChildBeforeOrAfter(child, target, this.children, FormDef.INSERT_AFTER); } public boolean insertChildBefore(IFormElement child, IFormElement target) { boolean isChildQuestionOrGroupDef = (child instanceof GroupDef || child instanceof QuestionDef); if(!isChildQuestionOrGroupDef){ return false; } //we don't want to insert OptionDefs into this list. return FormDef.insertChildBeforeOrAfter(child, target, this.children, FormDef.INSERT_BEFORE); } }