Package org.openmrs.module.htmlformentry

Source Code of org.openmrs.module.htmlformentry.HtmlFormEntryUtil

package org.openmrs.module.htmlformentry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptName;
import org.openmrs.Drug;
import org.openmrs.Encounter;
import org.openmrs.EncounterType;
import org.openmrs.FormField;
import org.openmrs.Location;
import org.openmrs.LocationTag;
import org.openmrs.Obs;
import org.openmrs.OpenmrsMetadata;
import org.openmrs.Order;
import org.openmrs.Patient;
import org.openmrs.PatientIdentifier;
import org.openmrs.PatientIdentifierType;
import org.openmrs.PatientProgram;
import org.openmrs.PatientState;
import org.openmrs.Person;
import org.openmrs.PersonAddress;
import org.openmrs.PersonAttribute;
import org.openmrs.PersonAttributeType;
import org.openmrs.PersonName;
import org.openmrs.Program;
import org.openmrs.ProgramWorkflow;
import org.openmrs.ProgramWorkflowState;
import org.openmrs.User;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.messagesource.MessageSourceService;
import org.openmrs.module.htmlformentry.FormEntryContext.Mode;
import org.openmrs.module.htmlformentry.action.FormSubmissionControllerAction;
import org.openmrs.module.htmlformentry.action.ObsGroupAction;
import org.openmrs.module.htmlformentry.element.GettingExistingOrder;
import org.openmrs.module.htmlformentry.element.ObsSubmissionElement;
import org.openmrs.obs.ComplexData;
import org.openmrs.propertyeditor.ConceptEditor;
import org.openmrs.propertyeditor.DrugEditor;
import org.openmrs.propertyeditor.EncounterTypeEditor;
import org.openmrs.propertyeditor.LocationEditor;
import org.openmrs.propertyeditor.PatientEditor;
import org.openmrs.propertyeditor.PersonEditor;
import org.openmrs.propertyeditor.UserEditor;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
* HTML Form Entry utility methods
*/
public class HtmlFormEntryUtil {
 
  public static Log log = LogFactory.getLog(HtmlFormEntryUtil.class);
 
  /**
   * Returns the HTML Form Entry service from the Context
   *
   * @return HTML Form Entry service
   */
  public static HtmlFormEntryService getService() {
    return Context.getService(HtmlFormEntryService.class);
  }
 
  /**
   * Fetches a http request parameter from an http request and returns it as a specific type
   *
   * @param request an http servlet request
   * @param name the name of the parameter to retrieve
   * @param clazz the type to convert the parameter to
   * @return parameter, converted to appropriate type
   */
  public static Object getParameterAsType(HttpServletRequest request, String name, Class<?> clazz) {
    if (ComplexData.class.isAssignableFrom(clazz) && request instanceof MultipartHttpServletRequest) {
      return convertToComplexData(request, name);
    } else {
      String val = request.getParameter(name);
      return convertToType(val, clazz);
    }
  }
 
  public static ComplexData convertToComplexData(HttpServletRequest request, String name) {
    MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
    MultipartFile file = mRequest.getFile(name);
    if (file != null && file.getSize() > 0) {
      try {
        return new ComplexData(file.getOriginalFilename(), file.getInputStream());
      }
      catch (IOException e) {
        throw new IllegalArgumentException(e);
      }
    } else {
      return null;
    }
  }
 
  /**
   * Converts a string to specified type
   *
   * @param val the string to convert
   * @param clazz the type to convert to
   * @return an instance of the specified type, with it's value set to val
   */
  public static Object convertToType(String val, Class<?> clazz) {
    if (val == null)
      return null;
    if ("".equals(val) && !String.class.equals(clazz))
      return null;
    if (Location.class.isAssignableFrom(clazz)) {
      LocationEditor ed = new LocationEditor();
      ed.setAsText(val);
      return ed.getValue();
    } else if (User.class.isAssignableFrom(clazz)) {
      UserEditor ed = new UserEditor();
      ed.setAsText(val);
      return ed.getValue();
    } else if (Date.class.isAssignableFrom(clazz)) {
      // all HTML Form Entry dates should be submitted as yyyy-mm-dd
      try {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        df.setLenient(false);
        return df.parse(val);
      }
      catch (ParseException e) {
        throw new IllegalArgumentException(e);
      }
    } else if (Double.class.isAssignableFrom(clazz)) {
      return Double.valueOf(val);
    } else if (Integer.class.isAssignableFrom(clazz)) {
      return Integer.valueOf(val);
    } else if (Concept.class.isAssignableFrom(clazz)) {
      ConceptEditor ed = new ConceptEditor();
      ed.setAsText(val);
      return ed.getValue();
        } else if (Drug.class.isAssignableFrom(clazz)) {
            DrugEditor ed = new DrugEditor();
            ed.setAsText(val);
            return ed.getValue();
        } else if (Patient.class.isAssignableFrom(clazz)) {
      PatientEditor ed = new PatientEditor();
      ed.setAsText(val);
      return ed.getValue();
    } else if (Person.class.isAssignableFrom(clazz)) {
      PersonEditor ed = new PersonEditor();
      ed.setAsText(val);
      return ed.getValue();
    } else if (EncounterType.class.isAssignableFrom(clazz)) {
      EncounterTypeEditor ed = new EncounterTypeEditor();
      ed.setAsText(val);
      return ed.getValue();
    } else {
      return val;
    }
  }
 
  /**
   * Creaets an OpenMRS Obs instance
   *
   * @param formField the form field that specifies the concept associated with the Obs
   * @param value value associated with the Obs
   * @param datetime date/time associated with the Obs (may be null)
   * @param accessionNumber accession number associatd with the Obs (may be null)
   * @return the created Obs instance
   */
 
  public static Obs createObs(FormField formField, Object value, Date datetime, String accessionNumber) {
    Concept concept = formField.getField().getConcept();
    if (concept == null)
      throw new FormEntryException("Can't create an Obs for a formField that doesn't represent a Concept");
    return createObs(concept, value, datetime, accessionNumber);
  }
 
