/**
* 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.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import org.openmrs.Concept;
import org.openmrs.ConceptName;
import org.openmrs.Drug;
import org.openmrs.Form;
import org.openmrs.FormField;
import org.openmrs.hl7.HL7Constants;
/**
* OpenMRS utilities related to forms.
*
* @see org.openmrs.Form
* @see org.openmrs.FormField
* @see org.openmrs.Field
* @see org.openmrs.FieldType
* @see org.openmrs.FieldAnswer
*/
public class FormUtil {
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
/**
* Converts a string into a valid XML token (tag name)
*
* @param s string to convert into XML token
* @return valid XML token based on s
*/
public static String getXmlToken(String s) {
// Converts a string into a valid XML token (tag name)
// No spaces, start with a letter or underscore, not 'xml*'
// if len(s) < 1, return '_blank'
if (s == null || s.length() < 1) {
return "_blank";
}
// xml tokens must start with a letter
String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
// after the leading letter, xml tokens may have
// digits, period, or hyphen
String nameChars = letters + "0123456789.-";
// special characters that should be replaced with valid text
// all other invalid characters will be removed
Map<String, String> swapChars = new HashMap<String, String>();
swapChars.put("!", "bang");
swapChars.put("#", "pound");
swapChars.put("\\*", "star");
swapChars.put("'", "apos");
swapChars.put("\"", "quote");
swapChars.put("%", "percent");
swapChars.put("<", "lt");
swapChars.put(">", "gt");
swapChars.put("=", "eq");
swapChars.put("/", "slash");
swapChars.put("\\\\", "backslash");
// start by cleaning whitespace and converting to lowercase
s = s.replaceAll("^\\s+", "").replaceAll("\\s+$", "").replaceAll("\\s+", "_").toLowerCase();
// swap characters
Set<Entry<String, String>> swaps = swapChars.entrySet();
for (Entry<String, String> entry : swaps) {
if (entry.getValue() != null) {
s = s.replaceAll(entry.getKey(), "_" + entry.getValue() + "_");
} else {
s = s.replaceAll(String.valueOf(entry.getKey()), "");
}
}
// ensure that invalid characters and consecutive underscores are
// removed
StringBuilder token = new StringBuilder("");
boolean underscoreFlag = false;
for (int i = 0; i < s.length(); i++) {
if (nameChars.indexOf(s.charAt(i)) != -1 && (s.charAt(i) != '_' || !underscoreFlag)) {
token.append(s.charAt(i));
underscoreFlag = (s.charAt(i) == '_');
}
}
// remove extraneous underscores before returning token
String tokenStr = token.toString();
tokenStr = tokenStr.replaceAll("_+", "_");
tokenStr = tokenStr.replaceAll("_+$", "");
// make sure token starts with valid letter
if (letters.indexOf(tokenStr.charAt(0)) == -1 || tokenStr.startsWith("xml")) {
tokenStr = "_" + tokenStr;
}
// return token
return tokenStr;
}
/**
* Generates a new, unique tag name for any given string
*
* @param s string to convert into a unique XML tag
* @param tagList java.util.Vector containing all previously created tags. If the tagList is
* null, it will be initialized automatically
* @return unique XML tag name from given string (guaranteed not to duplicate any tag names
* already within <code>tagList</code>)
*/
public static String getNewTag(String s, Vector<String> tagList) {
String token = getXmlToken(s);
if (tagList.contains(token)) {
int i = 1;
while (tagList.contains(token + "_" + i)) {
i++;
}
String tagName = token + "_" + i;
tagList.add(tagName);
return tagName;
} else {
tagList.add(token);
return token;
}
}
/**
* Returns a sorted and structured map of <code>FormField</code>s for the given OpenMRS form.
* The root sections of the schema are stored under a key of zero (i.e.,
* <code>java.lang.Integer.<em>valueOf(0)</em></code>). All other entries represent sequences of
* children stored under the identifier (<code>formField.<em>getFormFieldId()</em></code>) of
* their parent FormField. The form structure is sorted by the natural sorting order of the
* <code>FormField</code>s (as defined by the <em>.equals()</em> and <em>.compareTo()</em>
* methods).
*
* @param form form for which structure is requested
* @return sorted map of <code>FormField</code>s, where the top-level fields are under the key
* zero and all other leaves are stored under their parent <code>FormField</code>'s id.
*/
public static Map<Integer, TreeSet<FormField>> getFormStructure(Form form) {
Map<Integer, TreeSet<FormField>> formStructure = new TreeMap<Integer, TreeSet<FormField>>();
Integer base = Integer.valueOf(0);
formStructure.put(base, new TreeSet<FormField>());
for (FormField formField : form.getFormFields()) {
FormField parent = formField.getParent();
if (parent == null) {
// top-level branches should be added to the base
formStructure.get(base).add(formField);
} else {
// child branches/leaves are added to their parent's branch
if (!formStructure.containsKey(parent.getFormFieldId())) {
formStructure.put(parent.getFormFieldId(), new TreeSet<FormField>());
}
formStructure.get(parent.getFormFieldId()).add(formField);
}
}
return formStructure;
}
public static String dateToString() {
return dateToString(new Date());
}
public static String dateToString(Date date) {
DateFormat dateFormatter = new SimpleDateFormat(DATE_TIME_FORMAT);
String dateString = dateFormatter.format(new Date());
// ISO 8601 requires a colon in time zone offset (Java doesn't
// include the colon, so we need to insert it
return dateString.substring(0, 22) + ":" + dateString.substring(22);
}
/**
* Get a string somewhat unique to this form. Combines the form's id and version and build
*
* @param form Form to get the uri for
* @return String representing this form
*/
public static String getFormUriWithoutExtension(Form form) {
return form.getFormId() + "-" + form.getVersion() + "-" + form.getBuild();
}
/**
* Turn the given concept into a string acceptable to for hl7 and forms
*
* @param concept Concept to convert to a string
* @param locale Locale to use for the concept name
* @return String representation of the given concept
*/
public static String conceptToString(Concept concept, Locale locale) {
ConceptName localizedName = concept.getName(locale, false);
return conceptToString(concept, localizedName);
}
/**
* Turn the given concept/concept-name pair into a string acceptable for hl7 and forms
*
* @param concept Concept to convert to a string
* @param localizedName specific localized concept-name
* @return String representation of the given concept
*/
public static String conceptToString(Concept concept, ConceptName localizedName) {
return concept.getConceptId() + "^" + localizedName.getName() + "^" + HL7Constants.HL7_LOCAL_CONCEPT; // + "^"
// + localizedName.getConceptNameId() + "^" + localizedName.getName() + "^" + FormConstants.HL7_LOCAL_CONCEPT_NAME;
}
/**
* Turn the given drug into a string acceptable for hl7 and forms
*
* @param drug Drug to convert to a string
* @return String representation of the given drug
*/
public static String drugToString(Drug drug) {
return drug.getDrugId() + "^" + drug.getName() + "^" + HL7Constants.HL7_LOCAL_DRUG;
}
}