gt: 			Set<Order> orders = encounter.getOrders();
context: /**
   * 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.api.impl;
  
  import org.apache.commons.lang.StringUtils;
  import org.openmrs.Cohort;
  import org.openmrs.Encounter;
  import org.openmrs.EncounterRole;
  import org.openmrs.EncounterType;
  import org.openmrs.Form;
  import org.openmrs.Location;
  import org.openmrs.Obs;
  import org.openmrs.Order;
  import org.openmrs.OrderGroup;
  import org.openmrs.Patient;
  import org.openmrs.Privilege;
  import org.openmrs.Provider;
  import org.openmrs.User;
  import org.openmrs.Visit;
  import org.openmrs.VisitType;
  import org.openmrs.api.APIException;
  import org.openmrs.api.EncounterService;
  import org.openmrs.api.EncounterTypeLockedException;
  import org.openmrs.api.ObsService;
  import org.openmrs.api.OrderService;
  import org.openmrs.api.context.Context;
  import org.openmrs.api.db.EncounterDAO;
  import org.openmrs.api.handler.EncounterVisitHandler;
  import org.openmrs.parameter.EncounterSearchCriteria;
  import org.openmrs.parameter.EncounterSearchCriteriaBuilder;
  import org.openmrs.util.HandlerUtil;
  import org.openmrs.util.OpenmrsClassLoader;
  import org.openmrs.util.OpenmrsConstants;
  import org.openmrs.util.OpenmrsUtil;
  import org.openmrs.util.PrivilegeConstants;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Date;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.Vector;
  
  /**
   * Default implementation of the {@link EncounterService}
   * <p>
   * This class should not be instantiated alone, get a service class from the Context:
   * Context.getEncounterService();
   * 
   * @see org.openmrs.api.context.Context
   * @see org.openmrs.api.EncounterService
   */
  @Transactional
  public class EncounterServiceImpl extends BaseOpenmrsService implements EncounterService {
  	
  	// private Log log = LogFactory.getLog(this.getClass());
  	
  	private EncounterDAO dao;
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#setEncounterDAO(org.openmrs.api.db.EncounterDAO)
  	 */
  	public void setEncounterDAO(EncounterDAO dao) {
  		this.dao = dao;
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncountersByPatient(java.lang.String, boolean)
  	 */
  	@Override
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncountersByPatient(String query, boolean includeVoided) throws APIException {
  		if (query == null) {
  			throw new IllegalArgumentException("The 'query' parameter is required and cannot be null");
  		}
  		
  		return Context.getEncounterService().filterEncountersByViewPermissions(
  		    dao.getEncounters(query, null, null, null, includeVoided), null);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#saveEncounter(org.openmrs.Encounter)
  	 */
  	public Encounter saveEncounter(Encounter encounter) throws APIException {
  		
  		// if authenticated user is not supposed to edit encounter of certain type
  		if (!canEditEncounter(encounter, null)) {
  			throw new APIException("Encounter.error.privilege.required.edit", new Object[] { encounter.getEncounterType()
  			        .getEditPrivilege() });
  		}
  		
  		//If new encounter, try to assign a visit using the registered visit assignment handler.
  		if (encounter.getEncounterId() == null) {
  			
  			//Am using Context.getEncounterService().getActiveEncounterVisitHandler() instead of just
  			//getActiveEncounterVisitHandler() for modules which may want to AOP around this call.
  			EncounterVisitHandler encounterVisitHandler = Context.getEncounterService().getActiveEncounterVisitHandler();
  			if (encounterVisitHandler != null) {
  				encounterVisitHandler.beforeCreateEncounter(encounter);
  				
  				//If we have been assigned a new visit, persist it.
  				if (encounter.getVisit() != null && encounter.getVisit().getVisitId() == null) {
  					Context.getVisitService().saveVisit(encounter.getVisit());
  				}
  			}
  		}
  		
  		boolean isNewEncounter = false;
  		Date newDate = encounter.getEncounterDatetime();
  		Date originalDate = null;
  		Location newLocation = encounter.getLocation();
  		Location originalLocation = null;
  		// check permissions
  		if (encounter.getEncounterId() == null) {
  			isNewEncounter = true;
  			Context.requirePrivilege(PrivilegeConstants.ADD_ENCOUNTERS);
  		} else {
  			Context.requirePrivilege(PrivilegeConstants.EDIT_ENCOUNTERS);
  		}
  		
  		// This must be done after setting dateCreated etc on the obs because
  		// of the way the ORM tools flush things and check for nullity
  		// This also must be done before the save encounter so we can use the
  		// orig date
  		// after the save
  		Patient p = encounter.getPatient();
  		
  		if (!isNewEncounter) {
  			// fetch the datetime from the database prior to saving for this
  			// encounter
  			// to see if it has changed and change all obs after saving if so
  			originalDate = dao.getSavedEncounterDatetime(encounter);
  			if (encounter.getLocation() != null) {
  				originalLocation = dao.getSavedEncounterLocation(encounter);
  			}
  			// Our data model duplicates the patient column to allow for
  			// observations to
  			// not have to look up the parent Encounter to find the patient
  			// Therefore, encounter.patient must always equal
  			// encounter.observations[0-n].patient
  			
  			// If we are changing encounter.encounterDatetime, then we need to
  			// also apply that
  			// to Obs that inherited their obsDatetime from the encounter in the
  			// first place
  			
  			for (Obs obs : encounter.getAllObs(true)) {
  				// if the date was changed
  				if (OpenmrsUtil.compare(originalDate, newDate) != 0
  				        && OpenmrsUtil.compare(obs.getObsDatetime(), originalDate) == 0) {
  					
  					// if the obs datetime is the same as the
  					// original encounter datetime, fix it
  					obs.setObsDatetime(newDate);
  					
  				}
  				
  				if (!OpenmrsUtil.nullSafeEquals(newLocation, originalLocation) && obs.getLocation().equals(originalLocation)) {
  					obs.setLocation(newLocation);
  				}
  				
  				// if the Person in the obs doesn't match the Patient in the
  				// encounter, fix it
  				if (!obs.getPerson().getPersonId().equals(p.getPatientId())) {
  					obs.setPerson(p);
  				}
  			}
  		}
  		// same goes for Orders
  		for (Order o : encounter.getOrders()) {
  			if (!p.equals(o.getPatient())) {
  				o.setPatient(p);
  			}
  		}
  		
  		// do the actual saving to the database
  		dao.saveEncounter(encounter);
  		
  		// save the new orderGroups
  		for (OrderGroup orderGroup : encounter.getOrderGroups()) {
  			Context.getOrderService().saveOrderGroup(orderGroup);
  		}
  		//save the new orders which do not have order groups
  		for (Order o : encounter.getOrdersWithoutOrderGroups()) {
  			if (o.getOrderId() == null) {
  				Context.getOrderService().saveOrder(o, null);
  			}
  		}
  		
  		// save the Obs
  		String changeMessage = Context.getMessageSourceService().getMessage("Obs.void.reason.default");
  		ObsService os = Context.getObsService();
  		List<Obs> toRemove = new ArrayList<>();
  		List<Obs> toAdd = new ArrayList<>();
  		for (Obs o : encounter.getObsAtTopLevel(true)) {
  			if (o.getId() == null) {
  				os.saveObs(o, null);
  			} else {
  				Obs newObs = os.saveObs(o, changeMessage);
  				//The logic in saveObs evicts the old obs instance, so we need to update the collection
  				//with the newly loaded and voided instance, apparently reloading the encounter
  				//didn't do the tick
  				toRemove.add(o);
  				toAdd.add(os.getObs(o.getId()));
  				toAdd.add(newObs);
  			}
  		}
  		
  		for (Obs o : toRemove) {
  			encounter.removeObs(o);
  		}
  		for (Obs o : toAdd) {
  			encounter.addObs(o);
  		}
  		
  		return encounter;
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncounter(java.lang.Integer)
  	 */
  	@Transactional(readOnly = true)
  	public Encounter getEncounter(Integer encounterId) throws APIException {
  		Encounter encounter = dao.getEncounter(encounterId);
  		if (encounter == null) {
  			return null;
  		} else if (canViewEncounter(encounter, null)) {
  			return encounter;
  		} else {
  			throw new APIException("Encounter.error.privilege.required.view", new Object[] { encounter.getEncounterType()
  			        .getViewPrivilege() });
  		}
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncountersByPatient(org.openmrs.Patient)
  	 */
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncountersByPatient(Patient patient) throws APIException {
  		if (patient == null) {
  			throw new IllegalArgumentException("The 'patient' parameter is requred and cannot be null");
  		}
  		
  		EncounterSearchCriteria encounterSearchCriteria = new EncounterSearchCriteriaBuilder().setPatient(patient)
  		        .setIncludeVoided(false).createEncounterSearchCriteria();
  		
  		return Context.getEncounterService().getEncounters(encounterSearchCriteria);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncountersByPatient(String)
  	 */
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncountersByPatient(String query) throws APIException {
  		
  		return Context.getEncounterService().filterEncountersByViewPermissions(getEncountersByPatient(query, false), null);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncountersByPatientId(java.lang.Integer)
  	 */
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncountersByPatientId(Integer patientId) throws APIException {
  		if (patientId == null) {
  			throw new IllegalArgumentException("The 'patientId' parameter is requred and cannot be null");
  		}
  		return Context.getEncounterService()
  		        .filterEncountersByViewPermissions(dao.getEncountersByPatientId(patientId), null);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncountersByPatientIdentifier(java.lang.String)
  	 */
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncountersByPatientIdentifier(String identifier) throws APIException {
  		if (identifier == null) {
  			throw new IllegalArgumentException("The 'identifier' parameter is required and cannot be null");
  		}
  		
  		List<Encounter> encs = new Vector<Encounter>();
  		for (Patient p : Context.getPatientService().getPatients(identifier, null, null, false)) {
  			encs.addAll(Context.getEncounterService().getEncountersByPatientId(p.getPatientId()));
  		}
  		return Context.getEncounterService().filterEncountersByViewPermissions(encs, null);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncounters(org.openmrs.Patient,
  	 *      org.openmrs.Location, java.util.Date, java.util.Date, java.util.Collection,
  	 *      java.util.Collection, java.util.Collection, java.util.Collection, java.util.Collection,
  	 *      boolean)
  	 * @deprecated As of 2.0, replaced by {@link #getEncounters(EncounterSearchCriteria)}
  	 */
  	@Deprecated
  	@Override
  	@Transactional(readOnly = true)
  	public List<Encounter> getEncounters(Patient who, Location loc, Date fromDate, Date toDate,
  	                                     Collection<Form> enteredViaForms, Collection<EncounterType> encounterTypes,
  	                                     Collection<Provider> providers, Collection<VisitType> visitTypes,
  	                                     Collection<Visit> visits, boolean includeVoided) {
  		EncounterSearchCriteriaBuilder encounterSearchCriteriaBuilder = new EncounterSearchCriteriaBuilder().setPatient(who)
  		        .setLocation(loc).setFromDate(fromDate).setToDate(toDate).setEnteredViaForms(enteredViaForms)
  		        .setEncounterTypes(encounterTypes).setProviders(providers).setVisitTypes(visitTypes).setVisits(visits)
  		        .setIncludeVoided(includeVoided);
  		
  		return getEncounters(encounterSearchCriteriaBuilder.createEncounterSearchCriteria());
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#getEncounters(org.openmrs.parameter.EncounterSearchCriteria)
  	 */
  	public List<Encounter> getEncounters(EncounterSearchCriteria encounterSearchCriteria) {
  		// the second search parameter is null as it defaults to authenticated user from context
  		return Context.getEncounterService().filterEncountersByViewPermissions(dao.getEncounters(encounterSearchCriteria),
  		    null);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#voidEncounter(org.openmrs.Encounter, java.lang.String)
  	 */
  	public Encounter voidEncounter(Encounter encounter, String reason) {
  		
  		// if authenticated user is not supposed to edit encounter of certain type
  		if (!canEditEncounter(encounter, null)) {
  			throw new APIException("Encounter.error.privilege.required.void", new Object[] { encounter.getEncounterType()
  			        .getEditPrivilege() });
  		}
  		
  		if (reason == null) {
  			throw new IllegalArgumentException("The argument 'reason' is required and so cannot be null");
  		}
  		
  		ObsService os = Context.getObsService();
  		for (Obs o : encounter.getObsAtTopLevel(false)) {
  			if (!o.isVoided()) {
  				os.voidObs(o, reason);
  			}
  		}
  		
  		OrderService orderService = Context.getOrderService();
  		for (Order o : encounter.getOrders()) {
  			if (!o.isVoided()) {
  				orderService.voidOrder(o, reason);
  			}
  		}
  		
  		encounter.setVoided(true);
  		encounter.setVoidedBy(Context.getAuthenticatedUser());
  		//we expect the dateVoided to be already set by AOP logic at this point unless this method was called within the API,
  		//this ensures that original ParentVoidedDate and the dateVoided of associated objects will always match for the
  		//unvoid handler to work
  		if (encounter.getDateVoided() == null) {
  			encounter.setDateVoided(new Date());
  		}
  		encounter.setVoidReason(reason);
  		Context.getEncounterService().saveEncounter(encounter);
  		return encounter;
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#unvoidEncounter(org.openmrs.Encounter)
  	 */
  	public Encounter unvoidEncounter(Encounter encounter) throws APIException {
  		
  		// if authenticated user is not supposed to edit encounter of certain type
  		if (!canEditEncounter(encounter, null)) {
  			throw new APIException("Encounter.error.privilege.required.unvoid", new Object[] { encounter.getEncounterType()
  			        .getEditPrivilege() });
  		}
  		
  		String voidReason = encounter.getVoidReason();
  		if (voidReason == null) {
  			voidReason = "";
  		}
  		
  		ObsService os = Context.getObsService();
  		for (Obs o : encounter.getObsAtTopLevel(true)) {
  			if (voidReason.equals(o.getVoidReason())) {
  				os.unvoidObs(o);
  			}
  		}
  		
  		OrderService orderService = Context.getOrderService();
  		for (Order o : encounter.getOrders()) {
  			if (voidReason.equals(o.getVoidReason())) {
  				orderService.unvoidOrder(o);
  			}
  		}
  		
  		encounter.setVoided(false);
  		encounter.setVoidedBy(null);
  		encounter.setDateVoided(null);
  		encounter.setVoidReason(null);
  		Context.getEncounterService().saveEncounter(encounter);
  		return encounter;
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#purgeEncounter(org.openmrs.Encounter)
  	 */
  	public void purgeEncounter(Encounter encounter) throws APIException {
  		// if authenticated user is not supposed to edit encounter of certain type
  		if (!canEditEncounter(encounter, null)) {
  			throw new APIException("Encounter.error.privilege.required.purge", new Object[] { encounter.getEncounterType()
  			        .getEditPrivilege() });
  		}
  		dao.deleteEncounter(encounter);
  	}
  	
  	/**
  	 * @see org.openmrs.api.EncounterService#purgeEncounter(Encounter, boolean)
  	 */
  	public void purgeEncounter(Encounter encounter, boolean cascade) throws APIException {
  		
  		// if authenticated user is not supposed to edit encounter of certain type
  		if (!canEditEncounter(encounter, null)) {
  			throw new APIException("Encounter.error.privilege.required.purge", new Object[] { encounter.getEncounterType()
  			        .getEditPrivilege() });
  		}
  		
  		if (cascade) {
  			ObsService obsService = Context.getObsService();
  			List<Encounter> justThisEncounter = new ArrayList<Encounter>();
  			justThisEncounter.add(encounter);
  			List<Obs> observations = new Vector<Obs>();
  			observations.addAll(obsService.getObservations(null, justThisEncounter, null, null, null, null, null, null,
  			    null, null, null, true));
  			for (Obs o : observations) {
  				obsService.purgeObs(o);
  			}
  