  /**
   * Creates an OpenMRS Obs instance
   *
   * @param concept concept associated with the Obs
   * @param value value associated with the Obs
   * @param datetime date/time associated with the Obs (may be null)
   * @param accessionNumber accession number associated with the Obs (may be null)
   * @return the created Obs instance
   */
  public static Obs createObs(Concept concept, Object value, Date datetime, String accessionNumber) {
    Obs obs = new Obs();
    obs.setConcept(concept);
    ConceptDatatype dt = obs.getConcept().getDatatype();
    if (dt.isNumeric()) {
      obs.setValueNumeric(Double.parseDouble(value.toString()));
    } else if (HtmlFormEntryConstants.COMPLEX_UUID.equals(dt.getUuid())) {
      obs.setComplexData((ComplexData) value);
      obs.setValueComplex(obs.getComplexData().getTitle());
    } else if (dt.isText()) {
      if (value instanceof Location) {
        Location location = (Location) value;
        obs.setValueText(location.getId().toString() + " - " + location.getName());
      } else if (value instanceof Person) {
        Person person = (Person) value;
        obs.setValueText(person.getId().toString() + " - " + person.getPersonName().toString());
      } else {
        obs.setValueText(value.toString());
      }
    } else if (dt.isCoded()) {
            if (value instanceof Drug) {
                obs.setValueDrug((Drug) value);
                obs.setValueCoded(((Drug) value).getConcept());
            } else if (value instanceof ConceptName) {
                obs.setValueCodedName((ConceptName) value);
                obs.setValueCoded(obs.getValueCodedName().getConcept());
            } else if (value instanceof Concept) {
        obs.setValueCoded((Concept) value);
            } else {
        obs.setValueCoded((Concept) convertToType(value.toString(), Concept.class));
            }
    } else if (dt.isBoolean()) {
      if (value != null) {
        try {
          obs.setValueAsString(value.toString());
        }
        catch (ParseException e) {
          throw new IllegalArgumentException("Unable to convert " + value + " to a Boolean Obs value", e);
        }
      }
    } else if (ConceptDatatype.DATE.equals(dt.getHl7Abbreviation())
            || ConceptDatatype.TIME.equals(dt.getHl7Abbreviation())
            || ConceptDatatype.DATETIME.equals(dt.getHl7Abbreviation())) {
      Date date = (Date) value;
      obs.setValueDatetime(date);
    } else if ("ZZ".equals(dt.getHl7Abbreviation())) {
      // don't set a value
    } else {
      throw new IllegalArgumentException("concept datatype not yet implemented: " + dt.getName()
              + " with Hl7 Abbreviation: " + dt.getHl7Abbreviation());
    }
    if (datetime != null)
      obs.setObsDatetime(datetime);
    if (accessionNumber != null)
      obs.setAccessionNumber(accessionNumber);
    return obs;
  }
 
  /**
   * Converts an xml string to a Document object
   *
   * @param xml the xml string to convert
   * @return the resulting Document object
   * @throws Exception
   */
  public static Document stringToDocument(String xml) throws Exception {
    try {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document document = db.parse(new InputSource(new StringReader(xml)));
      return document;
    }
    catch (Exception e) {
      log.error("Error converting String to Document:\n" + xml);
      throw e;
    }
  }
 
  /**
   * Converts a Document object to an xml string
   *
   * @param document the Document instance to convert
   * @return the resulting xml string
   * @throws Exception
   */
  public static String documentToString(Document document) throws Exception {
    //set up a transformer
    Transformer trans = null;
    TransformerFactory transfac = TransformerFactory.newInstance();

    try {
      trans = transfac.newTransformer();
    }
    catch (TransformerException te) {
      System.out.println(HtmlFormEntryConstants.ERROR_TRANSFORMER_1 + te);
    }
    trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, HtmlFormEntryConstants.CONSTANT_YES);
    trans.setOutputProperty(OutputKeys.INDENT, HtmlFormEntryConstants.CONSTANT_YES);
    trans.setOutputProperty(OutputKeys.METHOD, HtmlFormEntryConstants.CONSTANT_XML);
        trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
   
    //create string from xml tree
    StringWriter sw = new StringWriter();
    StreamResult result = new StreamResult(sw);
    DOMSource source = new DOMSource(document);
    try {
      trans.transform(source, result);
    }
    catch (TransformerException te) {
      System.out.println(HtmlFormEntryConstants.ERROR_TRANSFORMER_2 + te);
    }
    String xmlString = sw.toString();
   
