/**
* 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.
*
* 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;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.kxml2.kdom.Element;
import org.openmrs.Patient;
import org.openmrs.Person;
import org.openmrs.Relationship;
import org.openmrs.RelationshipType;
import org.openmrs.api.context.Context;
/**
* Utility for building relationship nodes in xforms.
*
* @since 4.0.3
*/
public class RelationshipBuilder {
public static final String NODE_PATIENT_RELATIONSHIP = "patient_relationship";
public static final String BIND_PATIENT_RELATIONSHIP = "patient." + NODE_PATIENT_RELATIONSHIP;
public static final String BIND_RELATIVE = NODE_PATIENT_RELATIONSHIP + ".relative";
public static final String BIND_RELATIONSHIP_TYPE_ID = NODE_PATIENT_RELATIONSHIP + ".relationship_type_id";
public static final String NODE_RELATIVE = "relative";
public static final String BIND_PATIENT_RELATIONSHIP_TYPE_ID = "patient_relationship.relationship_type_id";
public static final String BIND_PATIENT_RELATIONSHIP_A_OR_B = "patient_relationship.a_or_b";
public static final String BIND_RELATIVE_UUID = "relative.uuid";
public static void build(Element modelElement, Element bodyNode, Element dataNode) {
//Create the parent repeat ui node.
Element groupNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
groupNode.setName(XformBuilder.NODE_GROUP);
//groupNode.setAttribute(null, XformBuilder.ATTRIBUTE_BIND, BIND_RELATIVE);
bodyNode.addChild(Element.ELEMENT, groupNode);
Element labelNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
labelNode.setName(XformBuilder.NODE_LABEL);
labelNode.addChild(Element.TEXT, "RELATIONSHIPS");
groupNode.addChild(Element.ELEMENT, labelNode);
Element hintNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
hintNode.setName(XformBuilder.NODE_HINT);
hintNode.addChild(Element.TEXT, "Relationships that this patient has.");
groupNode.addChild(Element.ELEMENT, hintNode);
Element repeatNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
repeatNode.setName(XformBuilder.CONTROL_REPEAT);
repeatNode.setAttribute(null, XformBuilder.ATTRIBUTE_BIND, BIND_PATIENT_RELATIONSHIP);
groupNode.addChild(Element.ELEMENT, repeatNode);
//Create relative input node.
Element inputNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
inputNode.setName(XformBuilder.CONTROL_INPUT);
inputNode.setAttribute(null, XformBuilder.ATTRIBUTE_BIND, NODE_RELATIVE);
repeatNode.addChild(Element.ELEMENT, inputNode);
//TODO, Fix the input node for the relative to support person search widget
//Create relative label.
labelNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
labelNode.setName(XformBuilder.NODE_LABEL);
labelNode.addChild(Element.TEXT, "RELATIVE");
inputNode.addChild(Element.ELEMENT, labelNode);
//Create relationship type input node.
inputNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
inputNode.setName(XformBuilder.CONTROL_SELECT1);
inputNode.setAttribute(null, XformBuilder.ATTRIBUTE_BIND, BIND_RELATIONSHIP_TYPE_ID);
populateRelationshipTypes(inputNode);
repeatNode.addChild(Element.ELEMENT, inputNode);
//Create relationship label.
labelNode = bodyNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
labelNode.setName(XformBuilder.NODE_LABEL);
labelNode.addChild(Element.TEXT, "RELATIONSHIP");
inputNode.addChild(Element.ELEMENT, labelNode);
//Create bind node for patient relationship.
Element bindNode = modelElement.createElement(XformBuilder.NAMESPACE_XFORMS, null);
bindNode.setName(XformBuilder.NODE_BIND);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_ID, BIND_PATIENT_RELATIONSHIP);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_NODESET, "/form/patient/patient_relationship");
modelElement.addChild(Element.ELEMENT, bindNode);
//Create bind node for patient relationship type.
bindNode = modelElement.createElement(XformBuilder.NAMESPACE_XFORMS, null);
bindNode.setName(XformBuilder.NODE_BIND);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_ID, BIND_RELATIONSHIP_TYPE_ID);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_NODESET,
"/form/patient/patient_relationship/patient_relationship.relationship_type_id");
modelElement.addChild(Element.ELEMENT, bindNode);
//Create bind node for relative.
bindNode = modelElement.createElement(XformBuilder.NAMESPACE_XFORMS, null);
bindNode.setName(XformBuilder.NODE_BIND);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_ID, NODE_RELATIVE);
bindNode.setAttribute(null, XformBuilder.ATTRIBUTE_NODESET, "/form/patient/patient_relationship/relative/relative.uuid");
modelElement.addChild(Element.ELEMENT, bindNode);
}
public static void fillRelationships(Patient patient, Element dataNode) throws Exception {
Element patientRelationShipNode = XformBuilder.getElement(dataNode, NODE_PATIENT_RELATIONSHIP);
if (patientRelationShipNode == null)
return; //For does not need relationships.
Element emptyPatientRelationShipNode = XformBuilder.createCopy(patientRelationShipNode, new ArrayList<String>());
int index = 0;
List<Relationship> relationships = Context.getPersonService().getRelationshipsByPerson(patient);
for (Relationship relationship : relationships) {
if (++index > 1)
patientRelationShipNode = XformBuilder.createCopy(emptyPatientRelationShipNode, new ArrayList<String>());
String relative;
if (getPersonId(patient).equals(relationship.getPersonA().getPersonId())) {
relative = relationship.getPersonB().getPersonName().toString() + " - "
+ getPatientIdentifier(relationship.getPersonB());
} else {
relative = relationship.getPersonA().getPersonName().toString() + " - "
+ getPatientIdentifier(relationship.getPersonA());
}
patientRelationShipNode.setAttribute(null, XformBuilder.ATTRIBUTE_UUID, relationship.getUuid());
XformBuilder.getElement(patientRelationShipNode, "relative.uuid").setAttribute(null, "displayValue", relative);
XformBuilder.setNodeValue(patientRelationShipNode, BIND_RELATIONSHIP_TYPE_ID, relationship.getRelationshipType()
.getRelationshipTypeId()
+ ((relationship.getPersonA().getPersonId().equals(patient.getPersonId())) ? "B" : "A"));
XformBuilder.setNodeValue(patientRelationShipNode, "patient_relationship.exists", "1");
}
}
private static Integer getPersonId(Patient patient) throws Exception {
try {
return patient.getPersonId();
}
catch (NoSuchMethodError ex) {
Method method = patient.getClass().getMethod("getPerson", null);
return ((Person) method.invoke(patient, null)).getPersonId();
}
}
private static String getShortName(Patient patient) {
if (patient.getGivenName() != null)
return patient.getGivenName();
return patient.getFamilyName();
}
private static String getPatientIdentifier(Person person) throws Exception {
Patient patient = Context.getPatientService().getPatient(person.getPersonId());
if (getPersonId(patient) == person.getPersonId())
return patient.getPatientIdentifier().getIdentifier();
return "";
}
private static void populateRelationshipTypes(Element controlNode) {
List<RelationshipType> relationshipTypes = Context.getPersonService().getAllRelationshipTypes(false);
for (RelationshipType type : relationshipTypes) {
Element itemNode;
//The value is of the form relationTypeId:A
itemNode = createRelationTypeOptionNode(type, controlNode, true);
controlNode.addChild(Element.ELEMENT, itemNode);
//For relationships like sibling/sibling just display one option. Otherwise, we need 2
//items for each side of the relationship, one for each side of the relationship so that
//the user can select which side the of the relationship the relative is i.e A Vs B
if (!type.getbIsToA().equalsIgnoreCase(type.getaIsToB())) {
itemNode = createRelationTypeOptionNode(type, controlNode, false);
controlNode.addChild(Element.ELEMENT, itemNode);
}
}
}
/**
* Creates a node for a select option for the specified relation type
*
* @param relationshipType the relationshipType object.
* @param controlNode the select node
* @param isA specifies which side of the relationship we are adding the option for
* @return the Element for the select option
*/
private static Element createRelationTypeOptionNode(RelationshipType relationshipType, Element controlNode, boolean isA) {
Element itemNode = controlNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
itemNode.setName(XformBuilder.NODE_ITEM);
Element node = itemNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
node.setName(XformBuilder.NODE_LABEL);
node.addChild(Element.TEXT, "is the " + ((isA) ? relationshipType.getaIsToB() : relationshipType.getbIsToA()) + " ["
+ relationshipType.getRelationshipTypeId() + "]");
itemNode.addChild(Element.ELEMENT, node);
node = itemNode.createElement(XformBuilder.NAMESPACE_XFORMS, null);
node.setName(XformBuilder.NODE_VALUE);
node.addChild(Element.TEXT, relationshipType.getRelationshipTypeId() + ((isA) ? "A" : "B"));
itemNode.addChild(Element.ELEMENT, node);
return itemNode;
}
}