package org.akaza.openclinica.service.crfdata;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.akaza.openclinica.dao.hibernate.ResponseSetDao;
import org.akaza.openclinica.domain.datamap.CrfVersion;
import org.akaza.openclinica.domain.datamap.ResponseSet;
import org.akaza.openclinica.domain.datamap.ResponseType;
import org.akaza.openclinica.domain.xform.XformItem;
import org.akaza.openclinica.domain.xform.XformUtils;
import org.akaza.openclinica.domain.xform.dto.Group;
import org.akaza.openclinica.domain.xform.dto.Html;
import org.akaza.openclinica.domain.xform.dto.Input;
import org.akaza.openclinica.domain.xform.dto.Item;
import org.akaza.openclinica.domain.xform.dto.ItemSet;
import org.akaza.openclinica.domain.xform.dto.Label;
import org.akaza.openclinica.domain.xform.dto.Select;
import org.akaza.openclinica.domain.xform.dto.Select1;
import org.akaza.openclinica.domain.xform.dto.Upload;
import org.akaza.openclinica.domain.xform.dto.UserControl;
import org.akaza.openclinica.validator.xform.ResponseSetValidator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@Service
public class ResponseSetService {
protected final Logger logger = LoggerFactory.getLogger(getClass().getName());
@Autowired
private ResponseSetDao responseSetDao;
public ResponseSetService() {
}
public ResponseSet getResponseSet(Html html, String submittedXformText, XformItem xformItem, CrfVersion version, ResponseType responseType,
org.akaza.openclinica.domain.datamap.Item item, Errors errors) throws Exception {
ResponseSet existingSet = responseSetDao.findByLabelVersion(xformItem.getItemName(), version.getCrfVersionId());
if (existingSet == null) {
// Create the response set
ResponseSet responseSet = new ResponseSet();
responseSet.setLabel(xformItem.getItemName());
String optionText = getOptionsText(html, submittedXformText, xformItem, responseType);
if (optionText != null) {
responseSet.setOptionsText(optionText);
responseSet.setOptionsValues(getOptionsValues(html, submittedXformText, xformItem, responseType));
responseSet.setResponseType(responseType);
responseSet.setVersionId(version.getCrfVersionId());
responseSetDao.saveOrUpdate(responseSet);
responseSet = responseSetDao.findByLabelVersion(xformItem.getItemName(), version.getCrfVersionId());
}
// Run validation against it
ResponseSetValidator validator = new ResponseSetValidator(responseSetDao, item);
DataBinder dataBinder = new DataBinder(responseSet);
Errors responseSetErrors = dataBinder.getBindingResult();
validator.validate(responseSet, responseSetErrors);
errors.addAllErrors(responseSetErrors);
return responseSet;
} else
return existingSet;
}
private String getOptionsText(Html html, String submittedXformText, XformItem xformItem, ResponseType responseType) throws Exception {
String optionsText = "";
List<Group> groups = html.getBody().getGroup();
for (Group group : groups) {
List<UserControl> controls = null;
if (group.getRepeat() != null)
controls = group.getRepeat().getUsercontrol();
else
controls = group.getUsercontrol();
for (UserControl control : controls) {
if (control.getRef().equals(xformItem.getItemPath())) {
List<Item> items = null;
ItemSet itemSet = null;
if (control instanceof Input) {
return responseType.getName();
} else if (control instanceof Select) {
items = ((Select) control).getItem();
itemSet = ((Select) control).getItemSet();
} else if (control instanceof Select1) {
items = ((Select1) control).getItem();
itemSet = ((Select1) control).getItemSet();
} else if (control instanceof Upload && control.getMediatype().equals("image/*")) {
return responseType.getName();
} else {
logger.debug("Found Unsupported UserControl (" + control.getClass().getName() + ". Returning null text.");
return null;
}
if (itemSet != null)
optionsText = getOptionsTextFromItemSet(submittedXformText, itemSet, html);
else {
for (Item option : items) {
String label = lookupLabel(html, option.getLabel());
label = label.replaceAll(",", "\\\\,");
if (optionsText.isEmpty())
optionsText = label;
else
optionsText += "," + label;
}
}
}
}
}
return optionsText;
}
private String getOptionsTextFromItemSet(String submittedXformText, ItemSet itemSet, Html html) throws Exception {
String optionsText = "";
// Based of ItemSet definition, look up name of element containing each item label.
// Determine if the value of this element is a reference to an itext lookup.
String itemSetLabelRef = itemSet.getLabel().getRef();
boolean hasItextLookup = false;
String itemSetLabelRefName;
if (itemSetLabelRef.startsWith("jr:itext(")) {
hasItextLookup = true;
itemSetLabelRefName = itemSetLabelRef.substring(itemSetLabelRef.indexOf("(") + 1, itemSetLabelRef.lastIndexOf(")"));
} else
itemSetLabelRefName = itemSetLabelRef;
// Use the XPath built into the ItemSet definition to mine the XML Xform for the list of items
// contained in this ItemSet.
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
XPath xPath = XPathFactory.newInstance().newXPath();
Document xml = builder.parse(new ByteArrayInputStream(submittedXformText.getBytes(StandardCharsets.UTF_8)));
String expression = formatItemSetXPath(itemSet.getNodeSet());
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xml, XPathConstants.NODESET);
// Iterate thru the list of items, build the list of Options Text, Performing Itext lookups if required.
for (int i = 0; i < nodeList.getLength(); i++) {
Element item = (Element) nodeList.item(i);
Element itemLabelName = (Element) item.getElementsByTagName(itemSetLabelRefName).item(0);
String label;
if (hasItextLookup)
label = XformUtils.getDefaultTranslation(html, itemLabelName.getTextContent());
else
label = itemLabelName.getTextContent();
label = label.replaceAll(",", "\\\\,");
if (optionsText.equals(""))
optionsText = label;
else
optionsText += "," + label;
}
// Return the completed Options Text
return optionsText;
}
private String getOptionsValues(Html html, String submittedXformText, XformItem xformItem, ResponseType responseType) throws Exception {
String optionsValues = "";
List<Group> groups = html.getBody().getGroup();
for (Group group : groups) {
List<UserControl> controls = null;
if (group.getRepeat() != null)
controls = group.getRepeat().getUsercontrol();
else
controls = group.getUsercontrol();
for (UserControl control : controls) {
if (control.getRef().equals(xformItem.getItemPath())) {
List<Item> items = null;
ItemSet itemSet = null;
if (control instanceof Input) {
return responseType.getName();
} else if (control instanceof Select) {
items = ((Select) control).getItem();
itemSet = ((Select) control).getItemSet();
} else if (control instanceof Select1) {
items = ((Select1) control).getItem();
itemSet = ((Select1) control).getItemSet();
} else if (control instanceof Upload && control.getMediatype().equals("image/*")) {
return responseType.getName();
} else {
logger.debug("Found Unsupported UserControl (" + control.getClass().getName() + ". Returning null text.");
return null;
}
if (itemSet != null)
optionsValues = getOptionsValuesFromItemSet(submittedXformText, itemSet, html);
else {
for (Item option : items) {
String value = option.getValue();
if (optionsValues.isEmpty())
optionsValues = value;
else
optionsValues += "," + value;
}
}
}
}
}
return optionsValues;
}
private String getOptionsValuesFromItemSet(String submittedXformText, ItemSet itemSet, Html html) throws Exception {
String optionsValues = "";
// Based of ItemSet definition, look up name of element containing each item value.
String itemSetValue = itemSet.getValue().getRef();
// Use the XPath built into the ItemSet definition to mine the XML Xform for the list of items
// contained in this ItemSet.
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
XPath xPath = XPathFactory.newInstance().newXPath();
Document xml = builder.parse(new ByteArrayInputStream(submittedXformText.getBytes(StandardCharsets.UTF_8)));
String expression = formatItemSetXPath(itemSet.getNodeSet());
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xml, XPathConstants.NODESET);
// Iterate thru the list of items, build the list of Options Values.
for (int i = 0; i < nodeList.getLength(); i++) {
Element item = (Element) nodeList.item(i);
Element itemValue = (Element) item.getElementsByTagName(itemSetValue).item(0);
String value = itemValue.getTextContent();
if (optionsValues.equals(""))
optionsValues = value;
else
optionsValues += "," + value;
}
// Return the completed Options Values
return optionsValues;
}
private String formatItemSetXPath(String nodeSet) {
String expression = null;
// Replace 'instance' function call with standard XPath syntax
if (nodeSet.trim().startsWith("instance")) {
String instanceID = nodeSet.substring(nodeSet.indexOf("'") + 1, StringUtils.ordinalIndexOf(nodeSet, "'", 2));
expression = "//head/model/instance[@id='" + instanceID + "']" + nodeSet.substring(nodeSet.indexOf(")") + 1);
}
// Remove filter on end
if (expression.trim().endsWith("]")) {
expression = expression.substring(0, expression.lastIndexOf("["));
}
return expression;
}
private String lookupLabel(Html html, Label label) {
if (label != null && label.getLabel() != null && !label.getLabel().equals(""))
return label.getLabel();
else if (label != null && label.getRef() != null && !label.getRef().equals("")) {
String ref = label.getRef();
String itextKey = ref.substring(ref.indexOf("'") + 1, ref.lastIndexOf("'"));
return XformUtils.getDefaultTranslation(html, itextKey);
} else
return "";
}
}