package org.akaza.openclinica.view.form; import org.akaza.openclinica.bean.submit.ResponseOptionBean; import org.akaza.openclinica.bean.submit.ResponseSetBean; import org.akaza.openclinica.i18n.util.HtmlUtils; import org.akaza.openclinica.i18n.util.ResourceBundleProvider; import org.jdom.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; /** * This class creates various types of input fields such as text inputs and * select lists. It is used by a class such as HorizontalFormBuilder to * dynamically generate HTML tables. */ public class DataEntryInputGenerator implements InputGenerator { private static Map<String, String> NULL_VALUES_INITVERSION = new HashMap<String, String>(); static { NULL_VALUES_INITVERSION.put("NI", ResourceBundleProvider.getTermsBundle().getString("no_information")); NULL_VALUES_INITVERSION.put("NA", ResourceBundleProvider.getTermsBundle().getString("not_applicable")); NULL_VALUES_INITVERSION.put("UNK", ResourceBundleProvider.getTermsBundle().getString("unknown")); NULL_VALUES_INITVERSION.put("NASK", ResourceBundleProvider.getTermsBundle().getString("not_asked")); NULL_VALUES_INITVERSION.put("ASKU", ResourceBundleProvider.getTermsBundle().getString("asked_but_unknown")); NULL_VALUES_INITVERSION.put("NAV", ResourceBundleProvider.getTermsBundle().getString("not_available")); NULL_VALUES_INITVERSION.put("OTH", ResourceBundleProvider.getTermsBundle().getString("other")); NULL_VALUES_INITVERSION.put("PINF", ResourceBundleProvider.getTermsBundle().getString("positive_infinity")); NULL_VALUES_INITVERSION.put("NINF", ResourceBundleProvider.getTermsBundle().getString("negative_infinity")); NULL_VALUES_INITVERSION.put("MSK", ResourceBundleProvider.getTermsBundle().getString("masked")); NULL_VALUES_INITVERSION.put("NP", ResourceBundleProvider.getTermsBundle().getString("not_present")); NULL_VALUES_INITVERSION.put("NPE", ResourceBundleProvider.getTermsBundle().getString("not_performed")); } public static Map<String, String> NULL_VALUES_LONGVERSION = Collections.unmodifiableMap(NULL_VALUES_INITVERSION); private static final Logger logger = LoggerFactory.getLogger("org.akaza.openclinica.view.form.DataEntryInputGenerator"); public static String ONCHANGE_TEXT_INPUT = "this.className='changedField';setImage('DataStatus_top','images/icon_UnsavedData.gif');setImage('DataStatus_bottom','images/icon_UnsavedData.gif');"; // for radio buttons only; to deal with an IE/repetition model bug public static String ONCHANGE_TEXT_INPUT_RADIOS = "if(! detectIEWindows(navigator.userAgent)){this.className='changedField';}setImage('DataStatus_top','images/icon_UnsavedData.gif');setImage('DataStatus_bottom','images/icon_UnsavedData.gif');"; /* * This method generates a text input field for a cell inside an HTML table. * Like the other methods, the user passes in a reference to the Element * object, and the object receives new attributes and content. Then the * method returns the altered Element object. */ public Element createTextInputTag(Element tdCell, Integer itemId, Integer tabNumber, String defaultValue, boolean isDateType, String dbValue, boolean hasSavedData) { // for i18n date formats String pattn = ""; pattn = ResourceBundleProvider.getFormatBundle().getString("date_format_string"); SimpleDateFormat dateFormat = null; if (isDateType) { dateFormat = new SimpleDateFormat(pattn); } Element element = new Element("input"); element.setAttribute("type", "text"); element.setAttribute("tabindex", tabNumber.toString()); element.setAttribute("name", "input" + itemId.toString()); element.setAttribute("onChange", ONCHANGE_TEXT_INPUT); if (dbValue != null && dbValue.length() > 0) { if (isDateType) { try { Date parsedValue = HtmlUtils.parseDateValue(dbValue); if (parsedValue != null) { element.setAttribute("value", dateFormat.format(parsedValue)); } else { element.setAttribute("value", dbValue); } } catch (Exception excep) { element.setAttribute("value", dbValue); } } else { element.setAttribute("value", dbValue); } } else if (!hasSavedData) { if (isDateType) { try { Date parsedValue = HtmlUtils.parseDateValue(defaultValue); if (parsedValue != null) { element.setAttribute("value", dateFormat.format(parsedValue)); } else { element.setAttribute("value", defaultValue); } } catch (Exception excep) { element.setAttribute("value", defaultValue); } } else { element.setAttribute("value", defaultValue); } } else { element.setAttribute("value", ""); } tdCell.addContent(element); Element dateElement; if (isDateType) { dateElement = getDateWidgetForCell(itemId); tdCell.addContent(dateElement); } return tdCell; } /* * This method generates a checkbox tag for a cell inside an HTML table. * Like the other methods, the user passes in a reference to the Element * object, and the object receives new attributes and content. Then method * returns the altered Element object. TODO: this code needs to be * refactored to separate the domain or business rules from the rest of the * parameters (e.g., isHorizontal) */ public Element createCheckboxTag(Element tdCell, Integer itemId, List options, Integer tabNumber, boolean includeLabel, String dbValue, String defaultValue, boolean isHorizontal, boolean hasSavedData) { Element element; String[] arrayOfValues = new String[] {}; // For keeping track of whether a checkbox is the first of a group boolean isFirstInGroup; int count = 0; // Handles lone Strings, or Strings separated by commas if (dbValue != null && dbValue.length() > 0) { arrayOfValues = handleSplitString(dbValue); } else if (!hasSavedData && defaultValue != null && defaultValue.length() > 0) { arrayOfValues = handleSplitString(defaultValue); } for (Object responseOptBean : options) { ++count; isFirstInGroup = count == 1; element = this.initializeInputElement("checkbox", itemId, tabNumber); String value = ((ResponseOptionBean) responseOptBean).getValue(); String forDefVal = ((ResponseOptionBean) responseOptBean).getText(); element.setAttribute("value", value); // It's checked if its value equals the DB value if (dbValue != null && dbValue.length() > 0) { // && value.equalsIgnoreCase(dbValue) for (String string : arrayOfValues) { if (value.equalsIgnoreCase(string)) { element.setAttribute("checked", "checked"); } } } else if (!hasSavedData && defaultValue != null && defaultValue.length() > 0) { // && value.equalsIgnoreCase(dbValue) for (String string : arrayOfValues) { if (forDefVal.equalsIgnoreCase(string) || value.equalsIgnoreCase(string)) { // ((ResponseOptionBean)responseOptBean).setSelected(true); element.setAttribute("checked", "checked"); } } } // add two br tags to before input element if the checkbox element // is not horizontal and // is first in a group of checkboxes if (!isHorizontal && isFirstInGroup) { tdCell.addContent(new Element("br")); } tdCell.addContent(element); if (includeLabel) { tdCell.addContent(((ResponseOptionBean) responseOptBean).getText()); } // if the response_layout property is not "horizontal", then add a // <br> tag if (!isHorizontal) { tdCell.addContent(new Element("br")); } } return tdCell; } /* * This method generates a radio button field for a cell inside an HTML * table. The options parameter contains ResponseOptionBeans, which provides * the "checked" value for the tag, as well as its accompanying text. Like * the other methods, the user passes in a reference to the Element object, * and the object receives new attributes and content. Then the method * returns the altered Element object. */ public Element createRadioButtonTag(Element tdCell, Integer itemId, List options, Integer tabNumber, boolean includeLabel, String dbValue, String defaultValue, boolean isHorizontal, boolean hasSavedData) { Element element; // For keeping track of whether a radio is the first of a group boolean isFirstInGroup; // for the preview, where the item id is 0, we have to generate random // IDs, // so that the input elements will have unique IDs if (itemId == 0) { Random rand = new Random(); itemId = rand.nextInt(10000) + 1; } int count = 0; // Do not use the default value if there is a valid database value boolean hasData = dbValue != null && dbValue.length() > 0; for (Object responseOptBean : options) { ++count; isFirstInGroup = count == 1; element = this.initializeInputElement("radio", itemId, tabNumber); String value = ((ResponseOptionBean) responseOptBean).getValue(); String forDefVal = ((ResponseOptionBean) responseOptBean).getText(); element.setAttribute("value", value); // It's checked if its value equals the DB value if (dbValue != null && dbValue.length() > 0 && value.equalsIgnoreCase(dbValue)) { element.setAttribute("checked", "checked"); } if (!hasSavedData && defaultValue != null && (forDefVal.equalsIgnoreCase(defaultValue) || value.equalsIgnoreCase(defaultValue))) { element.setAttribute("checked", "checked"); } // dealing with IE/repetition model library bug if (isHorizontal) { element.setAttribute("onclick", "if(detectIEWindows(navigator.userAgent)){this.checked=true; unCheckSiblings(this,'horizontal');}"); } else { element.setAttribute("onclick", "if(detectIEWindows(navigator.userAgent)){this.checked=true; unCheckSiblings(this,'vertical');}"); } // add br tag to before input element if the radio element is not // horizontal and // is first in a group of radio buttons if (!isHorizontal && isFirstInGroup) { tdCell.addContent(new Element("br")); } tdCell.addContent(element); tdCell.addContent(" "); if (includeLabel) { tdCell.addContent(((ResponseOptionBean) responseOptBean).getText()); // tdCell.addContent(new Element("br")); } // if the response_layout property is not "horizontal", then add a // <br> tag if (!isHorizontal) { tdCell.addContent(new Element("br")); } } return tdCell; } /* * This method generates a single select tag for a cell inside an HTML * table. Like the other methods, the user passes in a reference to the * Element object, and the object receives new attributes and content. Then * the method returns the altered Element object. The options List contains * ResponseOptionBeans, which represent each option child element of the * select tag. */ public Element createSingleSelectTag(Element tdCell, Integer itemId, List options, Integer tabNumber) { Element element = new Element("select"); element.setAttribute("tabindex", tabNumber.toString()); // A repeating attribute may already have had its "name" attribute set if (element.getAttribute("name") == null) { element.setAttribute("name", "item" + itemId.toString()); } element.setAttribute("onChange", ONCHANGE_TEXT_INPUT); element.setAttribute("class", "formfield"); Element optElement; String optValue; String optText; for (Object responseOptBean : options) { optElement = new Element("option"); optValue = ((ResponseOptionBean) responseOptBean).getValue(); optText = ((ResponseOptionBean) responseOptBean).getText(); optElement.setAttribute("value", optValue); if (((ResponseOptionBean) responseOptBean).isSelected()) { optElement.setAttribute("selected", "selected"); } optElement.addContent(optText); element.addContent(optElement); } tdCell.addContent(element); return tdCell; } // YW 08-14-2007 /** * <p> * Combine default_value with options of single-selected response type. * <p> * If there is default_value, by default, * <ul> * <li>if default_value matches one of options, this option will be * selected. * <li>otherwise, the default_value will be listed at the top of options * </ul> * <p> * If there is no default_value, no modification to options required.<br/> * BWP added parameter databaseValue 09/13/2007 */ public Element createSingleSelectTag(Element tdCell, Integer itemId, List options, Integer tabNumber, String defaultValue, String databaseValue, boolean hasSavedData) { if (databaseValue != null && databaseValue.length() > 0) { tdCell = createSingleSelectTag(tdCell, itemId, options, tabNumber); Element select = tdCell.getChild("select"); if (select != null) { List<Element> optElements = select.getChildren("option"); String optVal = ""; for (Element opts : optElements) { optVal = opts.getAttribute("value").getValue(); if (opts.getAttribute("selected") != null) { opts.removeAttribute("selected"); } if (optVal.equalsIgnoreCase(databaseValue)) { opts.setAttribute("selected", "selected"); } } } return tdCell; } int selectedOption = -1; boolean foundMatch = false; boolean printDefault = false; // check if an option has been selected for (int i = 0; i < options.size(); ++i) { ResponseOptionBean option = (ResponseOptionBean) options.get(i); if (option.isSelected()) { selectedOption = i; break; } } // handle default_value if (defaultValue.length() > 0 && !hasSavedData) { printDefault = true; for (int i = 0; i < options.size(); ++i) { ResponseOptionBean option = (ResponseOptionBean) options.get(i); if (defaultValue.equalsIgnoreCase(option.getText()) || defaultValue.equalsIgnoreCase(option.getValue())) { if (selectedOption == -1) { selectedOption = i; } printDefault = false; foundMatch = true; break; } } } // modify options List<ResponseOptionBean> op = new ArrayList<ResponseOptionBean>(); if (!foundMatch) { if (printDefault) { ResponseOptionBean ro = new ResponseOptionBean(); ro.setText(defaultValue); ro.setValue(""); op.add(ro); op.addAll(options); } } else { ((ResponseOptionBean) options.get(selectedOption)).setSelected(true); } if (op.size() > 0) { tdCell = createSingleSelectTag(tdCell, itemId, op, tabNumber); } else { tdCell = createSingleSelectTag(tdCell, itemId, options, tabNumber); } return tdCell; } public Element createMultiSelectTag(Element tdCell, Integer itemId, List options, Integer tabNumber, String dbValue, String defaultValue, boolean hasSavedData) { // Database values are Strings separated by spaces or commas as // in "meeny moe NASK" or "meeny,moe,NASK" String[] arrayOfValues = new String[] {}; boolean hasDBValue = false; boolean hasDefaultValue = false; List<String> dbValues = new ArrayList<String>(); List<String> defValues = new ArrayList<String>(); if (dbValue != null && dbValue.length() > 0) { dbValues = new ArrayList<String>(); arrayOfValues = handleSplitString(dbValue); for (String subVal : arrayOfValues) { dbValues.add(subVal); } hasDBValue = true; } if (defaultValue != null && defaultValue.length() > 0 && !hasSavedData) { defValues = new ArrayList<String>(); arrayOfValues = handleSplitString(defaultValue); for (String subVal : arrayOfValues) { defValues.add(subVal); } hasDefaultValue = true; } Element element = new Element("select"); element.setAttribute("tabindex", tabNumber.toString()); // A repeating attribute may already have had its "name" attribute set if (element.getAttribute("name") == null) { element.setAttribute("name", "item" + itemId.toString()); } element.setAttribute("multiple", "multiple"); // start out with two visible element.setAttribute("size", "2"); Element optElement; String optValue; String optText; for (Object responseOptBean : options) { optElement = new Element("option"); optValue = ((ResponseOptionBean) responseOptBean).getValue(); optText = ((ResponseOptionBean) responseOptBean).getText(); optElement.setAttribute("value", optValue); if (hasDBValue) { if (dbValues.contains(optValue)) { optElement.setAttribute("selected", "selected"); } } else if (hasDefaultValue) { if (defValues.contains(optValue) || defValues.contains(optText)) { optElement.setAttribute("selected", "selected"); } } else { if (((ResponseOptionBean) responseOptBean).isSelected()) { optElement.setAttribute("selected", "selected"); } } optElement.addContent(optText); element.addContent(optElement); } tdCell.addContent(element); return tdCell; } /* * This method generates a textarea tag for a cell inside an HTML table. * Like the other methods, the user passes in a reference to the Element * object, and the object receives new attributes and content. Then the * method returns the altered Element object. */ public Element createTextareaTag(Element tdCell, Integer itemId, Integer tabNumber, String dbValue, String defaultValue, boolean hasSavedData) { Element element = new Element("textarea"); element.setAttribute("tabindex", tabNumber.toString()); // A repeating attribute may already have had its "name" attribute set if (element.getAttribute("name") == null) { element.setAttribute("name", "item" + itemId.toString()); } element.setAttribute("onChange", ONCHANGE_TEXT_INPUT); element.setAttribute("rows", "5"); if (dbValue != null && dbValue.length() > 0) { element.addContent(dbValue); } else if (defaultValue != null && defaultValue.length() > 0 && !hasSavedData) { element.addContent(defaultValue); } else { // This is necessary to prevent JDOM from creating a textarea tag // that doesn't contain // any text as an empty tag like <textarea/> element.addContent(new NbspaceContent()); } tdCell.addContent(element); return tdCell; } public Element createFileTag(Element tdCell, String dbValue, boolean forPrinting) { if (forPrinting) { Element e = new Element("input"); e.setAttribute("type", "text"); e.setAttribute("value", dbValue); tdCell.addContent(e); } else { Element element1 = new Element("input"); element1.setAttribute("type", "text"); element1.setAttribute("disabled", "disabled"); element1.setAttribute("class", "disabled"); tdCell.addContent(element1); Element element2 = new Element("input"); element2.setAttribute("type", "button"); element2.setAttribute("value", "click to upload file"); tdCell.addContent(element2); } return tdCell; } /** * Calculation response type has been treated as disabled text html input * type, currently - (ywang, 1/10/2007) * * @param tdCell * @param itemId * @param tabNumber * @param defaultValue * @param isDateType * @param dbValue * @param hasSavedData * @return */ public Element createCaculationTag(Element tdCell, Integer itemId, ResponseSetBean responseSet, boolean isDateType, String dbValue, boolean hasSavedData) { // for i18n date formats String pattn = ""; pattn = ResourceBundleProvider.getFormatBundle().getString("date_format_string"); SimpleDateFormat dateFormat = null; if (isDateType) { dateFormat = new SimpleDateFormat(pattn); } Element element = new Element("input"); element.setAttribute("type", "hidden"); element.setAttribute("name", "input" + itemId.toString()); if (dbValue != null && dbValue.length() > 0) { if (isDateType) { try { Date parsedValue = HtmlUtils.parseDateValue(dbValue); if (parsedValue != null) { element.setAttribute("value", dateFormat.format(parsedValue)); } else { element.setAttribute("value", dbValue); } } catch (Exception excep) { element.setAttribute("value", dbValue); } } else { element.setAttribute("value", dbValue); } } else { element.setAttribute("value", ""); } tdCell.addContent(element); Element element2 = new Element("input"); element2.setAttribute("type", "text"); element2.setAttribute("disabled", "disabled"); // Try this, to enable the 'disabled' property with the rep model element2.setAttribute("class", "disabled"); if (dbValue != null && dbValue.length() > 0) { if (isDateType) { try { Date parsedValue = HtmlUtils.parseDateValue(dbValue); if (parsedValue != null) { element2.setAttribute("value", dateFormat.format(parsedValue)); } else { element2.setAttribute("value", dbValue); } } catch (Exception excep) { element2.setAttribute("value", dbValue); } } else { element2.setAttribute("value", dbValue); } } else { element2.setAttribute("value", ""); } tdCell.addContent(element2); return tdCell; } public Element createInstantTag(Element tdCell, Integer itemId, Integer tabNumber, String dbValue, boolean hasSavedData) { Element element = new Element("input"); element.setAttribute("type", "text"); element.setAttribute("tabindex", tabNumber.toString()); element.setAttribute("name", "input" + itemId.toString()); element.setAttribute("onChange", ONCHANGE_TEXT_INPUT); element.setAttribute("value", ""); if (dbValue != null && dbValue.length() > 0) { element.setAttribute("value", dbValue); } else { element.setAttribute("value", ""); } tdCell.addContent(element); return tdCell; } // Initialize an input element with its input type, tab number, and name // attributes // This method returns a JDOM Element object representing an input tag public Element initializeInputElement(String inputType, Integer itemId, Integer tabNumber) { Element element = new Element("input"); element.setAttribute("type", inputType); element.setAttribute("tabindex", tabNumber.toString()); // A repeating attribute may already have had its "name" attribute set if (element.getAttribute("name") == null) { element.setAttribute("name", "item" + itemId.toString()); } if (!inputType.equalsIgnoreCase("radio")) { element.setAttribute("onChange", ONCHANGE_TEXT_INPUT); } else { element.setAttribute("onChange", ONCHANGE_TEXT_INPUT_RADIOS); } return element; } // Create a Date widget for a cell, using the same attribute values and // elements of the exisiting OpenClinica JSPs // Returns an Element representing an "a href" tag containing an img tag public Element getDateWidgetForCell(Integer itemId) { /* * Note: All the previous codes been changed to implement the new * Calender Widget. Changes made by Hamid. */ Element href = new Element("a"); href.setAttribute("href", "#"); // i18n format String pattn = ""; pattn = ResourceBundleProvider.getFormatBundle().getString("date_format_calender"); StringBuilder sbuilder = new StringBuilder("Calendar.setup({inputField : getSib(this.previousSibling), ifFormat :'"); sbuilder.append(pattn).append("',").append("button :'anchor").append(itemId).append("'});"); href.setAttribute("onmouseover", sbuilder.toString()); href.setAttribute("name", "anchor" + itemId); href.setAttribute("id", "anchor" + itemId); Element img = new Element("img"); img.setAttribute("src", "images/bt_Calendar.gif"); img.setAttribute("alt", ResourceBundleProvider.getWordsBundle().getString("show_calendar")); img.setAttribute("title", ResourceBundleProvider.getWordsBundle().getString("show_calendar")); img.setAttribute("border", "0"); href.addContent(img); return href; } // Create a span element with a class attribute referring to the "alert" // class. // This method returns the altered Element object, which contains the span // element as content public Element createRequiredAlert(Element tdCell) { Element alertReq = new Element("span"); alertReq.setAttribute("class", "alert"); alertReq.addContent("*"); tdCell.addContent(alertReq); return tdCell; } // This method creates an "a href" element that contains an img element. // the link element is designed to generate a discrepancy note. The method // uses the same markup as the existing discrepancy-note related JSPs. public Element createDiscrepancyNoteSymbol(Integer numDiscrepancyNotes, Integer tabNumber, Integer itemDataId, Integer itemId, boolean forPrinting) { Element ahref = new Element("a"); ahref.setAttribute("tabindex", tabNumber + 1000 + ""); ahref.setAttribute("href", "#"); StringBuilder clickValue = new StringBuilder(""); // Disable the D Note icon for CRF print views // if (!forPrinting) { // clickValue.append("openDNoteWindow('CreateDiscrepancyNote?id=<c:out // value="); // clickValue.append(itemDataId).append("/>&name=itemData&field=input<c:out // value="); // clickValue.append(itemId).append(" // />&column=value','spanAlert-input<c:out value="); // clickValue.append(itemId).append("/>'); return false;"); // } else { if (forPrinting) { clickValue.append("javascript:void 0"); } ahref.setAttribute("onClick", clickValue.toString()); Element img = new Element("img"); img.setAttribute("name", "flag_input" + itemId); String fileName = numDiscrepancyNotes > 0 ? "icon_Note.gif" : "icon_noNote.gif"; img.setAttribute("src", "images/" + fileName); img.setAttribute("border", "0"); img.setAttribute("alt", ResourceBundleProvider.getWordsBundle().getString("discrepancy_note")); img.setAttribute("title", ResourceBundleProvider.getWordsBundle().getString("discrepancy_note")); ahref.addContent(img); return ahref; } // This method returns a span element containing any error messages // associated with a particular input tag. public Element createInputErrorMessage(Map<Object, ArrayList> messages, Integer itemId) { String key = "input" + itemId; ArrayList _messages = messages.get(key); String errMsg = ""; for (Object msg : _messages) { errMsg = (String) msg; } Element msgSpan = new Element("span"); msgSpan.setAttribute("id", "spanAlert-" + key); msgSpan.setAttribute("class", "alert"); msgSpan.addContent(errMsg); return msgSpan; } public String[] handleSplitString(String param) { if (param == null) { return new String[] {}; } // This method must be able to handle a String such as // "Asian,American Indian or Alaska Native" as two separate values, and // "Elementary School 1-4" as one value String[] values = null; if (param.indexOf(",") != -1) { values = param.split(","); } else { values = new String[] { param }; } // trim array contents for (int i = 0; i < values.length; i++) { values[i] = values[i].trim(); } return values; } }