    return xmlString;
  }
 
  /**
   * Retrieves a child Node by name
   *
   * @param content the parent Node
   * @param name the name of the child Node
   * @return the child Node with the specified name
   */
  public static Node findChild(Node content, String name) {
    if (content == null)
      return null;
    NodeList children = content.getChildNodes();
    for (int i = 0; i < children.getLength(); ++i) {
      Node node = children.item(i);
      if (name.equals(node.getNodeName()))
        return node;
    }
    return null;
  }

    /**
     * Finds the first descendant of this node with the given tag name
     * @param node
     * @param tagName
     * @return
     */
    public static Node findDescendant(Node node, String tagName) {
        if (node == null)
            return null;
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); ++i) {
            Node child = children.item(i);
            if (tagName.equals(node.getNodeName()))
                return node;
            Node matchingDescendant = findDescendant(child, tagName);
            if (matchingDescendant != null) {
                return matchingDescendant;
            }
        }
        return null;
    }

    /**
   * Returns all the attributes associated with a Node
   *
   * @param node the Node to retrieve attributes from
   * @return a Map containing all the attributes of the Node
   */
  public static Map<String, String> getNodeAttributes(Node node) {
    Map<String, String> ret = new HashMap<String, String>();
    NamedNodeMap atts = node.getAttributes();
    for (int i = 0; i < atts.getLength(); i++) {
      Node attribute = atts.item(i);
      ret.put(attribute.getNodeName(), attribute.getNodeValue());
    }
    return ret;
  }
 
  /**
   * Returns a specific attribute of a Node
   *
   * @param node the Node to retrieve the attribute from
   * @param attributeName the name of the attribute to return
   * @param defaultVal a default value to return if the attribute is not specified for the
   *            selected Node
   * @return
   */
  public static String getNodeAttribute(Node node, String attributeName, String defaultVal) {
    String ret = getNodeAttributes(node).get(attributeName);
    return (ret == null ? defaultVal : ret);
  }

    /**
     * @param node
     * @return the contents of node as a String
     */
    public static String getNodeContentsAsString(Node node) {
        StringWriter sw = new StringWriter();
        try {
            Transformer t = TransformerFactory.newInstance().newTransformer();
            t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            t.setOutputProperty(OutputKeys.INDENT, "yes");
            t.transform(new DOMSource(node), new StreamResult(sw));
        } catch (TransformerException ex) {
            throw new RuntimeException("Error transforming node", ex);
        }
        return sw.toString();
    }

    /**
   * Creates a non-persistent "Fake" Person (used when previewing or validating an HTML Form)
   *
   * @return the "fake" person
   */
  public static Patient getFakePerson() {
    Patient demo = new Patient(12345);
    demo.addName(new PersonName("Demo", "The", "Person"));
    Location l = Context.getLocationService().getAllLocations().iterator().next();
    for (PatientIdentifierType pit : Context.getPatientService().getAllPatientIdentifierTypes()) {
      if (StringUtils.isEmpty(pit.getValidator())) {
        demo.addIdentifier(new PatientIdentifier("Testing" + pit.getName() + "123", pit, l));
      }
    }
    demo.setGender("F");
    demo.setUuid("testing-html-form-entry");
    {
      Calendar cal = Calendar.getInstance();
      cal.add(Calendar.YEAR, -31);
      demo.setBirthdate(cal.getTime());
    }
   
    for (PersonAttributeType type : Context.getPersonService().getAllPersonAttributeTypes()) {
      if (type.getFormat() != null && type.getFormat().equals("java.lang.String")) {
        demo.addAttribute(new PersonAttribute(type, "Test " + type.getName() + " Attribute"));
      }
    }
    PersonAddress addr = new PersonAddress();
    addr.setCityVillage("Rwinkwavu");
    addr.setCountyDistrict("Kayonza District");
    addr.setStateProvince("Eastern Province");
    addr.setCountry("Rwanda");
    demo.addAddress(addr);
    return demo;
  }
 
  /**
   * Combines a Date object that contains only a date component (day, month, year) with a Date
   * object that contains only a time component (hour, minute, second) into a single Date object
   *
   * @param date the Date object that contains date information
   * @param time the Date object that contains time information
   * @return a Date object with the combined date/time
   */
  public static Date combineDateAndTime(Date date, Date time) {
    if (date == null)
      return null;
    Calendar cal = Calendar.getInstance();
   
    cal.setTime(date);
    if (time != null) {
      Calendar temp = Calendar.getInstance();
      temp.setTime(time);
      cal.set(Calendar.HOUR_OF_DAY, temp.get(Calendar.HOUR_OF_DAY));
      cal.set(Calendar.MINUTE, temp.get(Calendar.MINUTE));
      cal.set(Calendar.SECOND, temp.get(Calendar.SECOND));
      cal.set(Calendar.MILLISECOND, temp.get(Calendar.MILLISECOND));
    }
    return cal.getTime();
  }
 
  /**
   * Get the concept by id where the id can either be:
   *   1) an integer id like 5090
   *   2) a mapping type id like "XYZ:HT"
   *   3) a uuid like "a3e12268-74bf-11df-9768-17cfc9833272"
   *   4) the fully qualified name of a Java constant that contains one of above
   *
   * @param id the concept identifier
   * @return the concept if exist, else null
   * @should find a concept by its conceptId
   * @should find a concept by its mapping
   * @should find a concept by its uuid
   * @should find a concept by static constant
   * @should return null otherwise
   * @should find a concept by its mapping with a space in between
   */
  public static Concept getConcept(String id) {

    Concept cpt = null;
   
    if (id != null) {

      id = id.trim();

      // see if this is a parseable int; if so, try looking up concept by id
      try { //handle integer: id
        int conceptId = Integer.parseInt(id);
        cpt = Context.getConceptService().getConcept(conceptId);
       
        if (cpt != null) {
          return cpt;
        }
      }
      catch (Exception ex) {
        //do nothing
      }
     
      // handle  mapping id: xyz:ht
      int index = id.indexOf(":");
      if (index != -1) {
        String mappingCode = id.substring(0, index).trim();
        String conceptCode = id.substring(index + 1, id.length()).trim();
        cpt = Context.getConceptService().getConceptByMapping(conceptCode, mappingCode);
       
        if (cpt != null) {
          return cpt;
        }
      }
     
      // handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272", if the id matches a uuid format
      if (isValidUuidFormat(id)) {
        cpt = Context.getConceptService().getConceptByUuid(id);
      }
      // finally, if input contains at least one period handle recursively as a code constant
      else if (id.contains(".")) {
        return getConcept(evaluateStaticConstant(id));
      }
    }
   
    return cpt;
  }
 
  /**
   * Gets a concept by id, mapping, or uuid. (See #getConcept(String) for precise details.) If no
   * concept is found, throws an IllegalArgumentException with the given message.
   *
   * @param id
   * @param errorMessageIfNotFound
   * @return
   * @throws IllegalArgumentException
   */
  public static Concept getConcept(String id, String errorMessageIfNotFound) throws IllegalArgumentException {

    Concept c = null;
    try {
      c = getConcept(id);
    }
    catch (Exception ex) {
      throw new IllegalArgumentException(errorMessageIfNotFound, ex);
    }
    if (c == null)
      throw new IllegalArgumentException(errorMessageIfNotFound);
    return c;
  }
 
  /**
     * This method doesn't support "SessionAttribute:", but is otherwise like the similarly-named method.
   * @see #getLocation(String, FormEntryContext)
   */
  public static Location getLocation(String id) {
        return getLocation(id, null);
    }

    /**
     * Get the location by:
     * <ol>
     *     <li>an integer id like 5090</li>
     *     <li>a uuid like "a3e12268-74bf-11df-9768-17cfc9833272"</li>
     *     <li>a name like "Boston"</li>
     *     <li>an id/name pair like "501 - Boston" (this format is used when saving a location on a obs as a value text)</li>
     *     <li>"GlobalProperty:property.name"</li>
     *     <li>"UserProperty:propertyName"</li>
     *     <li>"SessionAttribute:attributeName"</li>
     * </ol>
     *
     *
     * @param id
     * @param context
     * @return the location if exist, else null
     * @should find a location by its locationId
     * @should find a location by name
     * @should find a location by its uuid
     * @should find a location by global property
     * @should find a location by user property
     * @should find a location by session attribute
     * @should not fail if trying to find a location by session attribute and we have no session
     * @should return null otherwise
     */
  public static Location getLocation(String id, FormEntryContext context) {

    Location location = null;
   
    if (id != null) {

            id = id.trim();

            // handle "SystemDefault" setting
            if(id.equals(HtmlFormEntryConstants.SYSTEM_DEFAULT)) {
                location= Context.getLocationService().getDefaultLocation();
                if (location != null) {
                    return location;

                }
            }
      // handle GlobalProperty:property.name
      if (id.startsWith("GlobalProperty:")) {
        String gpName = id.substring("GlobalProperty:".length());
        String gpValue = Context.getAdministrationService().getGlobalProperty(gpName);
        if (StringUtils.isNotEmpty(gpValue)) {
          return getLocation(gpValue, context);
        }
      }
     
      // handle UserProperty:propName
      if (id.startsWith("UserProperty:")) {
        String upName = id.substring("UserProperty:".length());
        String upValue = Context.getAuthenticatedUser().getUserProperty(upName);
        if (StringUtils.isNotEmpty(upValue)) {
          return getLocation(upValue, context);
        }
      }
            // handle SessionAttribute:attributeName
            if (id.startsWith("SessionAttribute:")) {
                if (context.getHttpSession() == null) {
                    // if we don't have access to a session, e.g. when validating a form, we can't do anything
                    return null;
                }
                String saName = id.substring("SessionAttribute:".length());
                Object saValue = context.getHttpSession().getAttribute(saName);
                if (saValue == null) {
                    return null;
                } else if (saValue instanceof Location) {
                    return (Location) saValue;
                } else if (saValue instanceof String) {
                    return getLocation((String) saValue, context);
                } else {
                    return getLocation(saValue.toString(), context);
                }
            }

            // see if this is parseable int; if so, try looking up by id
      try { //handle integer: id
        int locationId = Integer.parseInt(id);
        location = Context.getLocationService().getLocation(locationId);
       
        if (location != null) {
          return location;
        }
      }
      catch (Exception ex) {
        //do nothing
      }
     
      // handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272" if id matches a uuid format
      if (isValidUuidFormat(id)) {
        location = Context.getLocationService().getLocationByUuid(id);
       
        if (location != null) {
          return location;
        }
      }
     
      // if it's neither a uuid or id, try location name
      location = Context.getLocationService().getLocation(id);
     
      if (location != null) {
        return location;
      }
     
      // try the "101 - Cange" case
      if (id.contains(" ")) {
        String[] values = id.split(" ");
        try {
          int locationId = Integer.parseInt(values[0]);
          location = Context.getLocationService().getLocation(locationId);
         
          if (location != null) {
            return location;
          }
        }
        catch (Exception ex) {
          //do nothing
        }
      }
    }
   
    // no match found, so return null
    return null;
  }
 
  /***
   * Get the program by: 1)an integer id like 5090 or 2) uuid like
   * "a3e12268-74bf-11df-9768-17cfc9833272" or 3) name of *associated concept* (not name of
   * program), like "MDR-TB Program"
   *
   * @param id
   * @return the program if exist, else null
   * @should find a program by its id
   * @should find a program by name of associated concept
   * @should find a program by its uuid
   * @should return null otherwise
   */
  public static Program getProgram(String id) {
   
    Program program = null;
   
    if (id != null) {

            id = id.trim();

      // see if this is parseable int; if so, try looking up by id
      try {//handle integer: id
        int programId = Integer.parseInt(id);
        program = Context.getProgramWorkflowService().getProgram(programId);
       
        if (program != null) {
          return program;
        }
      }
      catch (Exception ex) {
        //do nothing
      }
     
      //handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272", if id matches uuid format
      if (isValidUuidFormat(id)) {
        program = Context.getProgramWorkflowService().getProgramByUuid(id);
       
        if (program != null) {
          return program;
        }
      } else {
        // if it's neither a uuid or id, try program name
        // (note that this API method actually checks based on name of the associated concept, not the name of the program itself)
        program = Context.getProgramWorkflowService().getProgramByName(id);
      }
     
    }
    return program;
  }
 
  /***
   * Get the person by: 1)an integer id like 5090 or 2) uuid like
   * "a3e12268-74bf-11df-9768-17cfc9833272" or 3) a username like "mgoodrich" or 4) an id/name
   * pair like "5090 - Bob Jones" (this format is used when saving a person on a obs as a value
   * text)
   *
   * @param id
   * @return the person if exist, else null
   * @should find a person by its id
   * @should find a person by its uuid
   * @should find a person by username of corresponding user
   * @should return null otherwise
   */
  public static Person getPerson(String id) {
   
    Person person = null;
   
    if (id != null) {

            id = id.trim();

      // see if this is parseable int; if so, try looking up by id
      try { //handle integer: id
        int personId = Integer.parseInt(id);
        person = Context.getPersonService().getPerson(personId);
       
        if (person != null) {
          return person;
        }
      }
      catch (Exception ex) {
        //do nothing
      }
     
      // handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272", if id matches uuid format
      if (isValidUuidFormat(id)) {
        person = Context.getPersonService().getPersonByUuid(id);
       
        if (person != null) {
          return person;
        }
      }
     
      // handle username
      User personByUsername = Context.getUserService().getUserByUsername(id);
      if (personByUsername != null) {
        return personByUsername.getPerson();
      }
     
      // try the "5090 - Bob Jones" case
      if (id.contains(" ")) {
        String[] values = id.split(" ");
        try {
          int personId = Integer.parseInt(values[0]);
          person = Context.getPersonService().getPerson(personId);
         
          if (person != null) {
            return person;
          }
        }
        catch (Exception ex) {
          //do nothing
        }
      }
    }
   
    // no match found, so return null
    return null;
  }
 
  /***
   * Get the patient identifier type by: 1)an integer id like 5090 or 2) uuid like
   * "a3e12268-74bf-11df-9768-17cfc9833272" or 3) a name like "Temporary Identifier"
   *
   * @param id
   * @return the identifier type if exist, else null
   * @should find an identifier type by its id
   * @should find an identifier type by its uuid
   * @should find an identifier type by its name
   * @should return null otherwise
   */
  public static PatientIdentifierType getPatientIdentifierType(String id) {
    PatientIdentifierType identifierType = null;
   
    if (id != null) {

            id = id.trim();

      // see if this is parseable int; if so, try looking up by id
      try { //handle integer: id
        int identifierTypeId = Integer.parseInt(id);
        identifierType = Context.getPatientService().getPatientIdentifierType(identifierTypeId);
       
        if (identifierType != null) {
          return identifierType;
        }
      }
      catch (Exception ex) {
        //do nothing
      }
     
      //handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272", if id matches uuid format
      if (isValidUuidFormat(id)) {
        identifierType = Context.getPatientService().getPatientIdentifierTypeByUuid(id);
       
        if (identifierType != null) {
          return identifierType;
        }
      }
      // handle name
      else {
        // if it's neither a uuid or id, try identifier type name
        identifierType = Context.getPatientService().getPatientIdentifierTypeByName(id);
      }
    }
    return identifierType;
  }
 
  /**
   * Looks up a {@link ProgramWorkflow} by id, uuid or by concept map of the underlying concept
   */
  @SuppressWarnings("deprecation")
  public static ProgramWorkflow getWorkflow(String identifier) {
    ProgramWorkflow workflow = null;
   
    if (identifier != null) {

            identifier = identifier.trim();

      // first try to fetch by id
      try {
        Integer id = Integer.valueOf(identifier);
        workflow = Context.getProgramWorkflowService().getWorkflow(id);
       
        if (workflow != null) {
          return workflow;
        }
      }
      catch (NumberFormatException e) {}
     
      // if not, try to fetch by uuid
      if (isValidUuidFormat(identifier)) {
        workflow = Context.getProgramWorkflowService().getWorkflowByUuid(identifier);
       
        if (workflow != null) {
          return workflow;
        }
      }
     
      // finally, try to fetch by concept map
      // handle  mapping id: xyz:ht
      int index = identifier.indexOf(":");
      if (index != -1) {
        Concept concept = getConcept(identifier);
       
        // iterate through workflows until we see if we find a match
        if (concept != null) {
          for (Program program : Context.getProgramWorkflowService().getAllPrograms(false)) {
            for (ProgramWorkflow w : program.getAllWorkflows()) {
              if (w.getConcept().equals(concept)) {
                return w;
              }
            }
          }
        }
      }
    }
   
    return workflow;
  }
 
  /**
   * Looks up a {@link ProgramWorkflowState} from the specified program by programWorkflowStateId,
   * uuid, or by a concept map to the the underlying concept (Note that if there are multiple
   * states associated with the same concept in the program, this method will return an arbitrary
   * one if fetched by concept mapping)
   *
   * @param identifier the programWorkflowStateId, uuid or the concept name to match against
   * @param program
   * @return
   * @should return the state with the matching id
   * @should return the state with the matching uuid
   * @should return the state associated with a concept that matches the passed concept map
   */
  public static ProgramWorkflowState getState(String identifier, Program program) {
    if (identifier == null) {
      return null;
    }
   
    // first try to fetch by id or uuid
    ProgramWorkflowState state = getState(identifier);
   
    if (state != null) {
      return state;
    }
    // if we didn't find a match, see if this is a concept mapping
    else {
      identifier = identifier.trim();

            int index = identifier.indexOf(":");
      if (index != -1) {
        Concept concept = getConcept(identifier);
        if (concept != null) {
          for (ProgramWorkflow workflow : program.getAllWorkflows()) {
            for (ProgramWorkflowState s : workflow.getStates(false)) {
              if (s.getConcept().equals(concept)) {
                return s;
              }
            }
          }
        }
      }
    }
    return null;
  }
 
  /**
   * Looks up a {@link ProgramWorkflowState} from the specified workflow by
   * programWorkflowStateId, uuid, or by a concept map to the the underlying concept (Note that if
   * there are multiple states associated with the same concept in the workflow, this method will
   * return an arbitrary one if fetched by concept mapping)
   *
   * @param identifier the programWorkflowStateId, uuid or the concept name to match against
   * @param workflow
   * @return
   * @should return the state with the matching id
   * @should return the state with the matching uuid
   * @should return the state associated with a concept that matches the passed concept map
   */
  public static ProgramWorkflowState getState(String identifier, ProgramWorkflow workflow) {
    if (identifier == null) {
      return null;
    }
   
    // first try to fetch by id or uuid
    ProgramWorkflowState state = getState(identifier);
   
    if (state != null && state.getProgramWorkflow().equals(workflow)) {
      return state;
    }
   
    // if we didn't find a match, see if this is a concept mapping
    else {
            identifier = identifier.trim();

      int index = identifier.indexOf(":");
      if (index != -1) {
        Concept concept = getConcept(identifier);
        if (concept != null) {
          for (ProgramWorkflowState s : workflow.getStates(false)) {
            if (s.getConcept().equals(concept)) {
              return s;
            }
          }
        }
      }
    }
    return null;
  }

    public static List<Location> getLocationsByTags(String attributeName, Map<String, String> parameters){
        List<Location> locations = null;

        String locationTags = parameters.get(attributeName);

        if ( locationTags != null) {
            List<LocationTag> tags = new ArrayList<LocationTag>();
            String[] temp = locationTags.split(",");
            for (String s : temp) {
                LocationTag tag = getLocationTag(s);
                if (tag == null) {
                    throw new RuntimeException("Cannot find tag: " + tag);
                }
                tags.add(tag);
            }
            locations =  new ArrayList<Location>();
            locations.addAll(Context.getLocationService().getLocationsHavingAnyTag(tags));
        }
        return locations;
    }
    /**
     * Fetches a location tag by name or id
     * (Will add support for uuid once we stop supporting OpenMRS 1.6, which doesn't a uuid on location tag)
     *
     * @param identifier
     * @return
     */
    public static LocationTag getLocationTag(String identifier) {

        LocationTag tag = null;

        if (identifier != null) {

            identifier = identifier.trim();

            // first try to fetch by id
            try {
                Integer id = Integer.valueOf(identifier);
                tag = Context.getLocationService().getLocationTag(id);

                if (tag != null) {
                    return tag;
                }
            }
            catch (NumberFormatException e) {}

            // if not, try to fetch by name
            tag = Context.getLocationService().getLocationTagByName(identifier);

            if (tag != null) {
                return tag;
            }

           // TODO add ability to fetch by uuid once we are no longer worried about being compatible with OpenMRS 1.6 (since getLocationTagByUuid is not available in 1.6)

        }

        return null;
    }

  /**
   * Looks up a {@link ProgramWorkflowState} from the specified workflow by
   * programWorkflowStateId, or uuid
   *
   * @param identifier the programWorkflowStateId or uuid to match against
   * @param
   * @return
   * @should return the state with the matching id
   * @should return the state with the matching uuid
   */
  @SuppressWarnings("deprecation")
  public static ProgramWorkflowState getState(String identifier) {
    ProgramWorkflowState state = null;
   
    if (identifier != null) {
      try {

        identifier = identifier.trim();

        Integer id = Integer.valueOf(identifier);
        state = Context.getProgramWorkflowService().getState(id);
       
        if (state != null) {
          return state;
        }
      }
      catch (NumberFormatException e) {}
     
      if (isValidUuidFormat(identifier)) {
        state = Context.getProgramWorkflowService().getStateByUuid(identifier);
       
        if (state != null) {
          return state;
        }
      }
    }
    return null;
  }

  /***
   * Determines if the passed string is in valid uuid format By OpenMRS standards, a uuid must be
   * 36 characters in length and not contain whitespace, but we do not enforce that a uuid be in
   * the "canonical" form, with alphanumerics seperated by dashes, since the MVP dictionary does
   * not use this format (We also are being slightly lenient and accepting uuids that are 37 or 38
   * characters in length, since the uuid data field is 38 characters long)
   */
  public static boolean isValidUuidFormat(String uuid) {
    if (uuid.length() < 36 || uuid.length() > 38 || uuid.contains(" ") || uuid.contains(".")) {
      return false;
    }
   
    return true;
  }

  /**
   * Evaluates the specified Java constant using reflection
   * @param fqn the fully qualified name of the constant
   * @return the constant value
   */
  protected static String evaluateStaticConstant(String fqn) {
    int lastPeriod = fqn.lastIndexOf(".");
    String clazzName = fqn.substring(0, lastPeriod);
    String constantName = fqn.substring(lastPeriod + 1);

    try {
      Class<?> clazz = Context.loadClass(clazzName);
      Field constantField = clazz.getField(constantName);
      Object val = constantField.get(null);
      return val != null ? String.valueOf(val) : null;
    }
    catch (Exception ex) {
      throw new IllegalArgumentException("Unable to evaluate " + fqn, ex);
    }
  }

  /**
   * Gets all patient identifier types
   * @return the patient identifier types
   */
  public static List<PatientIdentifierType> getPatientIdentifierTypes() {
    return Context.getPatientService().getAllPatientIdentifierTypes();
  }
 
  /**
   * Utility method to void an encounter. If the voidEncounterByHtmlFormSchema global property has
   * been set to true, OR if the Html Form Flowsheet module has been started AND the
   * voidEncounterByHtmlFormSchema global property has not been explicitly set to false, void the
   * encounter via the special voidEncounterByHtmlFormSchema algorithm. Otherwise simply void the
   * encounter normally. This test is done because when using the Html Form Flowsheet module we
   * only want to void observations associated with the current form, not the entire encounter
   */
  public static void voidEncounter(Encounter e, HtmlForm htmlform, String voidReason) throws Exception {
    if (voidReason == null) {
      voidReason = "htmlformentry";
    }
   
    if (HtmlFormEntryGlobalProperties.VOID_ENCOUNTER_BY_HTML_FORM_SCHEMA() != null) {
     
      if (HtmlFormEntryGlobalProperties.VOID_ENCOUNTER_BY_HTML_FORM_SCHEMA() == true) {
        voidEncounterByHtmlFormSchema(e, htmlform, voidReason);
      } else {
        Context.getEncounterService().voidEncounter(e, voidReason);
      }
     
    } else if (HtmlFormEntryGlobalProperties.HTML_FORM_FLOWSHEET_STARTED() != null
            && HtmlFormEntryGlobalProperties.HTML_FORM_FLOWSHEET_STARTED() == true) {
      voidEncounterByHtmlFormSchema(e, htmlform, voidReason);
    } else {
      Context.getEncounterService().voidEncounter(e, voidReason);
    }
  }
 
  /**
   * Utility method that sets all matched obs and orders to voided, and voids encounter if all obs
   * and orders in encounter are voided. Does not call save, just updates the voided fields on all
   * objects in encounter Uses a 'dummy' FormEntrySession to use htmlformentry schema matching
   * mechanism, and then examines the leftover Obs, Orders from the FormEntrySession constructor
   */
  public static void voidEncounterByHtmlFormSchema(Encounter e, HtmlForm htmlform, String voidReason) throws Exception {
    if (e != null && htmlform != null) {
      if (voidReason == null)
        voidReason = "htmlformentry";
      boolean shouldVoidEncounter = true;
      Map<Obs, Obs> replacementObs = new HashMap<Obs, Obs>();//new, then source
      Map<Order, Order> replacementOrders = new HashMap<Order, Order>();//new, then source
      Encounter eTmp = returnEncounterCopy(e, replacementObs, replacementOrders);
      FormEntrySession session = new FormEntrySession(eTmp.getPatient(), eTmp, Mode.VIEW, htmlform, null); // session gets a null HttpSession
            session.getHtmlToDisplay();
      List<FormSubmissionControllerAction> actions = session.getSubmissionController().getActions();
      Set<Obs> matchedObs = new HashSet<Obs>();
      Set<Order> matchedOrders = new HashSet<Order>();
      for (FormSubmissionControllerAction lfca : actions) {
        if (lfca instanceof ObsSubmissionElement) {
          ObsSubmissionElement ose = (ObsSubmissionElement) lfca;
          if (ose.getExistingObs() != null) {
            matchedObs.add(ose.getExistingObs());
          }
        }
        if (lfca instanceof ObsGroupAction) {
          ObsGroupAction oga = (ObsGroupAction) lfca;
          if (oga.getExistingGroup() != null) {
            matchedObs.add(oga.getExistingGroup());
          }
        }
        if (lfca instanceof GettingExistingOrder) {
          GettingExistingOrder dse = (GettingExistingOrder) lfca;
          if (dse.getExistingOrder() != null) {
            matchedOrders.add(dse.getExistingOrder());
          }
        }
      }
     
      for (Obs o : e.getAllObs(false)) { //ignore voided obs
        boolean matched = false;
        for (Obs oMatched : matchedObs) {
          if (replacementObs.get(oMatched) != null && replacementObs.get(oMatched).equals(o)) {
            o.setVoided(true);
            o.setVoidedBy(Context.getAuthenticatedUser());
            o.setVoidReason(voidReason);
            o.setDateVoided(new Date());
            matched = true;
            break;
          }
        }
        if (!matched)
          shouldVoidEncounter = false;
      }
     
      for (Order o : e.getOrders()) {
        if (!o.isVoided()) { //ignore voided orders
          boolean matched = false;
          for (Order oMatched : matchedOrders) {
            //Order.equals only checks Id value
            if (replacementOrders.get(oMatched) != null && replacementOrders.get(oMatched).equals(o)
                    && !o.isVoided()) {
              o.setVoided(true);
              o.setVoidedBy(Context.getAuthenticatedUser());
              o.setVoidReason(voidReason);
              o.setDateVoided(new Date());
              matched = true;
              break;
            }
          }
          if (!matched)
            shouldVoidEncounter = false;
        }
      }
     
      if (shouldVoidEncounter) {
        e.setVoided(true);
        e.setVoidedBy(Context.getAuthenticatedUser());
        e.setVoidReason(voidReason);
        e.setDateVoided(new Date());
      }
      eTmp = null;
    }
  }
 
  /**
   * Method that returns a copy of an Encounter. Includes copies of all Obs tree structures and
   * Orders.
   *
   * @param source
   * @param replacementObs
   * @param replacementOrders
   * @return
   * @throws Exception
   */
  private static Encounter returnEncounterCopy(Encounter source, Map<Obs, Obs> replacementObs,
                                               Map<Order, Order> replacementOrders) throws Exception {
    if (source != null) {
      Encounter encNew = (Encounter) returnCopy(source);
     
      //note: we can do this because we're not going to manipulate anything about these obs or orders, and this copy won't be persisted...
     
      Set<Obs> newObs = new HashSet<Obs>();
      for (Obs o : source.getAllObs(true)) {
        Obs oNew = returnObsCopy(o, replacementObs);
        newObs.add(oNew);
      }
      encNew.setObs(newObs);
     
      Set<Order> newOrders = new HashSet<Order>();
      for (Order o : source.getOrders()) {
        Order oNew = (Order) returnOrderCopy(o, replacementOrders);
        newOrders.add(oNew);
      }
      encNew.setOrders(newOrders);
      return encNew;
    }
    return null;
  }
 
  /**
   * Returns a copy of an Obs. Recurses through GroupMembers to return copies of those also, so
   * the whole Obs tree is a copy.
   *
   * @param obsToCopy
   * @param replacements
   * @return
   * @throws Exception
   */
  private static Obs returnObsCopy(Obs obsToCopy, Map<Obs, Obs> replacements) throws Exception {
    Obs newObs = (Obs) returnCopy(obsToCopy);
   
    if (obsToCopy.isObsGrouping()) {
      newObs.setGroupMembers(null);
      for (Obs oinner : obsToCopy.getGroupMembers()) {
        Obs oinnerNew = returnObsCopy(oinner, replacements);
        newObs.addGroupMember(oinnerNew);
      }
    }
   
    replacements.put(newObs, obsToCopy);
    return newObs;
  }
 
  /**
   * Utility to return a copy of an Order. Uses reflection so that this code will support any
   * subclassing of Order, such as DrugOrder
   *
   * @param source
   * @param replacementOrders
   * @return A copy of an Order
   * @throws Exception
   */
  private static Object returnOrderCopy(Order source, Map<Order, Order> replacementOrders) throws Exception {
    Object ret = returnCopy(source);
    replacementOrders.put((Order) ret, (Order) source);
    return ret;
  }
 
  /**
   * Utility to return a copy of an Object. Copies all properties that are referencese by getters
   * and setters and *are not* collection
   *
   * @param source
   * @return A copy of an object
   * @throws Exception
   */
  private static Object returnCopy(Object source) throws Exception {
    Class<? extends Object> clazz = source.getClass();
    Object ret = clazz.newInstance();
    Set<String> fieldNames = new HashSet<String>();
    List<Field> fields = new ArrayList<Field>();
    addSuperclassFields(fields, clazz);
    for (Field f : fields) {
      fieldNames.add(f.getName());
    }
    for (String root : fieldNames) {
      for (Method getter : clazz.getMethods()) {
        if (getter.getName().toUpperCase().equals("GET" + root.toUpperCase())
                && getter.getParameterTypes().length == 0) {
          Method setter = getSetter(clazz, getter, "SET" + root.toUpperCase());
          //NOTE: Collection properties are not copied
          if (setter != null && methodsSupportSameArgs(getter, setter)
                  && !(getter.getReturnType().isInstance(Collection.class))) {
            Object o = getter.invoke(source, Collections.EMPTY_LIST.toArray());
            if (o != null) {
              setter.invoke(ret, o);
            }
          }
        }
      }
    }
    return ret;
  }
 
  /**
   * The Encounter.setProvider() contains the different overloaded methods and this filters the
   * correct setter from those
   *
   * @param clazz
   * @param getter
   * @param methodname
   * @return
   */
  private static Method getSetter(Class<? extends Object> clazz, Method getter, String methodname) {
   
    List<Method> setterMethods = getMethodCaseInsensitive(clazz, methodname);
    if (setterMethods != null && !setterMethods.isEmpty()) {
      if (setterMethods.size() == 1) {
        return setterMethods.get(0);
      } else if (setterMethods.size() > 1) {
        for (Method m : setterMethods) {
          Class<?>[] parameters = m.getParameterTypes();
          for (Class<?> parameter : parameters) {
            if (getter.getReturnType().equals(parameter)) {
              return m;
            }
          }
        }
      }
    }
    return null;
  }
 
  /**
   * Performs a case insensitive search on a class for a method by name.
   *
   * @param clazz
   * @param methodName
   * @return the found Method
   */
  private static List<Method> getMethodCaseInsensitive(Class<? extends Object> clazz, String methodName) {
   
    List<Method> methodList = new ArrayList<Method>();
    for (Method m : clazz.getMethods()) {
      if (m.getName().toUpperCase().equals(methodName.toUpperCase())) {
        methodList.add(m);
       
      }
    }
    return methodList;
  }
 
  /**
   * compares getter return types to setter parameter types
   *
   * @param getter
   * @param setter
   * @return true if getter return types are the same as setter parameter types. Else false.
   */
  private static boolean methodsSupportSameArgs(Method getter, Method setter) {
    if (getter != null && setter != null && setter.getParameterTypes() != null && setter.getParameterTypes().length == 1
            && getter.getReturnType() != null && getter.getReturnType().equals(setter.getParameterTypes()[0]))
      return true;
    return false;
  }
 
  /**
   * recurses through all superclasses of a class and adds the fields from that superclass
   *
   * @param fields
   * @param clazz
   */
  private static void addSuperclassFields(List<Field> fields, Class<? extends Object> clazz) {
    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
    if (clazz.getSuperclass() != null) {
      addSuperclassFields(fields, clazz.getSuperclass());
    }
  }
 
  /**
   * Translates a String into a Date.
   *
   * @param value use "now" for the current timestamp, "today" for the current date with a
   *            timestamp of 00:00, or a date string that can be parsed by SimpleDateFormat with
   *            the format parameter.
   * @param format the pattern SimpleDateTime will use to parse the value, if other than "now" or
   *            "today".
   * @return Date on success; null for an invalid value
   * @throws IllegalArgumentException if a date string cannot be parsed with the format string you
   *             provided
   * @see java.text.SimpleDateFormat
   * @should return a Date object with current date and time for "now"
   * @shold return a Date with current date, but time of 00:00:00:00, for "today"
   * @should return a Date object matching the value param if a format is specified
   * @should return null for null value
   * @should return null if format is null and value not in [ null, "now", "today" ]
   * @should fail if date parsing fails
   */
  public static Date translateDatetimeParam(String value, String format) {
    if (value == null)
      return null;
    if ("now".equals(value)) {
      return new Date();
    } else if ("today".equals(value)) {
      Calendar cal = Calendar.getInstance();
      cal.set(Calendar.HOUR_OF_DAY, 0);
      cal.set(Calendar.MINUTE, 0);
      cal.set(Calendar.SECOND, 0);
      cal.set(Calendar.MILLISECOND, 0);
      return cal.getTime();
    } else if (format != null) { // the value is a timestamp; parse it with
                               // the format pattern
      try {
        SimpleDateFormat df = new SimpleDateFormat(format);
        return df.parse(value);
      }
      catch (ParseException ex) {
        throw new IllegalArgumentException(ex);
      }
    }
    return null;
  }

    /**
     * Given a list of patient programs and a program, returns the first patient program
     * that matches the specified program
     *
     * @param patientPrograms
     * @param program
     * @return
     */
    public static PatientProgram getPatientProgramByProgram(List<PatientProgram> patientPrograms, Program program) {

        for (PatientProgram patientProgram : patientPrograms) {
            if (patientProgram.getProgram().equals(program)) {
                return patientProgram;
            }
        }

        return null;
    }

    /**
     * Given a patient and a program workflow, returns the first patient program that contains
     * a state in the specified workflow
     *
     * @param patient
     * @param workflow
     * @return
     */
  public static PatientProgram getPatientProgramByWorkflow(Patient patient, ProgramWorkflow workflow) {
    List<PatientProgram> patientPrograms = Context.getProgramWorkflowService().getPatientPrograms(patient,
        workflow.getProgram(), null, null, null, null, false);
   
    PatientProgram patientProgram = null;
   
    for (PatientProgram eachPatientProgram : patientPrograms) {
      boolean foundState = false;
      for (PatientState patientState : eachPatientProgram.getStates()) {
        if (patientState.getState().getProgramWorkflow().equals(workflow)) {
          foundState = true;
        }
      }
     
      if (foundState) {
        if (patientProgram != null) {
          throw new IllegalStateException("Does not support multiple programs");
        } else {
          patientProgram = eachPatientProgram;
        }
      }
    }
   
    return patientProgram;
  }

    /**
     * If the specified patient is enrolled in the specified program on the specified date,
     * return the associated patient program, otherwise return null
     *
     * @param patient
     * @param program
     * @param date
     * @return
     */
    public static PatientProgram getPatientProgramByProgramOnDate(Patient patient, Program program, Date date) {

        List<PatientProgram> patientPrograms = Context.getProgramWorkflowService().getPatientPrograms(patient, program, null, date, date, null, false);

        if (patientPrograms.size() > 1) {
            throw new APIException("Simultaneous program enrollments in same program not supported");
        }

        if (patientPrograms.size() == 1) {
            return patientPrograms.get(0);
        }
        else {
            return null;
        }

    }

  /**
   * Checks whether the encounter has a provider specified (including ugly reflection code for
   * 1.9+)
   *
   * @param e
   * @return whether e has one or more providers specified
   */
  @SuppressWarnings("rawtypes")
  public static boolean hasProvider(Encounter e) {
    try {
      Method method = e.getClass().getMethod("getProvidersByRoles");
      // this is a Map<EncounterRole, Set<Provider>>
      Map providersByRoles = (Map) method.invoke(e);
      return providersByRoles != null && providersByRoles.size() > 0;
    }
    catch (Exception ex) {
      return e.getProvider() != null;
    }
  }
 
  /**
   * Checks if the user is enrolled in a program at the specified date
   *
   * @param patient the patient that should be enrolled in the program
   * @param program the program the patient be enrolled in
   * @param date the date at which to check
   * @return
   * @should return true if the patient is enrolled in the program at the specified date
   * @should return false if the patient is not enrolled in the program
   * @should return false if the program was completed
   * @should return false if the date is before the existing patient program enrollment date
   */
  public static boolean isEnrolledInProgramOnDate(Patient patient, Program program, Date date) {
    if (patient == null)
      throw new IllegalArgumentException("patient should not be null");
    if (program == null)
      throw new IllegalArgumentException("program should not be null");
    if (date == null)
      throw new IllegalArgumentException("date should not be null");
   
    if (patient.getPatientId() == null)
      return false;
   
    List<PatientProgram> patientPrograms = Context.getProgramWorkflowService().getPatientPrograms(patient, program,
        null, date, date, null, false);
   
    return (patientPrograms.size() > 0);
   
  }
 
  /**
   * Checks to see if the patient has a program enrollment in the specified program after the
   * given date If multiple patient programs, returns the earliest enrollment If no enrollments,
   * returns null
   */
  public static PatientProgram getClosestFutureProgramEnrollment(Patient patient, Program program, Date date) {
    if (patient == null)
      throw new IllegalArgumentException("patient should not be null");
    if (program == null)
      throw new IllegalArgumentException("program should not be null");
    if (date == null)
      throw new IllegalArgumentException("date should not be null");
   
    if (patient.getPatientId() == null)
      return null;
   
    PatientProgram closestProgram = null;
    List<PatientProgram> patientPrograms = Context.getProgramWorkflowService().getPatientPrograms(patient, program,
        date, null, null, null, false);
   
    for (PatientProgram pp : patientPrograms) {
      if ((closestProgram == null || pp.getDateEnrolled().before(closestProgram.getDateEnrolled()))
              && pp.getDateEnrolled().after(date)) {
        closestProgram = pp;
      }
     
    }
   
    return closestProgram;
  }
 
  /**
   * Given a Date object, returns a Date object for the same date but with the time component
   * (hours, minutes, seconds & milliseconds) removed
   */
  public static Date clearTimeComponent(Date date) {
    // Get Calendar object set to the date and time of the given Date object 
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
   
    // Set time fields to zero 
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
   
    return cal.getTime();
  }
 
  /**
   * @param s the string to conver to camelcase
   * @return should return the passed in string in the camelcase format
   */
  public static String toCamelCase(String s) {
    StringBuffer sb = new StringBuffer();
    String[] words = s.replaceAll("[^A-Za-z]", " ").replaceAll("\\s+", " ").trim().split(" ");
   
    for (int i = 0; i < words.length; i++) {
      if (i == 0)
        words[i] = words[i].toLowerCase();
      else
        words[i] = String.valueOf(words[i].charAt(0)).toUpperCase() + words[i].substring(1);
     
      sb.append(words[i]);
    }
    return sb.toString();
  }
 
  /***
   * Get the encountger type by: 1)an integer id like 1 or 2) uuid like
   * "a3e12268-74bf-11df-9768-17cfc9833272" or 3) encounter type name like "AdultInitial".
   *
   * @param id
   * @return the encounter type if exist, else null
   * @should find a encounter type by its encounterTypeId
   * @should find a encounter type by name
   * @should find a encounter type by its uuid
   * @should return null otherwise
   */
  public static EncounterType getEncounterType(String id) {
   
    EncounterType encounterType = null;
   
    if (StringUtils.isNotBlank(id)) {
      id = id.trim();
      // see if this is parseable int; if so, try looking up by id
      try {
        int encounterTypeId = Integer.parseInt(id);
        encounterType = Context.getEncounterService().getEncounterType(encounterTypeId);
       
        if (encounterType != null)
          return encounterType;
      }
      catch (Exception ex) {
        //do nothing
      }
     
      // handle uuid id: "a3e1302b-74bf-11df-9768-17cfc9833272" if id matches a uuid format
      if (isValidUuidFormat(id)) {
        encounterType = Context.getEncounterService().getEncounterTypeByUuid(id);
       
        if (encounterType != null)
          return encounterType;
      }
     
      // if it's neither a uuid or id, try encounter type name
      encounterType = Context.getEncounterService().getEncounterType(id);
     
      if (encounterType != null)
        return encounterType;
    }
   
    // no match found
    return null;
  }

  /**
   * Removes any Obs that are empty or which have only empty children
   */
  public static void removeEmptyObs(Collection<Obs> obsList) {
    if (obsList != null) {
      Set<Obs> obsToRemove = new HashSet<Obs>();
      for (Obs o : obsList) {
        removeEmptyObs(o.getGroupMembers());
        boolean valueEmpty = StringUtils.isEmpty(o.getValueAsString(Context.getLocale()));
        boolean membersEmpty = o.getGroupMembers() == null || o.getGroupMembers().isEmpty();
        if (valueEmpty && membersEmpty) {
          obsToRemove.add(o);
        }
      }
      for (Obs o : obsToRemove) {
        if (o.getObsGroup() != null) {
          o.getObsGroup().removeGroupMember(o);
          o.setObsGroup(null);
        }
        if (o.getEncounter() != null) {
          o.getEncounter().removeObs(o);
          o.setEncounter(null);
        }
        obsList.remove(o);
      }
    }
  }


    /**
     * Formats a piece of Metadata for Display
     *
     * @param md
     * @return
     */
    public static String format(OpenmrsMetadata md) {
        return format(md, Context.getLocale());
    }

    /**
     * Formats a piece of Metadata for Display
     *
     * TODO This was copied from UiFramework--we probably should come up with a way to inject a formatter directly into HFE
     *
     * @param md
     * @param locale
     * @return
     */
    public static String format(OpenmrsMetadata md, Locale locale) {
        String override = getLocalization(locale, md.getClass().getSimpleName(), md.getUuid());
        return override != null ? override : md.getName();
    }

    /**
     * See if there is a custom name for this message in the messages.properties files
     *
     * TODO This was copied from UiFramework--we probably should come up with a way to inject a formatter directly into HFE
     *
     * @param locale
     * @return
     */
    private static String getLocalization(Locale locale, String shortClassName, String uuid) {

        // in case this is a hibernate proxy, strip off anything after an underscore
        // ie: EncounterType_$$_javassist_26 needs to be converted to EncounterType
        int underscoreIndex = shortClassName.indexOf("_$");
        if (underscoreIndex > 0) {
            shortClassName = shortClassName.substring(0, underscoreIndex);
        }

        String code = "ui.i18n." + shortClassName + ".name." + uuid;
        String localization = Context.getService(MessageSourceService.class).getMessage(code, null, locale);
        if (localization == null || localization.equals(code)) {
            return null;
        } else {
            return localization;
        }
    }

    /**
     * Given a include/exclude string. fetch the test expression
     *
     * @param teststr
     * @return a substring of a test expression
     * @throws BadFormDesignException
     * @should extract the correct expression from teststr
     */
    public static String getTestStr(String teststr) throws BadFormDesignException {
        if (StringUtils.isBlank(teststr))
            throw new BadFormDesignException("Can't extract the test expression from " + teststr);

        //get the text inside the quotes, i.e the expression
        String[] actualExpression = StringUtils.substringsBetween(teststr, "\"", "\"");

        if (actualExpression == null || actualExpression.length != 1 || StringUtils.isBlank(actualExpression[0])) {
            throw new BadFormDesignException("Can't extract the test expression from " + teststr);//throw bad design exception here
        }

        return actualExpression[0];
    }

}
TOP

Related Classes of org.openmrs.module.htmlformentry.HtmlFormEntryUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.