public PatientSearch createCompositionFilter(String description) {
    Set<String> andWords = new HashSet<String>();
    Set<String> orWords = new HashSet<String>();
    Set<String> notWords = new HashSet<String>();
    andWords.add("and");
    andWords.add("intersection");
    andWords.add("*");
    orWords.add("or");
    orWords.add("union");
    orWords.add("+");
    notWords.add("not");
    notWords.add("!");

    List<Object> currentLine = new ArrayList<Object>();

    try {
      StreamTokenizer st = new StreamTokenizer(new StringReader(description));
      st.ordinaryChar('(');
      st.ordinaryChar(')');
      Stack<List<Object>> stack = new Stack<List<Object>>();
      while (st.nextToken() != StreamTokenizer.TT_EOF) {
        if (st.ttype == StreamTokenizer.TT_NUMBER) {
          Integer thisInt = new Integer((int) st.nval);
          if (thisInt < 1 || thisInt > searchHistory.size()) {
            log.error("number < 1 or > search history size");
            return null;
          }
          currentLine.add(thisInt);
        } else if (st.ttype == '(') {
          stack.push(currentLine);
          currentLine = new ArrayList<Object>();
        } else if (st.ttype == ')') {
          List<Object> l = stack.pop();
          l.add(currentLine);
          currentLine = l;
        } else if (st.ttype == StreamTokenizer.TT_WORD) {
          String str = st.sval.toLowerCase();
          if (andWords.contains(str)) currentLine.add(PatientSetService.BooleanOperator.AND);
          else if (orWords.contains(str)) currentLine.add(PatientSetService.BooleanOperator.OR);
          else if (notWords.contains(str)) currentLine.add(PatientSetService.BooleanOperator.NOT);
          else throw new IllegalArgumentException("Don't recognize " + st.sval);
        }
      }
    } catch (Exception ex) {
      log.error("Error in description string: " + description, ex);
      return null;
    }

    if (!testCompositionList(currentLine)) {
      log.error("Description string failed test: " + description);
      return null;
    }

    // return toPatientFilter(currentLine);
    PatientSearch ret = new PatientSearch();
    ret.setParsedComposition(currentLine);
    return ret;
  }
 /**
  * @return zero-based indices that should also be removed due to cascading. (These will already
  *     have had 1 subtracted from them, since we know that a search from above is being deleted)
  */
 private synchronized List<Integer> removeSearchItemHelper(int i) {
   // 1. Decrement any number in a CohortHistoryCompositionFilter that's greater than i.
   // 2. If any CohortHistoryCompositionFilter references search i, we'll have to cascade delete it
   List<Integer> ret = new ArrayList<Integer>();
   for (int j = i + 1; j < searchHistory.size(); ++j) {
     PatientSearch ps = searchHistory.get(j);
     if (ps.isComposition()) {
       cachedFilters.set(i, null); // this actually only needs to happen if the filter is affected
       // note that i is zero-based, but in a composition filter it would be one-based
       boolean removeMeToo = ps.removeFromHistoryNotify(i + 1);
       if (removeMeToo) ret.add(j - 1);
     }
   }
   return ret;
 }
 public synchronized List<CohortSearchHistoryItemHolder> getItems() {
   checkArrayLengths();
   List<CohortSearchHistoryItemHolder> ret = new ArrayList<CohortSearchHistoryItemHolder>();
   for (int i = 0; i < searchHistory.size(); ++i) {
     CohortSearchHistoryItemHolder item = new CohortSearchHistoryItemHolder();
     PatientSearch search = searchHistory.get(i);
     item.setSearch(search);
     ensureCachedFilter(i);
     PatientFilter filter = cachedFilters.get(i);
     item.setFilter(filter);
     if (search.isSavedFilterReference()) {
       ReportObject ro =
           Context.getReportObjectService().getReportObject(search.getSavedFilterId());
       item.setName(ro.getName());
       item.setDescription(ro.getDescription());
     } else if (search.isSavedCohortReference()) {
       org.openmrs.Cohort c = Context.getCohortService().getCohort(search.getSavedCohortId());
       item.setName(c.getName());
       item.setDescription(c.getDescription());
     } else if (search.isSavedSearchReference()) {
       ReportObject ro =
           Context.getReportObjectService().getReportObject(search.getSavedSearchId());
       item.setName(ro.getName());
       item.setDescription(ro.getDescription());
     } else if (search.isComposition()) {
       item.setName(search.getCompositionString());
     } else {
       item.setName(filter.getName());
       item.setDescription(filter.getDescription());
     }
     item.setSaved(search.isSavedReference());
     item.setCachedResult(cachedResults.get(i));
     item.setCachedResultDate(cachedResultDates.get(i));
     ret.add(item);
   }
   return ret;
 }
  /** @see {@link CohortService#evaluate(CohortDefinition,EvaluationContext)} */
  @Test
  @SkipBaseSetup
  @Verifies(
      value = "should return all patients with blank patient search cohort definition provider",
      method = "evaluate(CohortDefinition,EvaluationContext)")
  public void evaluate_shouldReturnAllPatientsWithBlankPatientSearchCohortDefinitionProvider()
      throws Exception {
    initializeInMemoryDatabase();
    executeDataSet(CREATE_PATIENT_XML);
    authenticate();

    CohortDefinition def = PatientSearch.createFilterSearch(PatientCharacteristicFilter.class);
    Cohort result = service.evaluate(def, null);
    assertNotNull("Should not return null", result);
    assertEquals("Should return one member", 1, result.size());
  }