CohortHistoryCompositionFilter.java

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.reporting;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Cohort;
import org.openmrs.api.PatientSetService.BooleanOperator;
import org.openmrs.cohort.CohortSearchHistory;
import org.openmrs.report.EvaluationContext;

/**
 * @deprecated see reportingcompatibility module
 */
@Deprecated
public class CohortHistoryCompositionFilter extends AbstractPatientFilter implements PatientFilter {
	
	protected final static Log log = LogFactory.getLog(CohortHistoryCompositionFilter.class);
	
	public static final long serialVersionUID = 6736677001L;
	
	private CohortSearchHistory history;
	
	private List<Object> parsedCompositionString;
	
	public CohortHistoryCompositionFilter() {
	}
	
	public CohortSearchHistory getHistory() {
		return history;
	}
	
	public void setHistory(CohortSearchHistory history) {
		this.history = history;
	}
	
	public List<Object> getParsedCompositionString() {
		return parsedCompositionString;
	}
	
	public void setParsedCompositionString(List<Object> parsedCompositionString) {
		this.parsedCompositionString = parsedCompositionString;
	}
	
	public String getName() {
		return nameHelper(parsedCompositionString);
	}
	
	public void setName(String name) {
	}
	
	@SuppressWarnings("unchecked")
	private String nameHelper(List list) {
		StringBuilder ret = new StringBuilder();
		for (Object o : list) {
			if (ret.length() > 0)
				ret.append(" ");
			if (o instanceof List)
				ret.append("(" + nameHelper((List) o) + ")");
			else
				ret.append(o);
		}
		return ret.toString();
	}
	
	/**
	 * Call this to notify this composition filter that the _i_th element of the search history has
	 * been removed, and it potentially needs to renumber its constituent parts
	 * 
	 * @return whether or not this filter itself should be removed (because it directly references
	 *         the removed history element
	 */
	/*
		public boolean removeFromHistoryNotify(int i) {
			return removeHelper(parsedCompositionString, i);
		}
		
		private boolean removeHelper(List<Object> list, int i) {
			boolean ret = false;
			for (ListIterator<Object> iter = list.listIterator(); iter.hasNext(); ) {
				Object o = iter.next();
				if (o instanceof List)
					ret |= removeHelper((List) o, i);
				else if (o instanceof Integer) {
					Integer ref = (Integer) o;
					if (ref == i) {
						ret = true;
						iter.set("-1");
					} else if (ref < i)
						iter.set(ref - 1);
				}
			}
			return ret;
		}
		*/

	@SuppressWarnings("unchecked")
	private PatientFilter toPatientFilter(List<Object> phrase) {
		// Recursive step:
		// * if anything in this list is a list, then recurse on that
		// * if anything in this list is a number, replace it with the relevant filter from the history
		log.debug("Starting with " + phrase);
		List<Object> use = new ArrayList<Object>();
		for (ListIterator<Object> i = phrase.listIterator(); i.hasNext();) {
			Object o = i.next();
			if (o instanceof List)
				use.add(toPatientFilter((List<Object>) o));
			else if (o instanceof Integer)
				use.add(getHistory().getSearchHistory().get((Integer) o - 1));
			else
				use.add(o);
		}
		
		// base case. All elements are PatientFilter or BooleanOperator.
		log.debug("Base case with " + use);
		
		// first, replace all [..., NOT, PatientFilter, ...] with [ ..., InvertedPatientFilter, ...]
		boolean invertTheNext = false;
		for (ListIterator<Object> i = use.listIterator(); i.hasNext();) {
			Object o = i.next();
			if (o instanceof BooleanOperator) {
				if ((BooleanOperator) o == BooleanOperator.NOT) {
					i.remove();
					invertTheNext = !invertTheNext;
				} else {
					if (invertTheNext)
						throw new RuntimeException("Can't have NOT AND. Test() should have failed");
				}
			} else {
				if (invertTheNext) {
					i.set(new InversePatientFilter((PatientFilter) o));
					invertTheNext = false;
				}
			}
		}
		
		log.debug("Finished with NOTs: " + use);
		
		// Now all we have left are PatientFilter, AND, OR
		// eventually go with left-to-right precedence, and we can combine runs of the same operator into a single one
		//     1 AND 2 AND 3 -> AND(1, 2, 3)
		//     1 AND 2 OR 3 -> OR(AND(1, 2), 3)
		// for now a hack so we take the last operator in the run, and apply that to all filters
		//     for example 1 AND 2 OR 3 -> OR(1, 2, 3)
		if (use.size() == 1) {
			return (PatientFilter) use.get(0);
		}
		BooleanOperator bo = BooleanOperator.AND;
		List<PatientFilter> args = new ArrayList<PatientFilter>();
		for (Object o : use)
			if (o instanceof BooleanOperator)
				bo = (BooleanOperator) o;
			else
				args.add((PatientFilter) o);
		
		return new CompoundPatientFilter(bo, args);
	}
	
	public PatientFilter toCohortDefinition() {
		return toPatientFilter(getParsedCompositionString());
	}
	
	public Cohort filter(Cohort input, EvaluationContext context) {
		PatientFilter pf = toPatientFilter(getParsedCompositionString());
		return pf.filter(input, context);
	}
	
	public Cohort filterInverse(Cohort input, EvaluationContext context) {
		PatientFilter pf = toPatientFilter(getParsedCompositionString());
		return pf.filterInverse(input, context);
	}
	
	public boolean isReadyToRun() {
		return true;
	}
	
}