/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
* <p/>
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.xforms.buendia;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kxml2.kdom.Document;
import org.kxml2.kdom.Element;
import org.kxml2.kdom.Node;
import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptNumeric;
import org.openmrs.Field;
import org.openmrs.Form;
import org.openmrs.FormField;
import org.openmrs.Location;
import org.openmrs.Provider;
import org.openmrs.api.ConceptService;
import org.openmrs.api.context.Context;
import org.openmrs.hl7.HL7Constants;
import org.openmrs.module.xforms.RelativeBuilder;
import org.openmrs.module.xforms.XformConstants;
import org.openmrs.module.xforms.formentry.FormEntryWrapper;
import org.openmrs.module.xforms.formentry.FormSchemaFragment;
import org.openmrs.module.xforms.util.XformsUtil;
import org.openmrs.util.FormConstants;
import org.openmrs.util.FormUtil;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_APPEARANCE;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_BIND;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_CONCEPT_ID;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_CONSTRAINT;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_ID;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_MESSAGE;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_NODESET;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_OPENMRS_CONCEPT;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_REQUIRED;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_TYPE;
import static org.openmrs.module.xforms.XformBuilder.ATTRIBUTE_UUID;
import static org.openmrs.module.xforms.XformBuilder.CONTROL_INPUT;
import static org.openmrs.module.xforms.XformBuilder.CONTROL_REPEAT;
import static org.openmrs.module.xforms.XformBuilder.CONTROL_SELECT;
import static org.openmrs.module.xforms.XformBuilder.CONTROL_SELECT1;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_BASE64BINARY;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_BOOLEAN;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_DATE;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_DATETIME;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_DECIMAL;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_TEXT;
import static org.openmrs.module.xforms.XformBuilder.DATA_TYPE_TIME;
import static org.openmrs.module.xforms.XformBuilder.INSTANCE_ID;
import static org.openmrs.module.xforms.XformBuilder.MODEL_ID;
import static org.openmrs.module.xforms.XformBuilder.NAMESPACE_XFORMS;
import static org.openmrs.module.xforms.XformBuilder.NAMESPACE_XML_INSTANCE;
import static org.openmrs.module.xforms.XformBuilder.NAMESPACE_XML_SCHEMA;
import static org.openmrs.module.xforms.XformBuilder.NODE_BIND;
import static org.openmrs.module.xforms.XformBuilder.NODE_GROUP;
import static org.openmrs.module.xforms.XformBuilder.NODE_HINT;
import static org.openmrs.module.xforms.XformBuilder.NODE_INSTANCE;
import static org.openmrs.module.xforms.XformBuilder.NODE_ITEM;
import static org.openmrs.module.xforms.XformBuilder.NODE_LABEL;
import static org.openmrs.module.xforms.XformBuilder.NODE_MODEL;
import static org.openmrs.module.xforms.XformBuilder.NODE_VALUE;
import static org.openmrs.module.xforms.XformBuilder.NODE_XFORMS;
import static org.openmrs.module.xforms.XformBuilder.PREFIX_XFORMS;
import static org.openmrs.module.xforms.XformBuilder.PREFIX_XML_INSTANCES;
import static org.openmrs.module.xforms.XformBuilder.PREFIX_XML_SCHEMA;
import static org.openmrs.module.xforms.XformBuilder.PREFIX_XML_SCHEMA2;
import static org.openmrs.module.xforms.XformBuilder.XPATH_VALUE_TRUE;
/**
* This is a clone of the Xforms module XformBuilderEx class, allowing us to tinker with the view
* creation code separately from the module itself.
*/
public class BuendiaXformBuilderEx {
private static final int MALE_CONCEPT_ID = 1534;
private static final int FEMALE_CONCEPT_ID = 1535;
private static final String ATTRIBUTE_ROWS = "rows";
private static final Log log = LogFactory.getLog(BuendiaXformBuilderEx.class);
private final Map<String, Element> bindings = new HashMap<>();
private final Map<FormField, String> fieldTokens = new HashMap<>();
private final boolean useConceptIdAsHint;
private final Locale locale;
private final XformCustomizer customizer;
private boolean includesLocations;
private boolean includesProviders;
/**
* Builds an xform for an given an openmrs form. This is the only
* public member in the class; it constructs an instance (to avoid
* nasty statics) and then invokes private methods appropriately.
*/
public static FormData buildXform(Form form, XformCustomizer customizer) throws Exception {
if (customizer == null) {
customizer = new XformCustomizer();
}
return new BuendiaXformBuilderEx(customizer).buildXformImpl(form);
}
private BuendiaXformBuilderEx(XformCustomizer customizer) {
useConceptIdAsHint = "true".equalsIgnoreCase(Context.getAdministrationService()
.getGlobalProperty("xforms.useConceptIdAsHint"));
// TODO(jonskeet): Have a parameter somewhere (URL parameter, Accept-Language header etc)
// which overrides this.
locale = Context.getLocale();
this.customizer = customizer;
}
private static Element appendElement(Node parent, String namespaceURI, String localName) {
Element child = parent.createElement(namespaceURI, localName);
parent.addChild(Element.ELEMENT, child);
return child;
}
/** Adds an element to the given parent, with the specified text as the element value */
private static Element appendTextElement(Node parent, String namespaceURI, String localName,
String text) {
Element child = appendElement(parent, namespaceURI, localName);
child.addChild(Element.TEXT, text);
return child;
}
private static Element addSelectOption(Element parent, String label, String value) {
Element itemNode = appendElement(parent, NAMESPACE_XFORMS, NODE_ITEM);
appendTextElement(itemNode, NAMESPACE_XFORMS, NODE_LABEL, label);
appendTextElement(itemNode, NAMESPACE_XFORMS, NODE_VALUE, value);
return itemNode;
}
private FormData buildXformImpl(Form form) throws Exception {
boolean includeRelationshipNodes = false;
/*
* TODO(jonskeet): Reinstate this code when we're using a version of the
* Xforms modules which has that property. (Or just don't reinstate...)
*
* !"false".equals(Context.getAdministrationService()
* .getGlobalProperty(XformConstants.GLOBAL_PROP_KEY_INCLUDE_PATIENT_RELATIONSHIPS));
*/
//String schemaXml = XformsUtil.getSchema(form);
String templateXml = FormEntryWrapper.getFormTemplate(form);
//Add relationship data node
if (includeRelationshipNodes) {
templateXml = templateXml.replace("</patient>", " <patient_relative>\n "
+ "<patient_relative.person/>\n <patient_relative.relationship/>\n "
+ "</patient_relative>\n </patient>");
}
Document doc = new Document();
doc.setEncoding(XformConstants.DEFAULT_CHARACTER_ENCODING);
Element xformsNode = appendElement(doc, NAMESPACE_XFORMS, NODE_XFORMS);
xformsNode.setPrefix(PREFIX_XFORMS, NAMESPACE_XFORMS);
xformsNode.setPrefix(PREFIX_XML_SCHEMA, NAMESPACE_XML_SCHEMA);
xformsNode.setPrefix(PREFIX_XML_SCHEMA2, NAMESPACE_XML_SCHEMA);
xformsNode.setPrefix(PREFIX_XML_INSTANCES, NAMESPACE_XML_INSTANCE);
xformsNode.setPrefix("jr", "http://openrosa.org/javarosa");
Element modelNode = appendElement(xformsNode, NAMESPACE_XFORMS, NODE_MODEL);
modelNode.setAttribute(null, ATTRIBUTE_ID, MODEL_ID);
// All our UI nodes are appended directly into the xforms node.
// Another alternative would be to create the HTML body node here, and append
// everything under that.
Element bodyNode = xformsNode;
Element instanceNode = appendElement(modelNode, NAMESPACE_XFORMS, NODE_INSTANCE);
instanceNode.setAttribute(null, ATTRIBUTE_ID, INSTANCE_ID);
Element formNode = BuendiaXformBuilder.getDocument(new StringReader(templateXml))
.getRootElement();
formNode.setAttribute(null, ATTRIBUTE_UUID, form.getUuid());
instanceNode.addChild(Element.ELEMENT, formNode);
// (Note for comparison with XformBuilderEx: schema doc code removed here, as it wasn't
// actually used.)
//TODO This block should be replaced with using database field items instead of
// parsing the template document.
Hashtable<String, String> problemList = new Hashtable<>();
Hashtable<String, String> problemListItems = new Hashtable<>();
BuendiaXformBuilder.parseTemplate(modelNode, formNode, formNode, bindings, problemList,
problemListItems, 0);
buildUInodes(form, bodyNode);
//find all conceptId attributes in the document and replace their value with a mapped
// concept
String prefSourceName = Context.getAdministrationService().getGlobalProperty(
XformConstants.GLOBAL_PROP_KEY_PREFERRED_CONCEPT_SOURCE);
//we only use the mappings if the global property is set
if (StringUtils.isNotBlank(prefSourceName)) {
for (int i = 0; i < formNode.getChildCount(); i++) {
Element childElement = formNode.getElement(i);
if (childElement != null) {
for (int j = 0; j < childElement.getChildCount(); j++) {
if (childElement.getElement(j) != null) {
Element grandChildElement = childElement.getElement(j);
String value = grandChildElement.getAttributeValue(null,
ATTRIBUTE_OPENMRS_CONCEPT);
if (StringUtils.isNotBlank(value)) {
BuendiaXformBuilder.addConceptMapAttributes(
grandChildElement, value);
}
}
}
}
}
}
if (includeRelationshipNodes) {
RelativeBuilder.build(modelNode, bodyNode, formNode);
}
String xml = BuendiaXformBuilder.fromDoc2String(doc);
return new FormData(xml, includesProviders, includesLocations);
}
private void buildUInodes(Form form, Element bodyNode) {
TreeMap<Integer, TreeSet<FormField>> formStructure = FormUtil.getFormStructure(form);
buildUInodes(formStructure, 0, bodyNode);
}
private void buildUInodes(TreeMap<Integer, TreeSet<FormField>> formStructure,
Integer sectionId, Element parentUiNode) {
TreeSet<FormField> section = formStructure.get(sectionId);
if (section == null) return;
// Note: FormUtil.getTagList needs a Vector<String>. Urgh.
Vector<String> tagList = new Vector<>();
for (FormField formField : section) {
String sectionName = FormUtil.getXmlToken(formField.getField().getName());
String name = FormUtil.getNewTag(sectionName, tagList);
fieldTokens.put(formField, name);
Field field = formField.getField();
boolean required = formField.isRequired();
Element fieldUiNode;
int fieldTypeId = field.getFieldType().getFieldTypeId();
if (fieldTypeId == FormConstants.FIELD_TYPE_CONCEPT) {
Concept concept = field.getConcept();
ConceptDatatype datatype = concept.getDatatype();
// TODO(jonskeet): Don't rely on names here? (Do we even need problem lists?)
if ((name.contains("problem_added") || name.contains("problem_resolved")) &&
formField.getParent() != null &&
(formField.getParent().getField().getName().contains("PROBLEM LIST"))) {
fieldUiNode = addProblemList(name, concept, formField, parentUiNode);
} else if (name.equals("problem_list")) {
// TODO(jonskeet): Work out what we should do here. There won't be any
// bindings for this.
// The child nodes will be covered by the case above, when we recurse down.
fieldUiNode = parentUiNode;
} else {
switch (datatype.getHl7Abbreviation()) {
case HL7Constants.HL7_BOOLEAN:
fieldUiNode = addUiNode(name, concept, DATA_TYPE_BOOLEAN,
CONTROL_INPUT, required, parentUiNode);
break;
case HL7Constants.HL7_DATE:
fieldUiNode = addUiNode(name, concept, DATA_TYPE_DATE, CONTROL_INPUT,
required, parentUiNode);
break;
case HL7Constants.HL7_DATETIME:
fieldUiNode = addUiNode(name, concept, DATA_TYPE_DATETIME,
CONTROL_INPUT, required, parentUiNode);
break;
case HL7Constants.HL7_TIME:
fieldUiNode = addUiNode(name, concept, DATA_TYPE_TIME, CONTROL_INPUT,
required, parentUiNode);
break;
case HL7Constants.HL7_TEXT:
fieldUiNode = addUiNode(name, concept, DATA_TYPE_TEXT, CONTROL_INPUT,
required, parentUiNode);
break;
case HL7Constants.HL7_NUMERIC:
ConceptNumeric conceptNumeric =
Context.getConceptService().getConceptNumeric(concept
.getConceptId());
if (conceptNumeric == null) {
log.error("Numeric concept could not be fetched for concept " +
concept);
throw new IllegalStateException(
"Numeric concept could not be fetched for concept " + concept);
}
fieldUiNode = addUiNode(name, conceptNumeric, DATA_TYPE_DECIMAL,
CONTROL_INPUT, required,
parentUiNode);
break;
case HL7Constants.HL7_CODED:
case HL7Constants.HL7_CODED_WITH_EXCEPTIONS:
fieldUiNode = addCodedField(name, formField, field, required,
concept, parentUiNode);
break;
case "ED": // This isn't in HL7Constants as far as I can tell.
fieldUiNode = addUiNode(name, concept, DATA_TYPE_BASE64BINARY,
CONTROL_INPUT, required, parentUiNode);
break;
default:
// TODO(jonskeet): Remove this hack when we understand better...
if (field.getName().equals("OBS")) {
fieldUiNode = createGroupNode(formField, parentUiNode);
} else {
// Don't understand this concept
log.warn("Unhandled HL7 abbreviation " + datatype
.getHl7Abbreviation() + " for field "
+ field.getName());
continue; // Skip recursion, go to next field
}
}
}
} else if (fieldTypeId == FormConstants.FIELD_TYPE_SECTION) {
// TODO(jonskeet): Use the description for a hint?
fieldUiNode = appendElement(parentUiNode, NAMESPACE_XFORMS, NODE_GROUP);
Element label = appendElement(fieldUiNode, NAMESPACE_XFORMS, NODE_LABEL);
label.addChild(Node.TEXT, getDisplayName(formField));
String appearanceAttribute = customizer.getAppearanceAttribute(formField);
if (appearanceAttribute != null) {
fieldUiNode.setAttribute(null, ATTRIBUTE_APPEARANCE, appearanceAttribute);
}
} else if (fieldTypeId == FormConstants.FIELD_TYPE_DATABASE) {
fieldUiNode = addDatabaseElementUiNode(name, formField, parentUiNode);
} else {
// Don't understand this field type
log.warn("Unhandled field type " + field.getFieldType().getName() + " for field "
+ field.getName());
continue; // Skip recursion, go to next field
}
// Recurse down to subnodes.
buildUInodes(formStructure, formField.getFormFieldId(), fieldUiNode);
}
}
private Element addUiNode(String token, Concept concept, String dataType, String controlName,
boolean required,
Element bodyNode) {
String bindName = token;
Element controlNode = appendElement(bodyNode, NAMESPACE_XFORMS, controlName);
controlNode.setAttribute(null, ATTRIBUTE_BIND, bindName);
if (DATA_TYPE_TEXT.equals(dataType)) {
Integer rows = customizer.getRows(concept);
if (rows != null) {
controlNode.setAttribute(null, ATTRIBUTE_ROWS, rows.toString());
}
}
Element bindNode = bindings.get(bindName);
if (bindNode == null) {
throw new IllegalArgumentException("No bind node for bindName " + bindName);
}
bindNode.setAttribute(null, ATTRIBUTE_TYPE, dataType);
if (required) {
bindNode.setAttribute(null, ATTRIBUTE_REQUIRED, XPATH_VALUE_TRUE);
}
Element labelNode = appendTextElement(controlNode, NAMESPACE_XFORMS, NODE_LABEL, getLabel
(concept));
addHintNode(labelNode, concept);
if (concept instanceof ConceptNumeric) {
ConceptNumeric numericConcept = (ConceptNumeric) concept;
Double minInclusive = numericConcept.getLowAbsolute();
Double maxInclusive = numericConcept.getHiAbsolute();
if (minInclusive != null) {
String lower = (minInclusive == null ? "" :
FormSchemaFragment.numericToString(minInclusive, numericConcept.isPrecise()));
if (maxInclusive != null) {
String upper = (maxInclusive == null ? "" :
FormSchemaFragment.numericToString(maxInclusive, numericConcept.isPrecise
()));
bindNode.setAttribute(null, ATTRIBUTE_CONSTRAINT, ". >= " + lower + " and . "
+ "<= " + upper);
bindNode.setAttribute(null,
(XformsUtil.isJavaRosaSaveFormat() ? "jr:constraintMsg" :
ATTRIBUTE_MESSAGE),
"value should be between " + lower + " and " + upper + " inclusive");
} else {
bindNode.setAttribute(null, ATTRIBUTE_CONSTRAINT, ". >= " + lower);
bindNode.setAttribute(null,
(XformsUtil.isJavaRosaSaveFormat() ? "jr:constraintMsg" :
ATTRIBUTE_MESSAGE),
"value should be greater than or equal to " + lower);
}
} else if (maxInclusive != null) {
String upper = (maxInclusive == null ? "" :
FormSchemaFragment.numericToString(maxInclusive, numericConcept.isPrecise()));
bindNode.setAttribute(null, ATTRIBUTE_CONSTRAINT, " . <= " + upper);
bindNode.setAttribute(null,
(XformsUtil.isJavaRosaSaveFormat() ? "jr:constraintMsg" : ATTRIBUTE_MESSAGE),
"value should be less than or equal to " + upper);
}
}
return controlNode;
}
private void addCodedUiNodes(boolean multiplSel, Element controlNode,
Collection<ConceptAnswer> answerList,
Concept concept) {
for (ConceptAnswer answer : answerList) {
String conceptName = customizer.getLabel(answer.getAnswerConcept());
String conceptValue;
if (answer.getAnswerConcept().getConceptClass().getConceptClassId().equals
(HL7Constants.CLASS_DRUG)
&& answer.getAnswerDrug() != null) {
conceptName = answer.getAnswerDrug().getName();
if (multiplSel) {
conceptValue = FormUtil.getXmlToken(conceptName);
} else {
conceptValue = FormUtil.conceptToString(answer.getAnswerConcept(), locale) +
"^" + FormUtil.drugToString(answer.getAnswerDrug());
}
} else {
if (multiplSel) {
conceptValue = FormUtil.getXmlToken(conceptName);
} else {
conceptValue = FormUtil.conceptToString(answer.getAnswerConcept(), locale);
}
}
Element itemNode = addSelectOption(controlNode, conceptName, conceptValue);
itemNode.setAttribute(null, ATTRIBUTE_CONCEPT_ID, concept.getConceptId().toString());
}
}
private Element addProblemList(String token, Concept concept, FormField formField, Node
parentUiNode) {
Element groupNode = appendElement(parentUiNode, NAMESPACE_XFORMS, NODE_GROUP);
Element labelNode = appendTextElement(groupNode, NAMESPACE_XFORMS, NODE_LABEL,
customizer.getLabel(formField.getField().getConcept()));
addHintNode(labelNode, concept);
Element repeatControl = appendElement(groupNode, NAMESPACE_XFORMS, CONTROL_REPEAT);
repeatControl.setAttribute(null, ATTRIBUTE_BIND, token);
//add the input node.
Element controlNode = appendElement(repeatControl, NAMESPACE_XFORMS, CONTROL_INPUT);
String nodeset = "problem_list/" + token + "/value";
String id = nodeset.replace('/', '_');
controlNode.setAttribute(null, ATTRIBUTE_BIND, id);
//add the label.
labelNode = appendTextElement(controlNode, NAMESPACE_XFORMS, NODE_LABEL, token + " value");
addHintNode(labelNode, concept);
//create bind node
Element bindNode = appendElement(bindings.get(token).getParent(), NAMESPACE_XFORMS,
NODE_BIND);
bindNode.setAttribute(null, ATTRIBUTE_ID, id);
bindNode.setAttribute(null, ATTRIBUTE_NODESET, "/form/" + nodeset);
bindNode.setAttribute(null, ATTRIBUTE_TYPE, DATA_TYPE_TEXT);
return groupNode;
}
private Element createGroupNode(FormField formField, Element parentUiNode) {
String token = fieldTokens.get(formField);
Element groupNode = appendElement(parentUiNode, NAMESPACE_XFORMS, NODE_GROUP);
Element labelNode = appendTextElement(groupNode, NAMESPACE_XFORMS, NODE_LABEL,
getDisplayName(formField));
addHintNode(labelNode, formField.getField().getConcept());
if (formField.getMaxOccurs() != null && formField.getMaxOccurs() == -1) {
Element repeatControl = appendElement(groupNode, NAMESPACE_XFORMS, CONTROL_REPEAT);
repeatControl.setAttribute(null, ATTRIBUTE_BIND, token);
return repeatControl;
} else {
groupNode.setAttribute(null, ATTRIBUTE_ID, token);
return groupNode;
}
}
private String getDisplayName(FormField formField) {
String name = formField.getDescription();
if (StringUtils.isNotEmpty(name)) return name;
name = formField.getName();
if (StringUtils.isNotEmpty(name)) return name;
name = formField.getField().getDescription();
if (StringUtils.isNotEmpty(name)) return name;
name = formField.getField().getName();
if (StringUtils.isNotEmpty(name)) return name;
throw new IllegalArgumentException("No field name available");
}
private Element addCodedField(String name, FormField formField, Field field,
boolean required, Concept concept, Element parentUiNode) {
if (formField.getMaxOccurs() != null && formField.getMaxOccurs().intValue() == -1) {
return addProblemList(name, concept, formField, parentUiNode);
} else {
List<ConceptAnswer> answers = new ArrayList<>(concept.getAnswers(false));
Collections.sort(answers);
String controlName = field.getSelectMultiple() ? CONTROL_SELECT : CONTROL_SELECT1;
Element controlNode = addUiNode(name, concept, DATA_TYPE_TEXT, controlName, required,
parentUiNode);
addCodedUiNodes(field.getSelectMultiple(), controlNode, answers, concept);
return controlNode;
}
}
private void addHintNode(Element labelNode, Concept concept) {
String hint = null;
if (concept.getDescription() != null) {
hint = concept.getDescription().getDescription();
}
if (useConceptIdAsHint) {
hint = (hint != null ? hint + " [" + concept.getConceptId() + "]" : concept
.getConceptId().toString());
}
if (hint != null) {
appendTextElement(labelNode.getParent(), NAMESPACE_XFORMS, NODE_HINT, hint);
}
}
private String getLabel(Concept concept) {
return customizer.getLabel(concept);
}
///////////////////////////////////////////////////////////////////////////
// Code which was in XformBuilder, but is UI-based
/**
* Builds a UI control node for a table field.
* @return - the created UI control node.
*/
private Element addDatabaseElementUiNode(String bindName, FormField formField, Element
parentUiNode) {
Element controlNode = appendElement(parentUiNode, NAMESPACE_XFORMS, CONTROL_INPUT);
controlNode.setAttribute(null, ATTRIBUTE_BIND, bindName);
// TODO: Set the data type on the bind node? It may already be done.
// Handle encounter provider / location: these are multiple choice questions, and we
// populate
// the options.
Field field = formField.getField();
if ("patient".equals(field.getTableName())) {
if ("gender".equals(field.getAttributeName())) {
controlNode.setName(CONTROL_SELECT1);
populateGenders(controlNode);
} else if ("birthdate".equals(field.getAttributeName())) {
controlNode.setAttribute(null, ATTRIBUTE_APPEARANCE,
"minimal|show_years|show_months");
}
} else if ("encounter".equals(field.getTableName())) {
if ("location_id".equals(field.getAttributeName())) {
controlNode.setName(CONTROL_SELECT1);
populateLocations(controlNode);
} else if ("provider_id".equals(field.getAttributeName())) {
controlNode.setName(CONTROL_SELECT1);
populateProviders(controlNode);
}
}
//create the label
appendTextElement(controlNode, NAMESPACE_XFORMS, NODE_LABEL, getDisplayName(formField));
return controlNode;
}
private void populateGenders(Element controlNode) {
ConceptService conceptService = Context.getConceptService();
addSelectOption(controlNode, getLabel(conceptService.getConcept(MALE_CONCEPT_ID)), "M");
addSelectOption(controlNode, getLabel(conceptService.getConcept(FEMALE_CONCEPT_ID)), "F");
}
/**
* Populates a UI control node with providers.
* @param controlNode - the UI control node.
*/
private void populateProviders(Element controlNode) {
includesProviders = true;
for (Provider provider : Context.getProviderService().getAllProviders()) {
Integer providerId = provider.getId();
addSelectOption(controlNode, customizer.getLabel(provider), providerId.toString());
}
}
/**
* Populates a UI control node with locations.
* @param controlNode - the UI control node.
*/
private void populateLocations(Element controlNode) {
includesLocations = true;
List<Location> locations = customizer.getEncounterLocations();
for (Location loc : locations) {
Integer id = loc.getLocationId();
addSelectOption(controlNode, customizer.getLabel(loc), id.toString());
}
}
}