@Override
  public EncounterQueryResult evaluate(EncounterQuery definition, EvaluationContext context)
      throws EvaluationException {
    context = ObjectUtil.nvl(context, new EvaluationContext());

    BasicEncounterQuery query = (BasicEncounterQuery) definition;
    EncounterQueryResult result = new EncounterQueryResult(query, context);

    if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
      return result;
    }
    if (context instanceof EncounterEvaluationContext) {
      EncounterIdSet baseEncounters = ((EncounterEvaluationContext) context).getBaseEncounters();
      if (baseEncounters != null && baseEncounters.getMemberIds().isEmpty()) {
        return result;
      }
    }

    HqlQueryBuilder q = new HqlQueryBuilder();
    q.select("e.encounterId");
    q.from(Encounter.class, "e");
    q.whereIn("e.encounterType", query.getEncounterTypes());
    q.whereIn("e.form", query.getForms());
    q.whereGreaterOrEqualTo("e.encounterDatetime", query.getOnOrAfter());
    q.whereLessOrEqualTo("e.encounterDatetime", query.getOnOrBefore());
    q.whereIn("e.location", query.getLocationList());
    q.whereEncounterIn("e.encounterId", context);

    if (query.getWhich() == null || query.getWhich() == TimeQualifier.ANY) {
      List<Integer> results = evaluationService.evaluateToList(q, Integer.class);
      result.getMemberIds().addAll(results);
    } else {
      q.innerJoin("e.patient", "p");
      q.select("p.patientId");
      if (query.getWhich() == TimeQualifier.LAST) {
        q.orderDesc("e.encounterDatetime");
      } else {
        q.orderAsc("e.encounterDatetime");
      }

      ListMap<Integer, Integer> foundEncountersForPatients = new ListMap<Integer, Integer>();
      int maxNumPerPatient = ObjectUtil.nvl(query.getWhichNumber(), 1);
      for (Object[] row : evaluationService.evaluateToList(q)) {
        Integer encounterId = (Integer) row[0];
        Integer patientId = (Integer) row[1];
        foundEncountersForPatients.putInList(patientId, encounterId);
        if (foundEncountersForPatients.get(patientId).size() <= maxNumPerPatient) {
          result.add(encounterId);
        }
      }
    }

    return result;
  }
  @Override
  public EncounterQueryResult evaluate(EncounterQuery definition, EvaluationContext context)
      throws EvaluationException {
    context = ObjectUtil.nvl(context, new EvaluationContext());

    BasicEncounterQuery query = (BasicEncounterQuery) definition;
    EncounterQueryResult result = new EncounterQueryResult(query, context);

    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class);
    criteria.setProjection(Projections.id());
    criteria.add(Restrictions.eq("voided", false));

    if (query.getOnOrAfter() != null) {
      criteria.add(Restrictions.ge("encounterDatetime", query.getOnOrAfter()));
    }
    if (query.getOnOrBefore() != null) {
      criteria.add(
          Restrictions.le(
              "encounterDatetime", DateUtil.getEndOfDayIfTimeExcluded(query.getOnOrBefore())));
    }

    if (context.getBaseCohort() != null) {
      if (context.getBaseCohort().size() == 0) {
        return result;
      } else {
        criteria.add(Restrictions.in("patient.id", context.getBaseCohort().getMemberIds()));
      }
    }
    if (context instanceof EncounterEvaluationContext) {
      EncounterIdSet baseEncounters = ((EncounterEvaluationContext) context).getBaseEncounters();
      if (baseEncounters != null) {
        if (baseEncounters.getSize() == 0) {
          return result;
        } else {
          criteria.add(Restrictions.in("id", baseEncounters.getMemberIds()));
        }
      }
    }

    for (Integer encounterId : ((List<Integer>) criteria.list())) {
      result.add(encounterId);
    }
    return result;
  }
  /**
   * @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext)
   * @should evaluate an EncounterAndObsDataSetDefinition
   */
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    EncounterAndObsDataSetDefinition definition =
        (EncounterAndObsDataSetDefinition) dataSetDefinition;
    SimpleDataSet dataSet = new SimpleDataSet(dataSetDefinition, context);
    context = ObjectUtil.nvl(context, new EvaluationContext());
    Cohort cohort = context.getBaseCohort();
    if (context.getLimit() != null) {
      CohortUtil.limitCohort(cohort, context.getLimit());
    }

    // Retrieve the rows for the dataset

    BasicEncounterQuery encounterQuery = new BasicEncounterQuery();
    encounterQuery.setWhich(definition.getWhichEncounterQualifier());
    encounterQuery.setWhichNumber(definition.getQuantity());
    encounterQuery.setEncounterTypes(definition.getEncounterTypes());
    encounterQuery.setForms(definition.getForms());
    encounterQuery.setOnOrBefore(definition.getEncounterDatetimeOnOrBefore());
    encounterQuery.setOnOrAfter(definition.getEncounterDatetimeOnOrAfter());

    EncounterQueryResult encounterIdRows = encounterQueryService.evaluate(encounterQuery, context);

    HqlQueryBuilder q = new HqlQueryBuilder();
    EncounterEvaluationContext eec = new EncounterEvaluationContext();
    eec.setBaseEncounters(encounterIdRows);
    q.from(Encounter.class, "e").whereEncounterIn("e.encounterId", eec);
    List<Encounter> encounters = evaluationService.evaluateToList(q, Encounter.class);

    // Determine what columns to display in the dataset

    List<EncounterAndObsDataSetDefinition.ObsOptionalColumn> optionalColumns =
        definition.getOptionalColumns();
    List<PatientIdentifierType> patientIdentifierTypes = definition.getPatientIdentifierTypes();
    List<EncounterAndObsDataSetDefinition.ColumnDisplayFormat> columnDisplayFormat =
        definition.getColumnDisplayFormat();
    Integer maxColumnHeaderWidth = definition.getMaxColumnHeaderWidth();

    if (patientIdentifierTypes == null) {
      patientIdentifierTypes = new ArrayList<PatientIdentifierType>();
    }

    if (columnDisplayFormat == null) {
      columnDisplayFormat = new ArrayList<ColumnDisplayFormat>();
    }

    if (columnDisplayFormat.size() == 0) {
      columnDisplayFormat.add(EncounterAndObsDataSetDefinition.ColumnDisplayFormat.ID);
    }

    // section index should be added here

    // Store all encounters within a data structure that keeps track of obs, obs groups, and their
    // occurrences
    // in order that column headers are unique and children obs are grouped by their parent
    Map<Encounter, Map<ObsColumnDescriptor, Obs>> populatedFieldMap = populateFieldMap(encounters);

    // Keeps track of the column headers for all obs-related columns
    Set<ObsColumnDescriptor> allColumns = new TreeSet<ObsColumnDescriptor>();

    for (Encounter encounter : encounters) {
      Map<ObsColumnDescriptor, Obs> obsInEncounter = populatedFieldMap.get(encounter);
      // Not all encounters will have data for all columns but
      // each encounter row should have all column headers
      // so encounters line up properly under a common set of column headers
      allColumns.addAll(obsInEncounter.keySet());
    }

    // add the data to the DataSet
    return addData(
        dataSet,
        encounters,
        patientIdentifierTypes,
        optionalColumns,
        columnDisplayFormat,
        maxColumnHeaderWidth,
        allColumns,
        populatedFieldMap);
  }
  /** @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext) */
  @SuppressWarnings("unchecked")
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    PatientDataSetDefinition dsd = (PatientDataSetDefinition) dataSetDefinition;
    context = ObjectUtil.nvl(context, new EvaluationContext());

    SimpleDataSet dataSet = new SimpleDataSet(dsd, context);
    dataSet.setSortCriteria(dsd.getSortCriteria());

    // Construct a new EvaluationContext based on the passed filters
    Cohort c = context.getBaseCohort();
    if (dsd.getRowFilters() != null) {
      for (Mapped<? extends CohortDefinition> q : dsd.getRowFilters()) {
        Cohort s = Context.getService(CohortDefinitionService.class).evaluate(q, context);
        c = CohortUtil.intersectNonNull(c, s);
      }
    }
    if (c == null) {
      c =
          Context.getService(CohortDefinitionService.class)
              .evaluate(new AllPatientsCohortDefinition(), context);
    }

    EvaluationContext ec = context.shallowCopy();
    if (!CohortUtil.areEqual(ec.getBaseCohort(), c)) {
      ec.setBaseCohort(c);
    }

    // Evaluate each specified ColumnDefinition for all of the included rows and add these to the
    // dataset
    for (RowPerObjectColumnDefinition cd : dsd.getColumnDefinitions()) {

      if (log.isDebugEnabled()) {
        log.debug("Evaluating column: " + cd.getName());
        log.debug(
            "With Data Definition: "
                + DefinitionUtil.format(cd.getDataDefinition().getParameterizable()));
        log.debug("With Mappings: " + cd.getDataDefinition().getParameterMappings());
        log.debug("With Parameters: " + ec.getParameterValues());
      }
      StopWatch sw = new StopWatch();
      sw.start();

      MappedData<? extends PatientDataDefinition> dataDef =
          (MappedData<? extends PatientDataDefinition>) cd.getDataDefinition();
      EvaluatedPatientData data =
          Context.getService(PatientDataService.class).evaluate(dataDef, ec);

      for (Integer id : c.getMemberIds()) {
        for (DataSetColumn column : cd.getDataSetColumns()) {
          Object val = data.getData().get(id);
          val = DataUtil.convertData(val, dataDef.getConverters());
          dataSet.addColumnValue(id, column, val);
        }
      }

      sw.stop();
      if (log.isDebugEnabled()) {
        log.debug("Evaluated column. Duration: " + sw.toString());
      }
    }

    return dataSet;
  }