ConceptAnswersEditor.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.propertyeditor;

import java.beans.PropertyEditorSupport;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.Drug;
import org.openmrs.api.ConceptService;
import org.openmrs.api.context.Context;
import org.springframework.util.StringUtils;

/**
 * Handles lists of conceptids that correspond to answers.
 */
public class ConceptAnswersEditor extends PropertyEditorSupport {
	
	private Log log = LogFactory.getLog(this.getClass());
	
	private Collection<ConceptAnswer> originalConceptAnswers = null;
	
	/**
	 * Default constructor taking in the original answers. This should be the actual list on the
	 * pojo object to prevent hibernate errors later on.
	 * 
	 * @param originalAnswers the list on the pojo
	 */
	public ConceptAnswersEditor(Collection<ConceptAnswer> originalAnswers) {
		if (originalAnswers == null)
			originalConceptAnswers = new HashSet<ConceptAnswer>();
		else
			originalConceptAnswers = originalAnswers;
	}
	
	/**
	 * loops over the textbox assigned to this property. The textbox is assumed to be a string of
	 * conceptIds^drugIds separated by spaces.
	 * 
	 * @param text list of conceptIds (not conceptAnswerIds)
	 * @should set the sort weights with the least possible changes
	 */
	public void setAsText(String text) throws IllegalArgumentException {
		if (StringUtils.hasText(text)) {
			ConceptService cs = Context.getConceptService();
			String[] conceptIds = text.split(" ");
			List<String> requestConceptIds = new Vector<String>();
			//set up parameter answer Set for easier add/delete functions and removal of duplicates
			for (String id : conceptIds) {
				id = id.trim();
				if (!id.equals("") && !requestConceptIds.contains(id)) //remove whitespace, blank lines, and duplicates
					requestConceptIds.add(id);
			}
			
			Collection<ConceptAnswer> deletedConceptAnswers = new HashSet<ConceptAnswer>();
			
			// loop over original concept answers to find any deleted answers
			for (ConceptAnswer origConceptAnswer : originalConceptAnswers) {
				boolean answerDeleted = true;
				for (String conceptId : requestConceptIds) {
					Integer id = getConceptId(conceptId);
					Integer drugId = getDrugId(conceptId);
					Drug answerDrug = origConceptAnswer.getAnswerDrug();
					if (id.equals(origConceptAnswer.getAnswerConcept().getConceptId())) {
						if (drugId == null && answerDrug == null)
							answerDeleted = false;
						else if ((drugId != null && answerDrug != null)
						        && drugId.equals(origConceptAnswer.getAnswerDrug().getDrugId()))
							answerDeleted = false;
					}
				}
				if (answerDeleted)
					deletedConceptAnswers.add(origConceptAnswer);
			}
			
			// loop over those deleted answers to delete them
			for (ConceptAnswer conceptAnswer : deletedConceptAnswers) {
				originalConceptAnswers.remove(conceptAnswer);
			}
			
			// loop over concept ids in the request to add any that are new
			for (String conceptId : requestConceptIds) {
				Integer id = getConceptId(conceptId);
				Integer drugId = getDrugId(conceptId);
				boolean newAnswerConcept = true;
				for (ConceptAnswer origConceptAnswer : originalConceptAnswers) {
					Drug answerDrug = origConceptAnswer.getAnswerDrug();
					if (id.equals(origConceptAnswer.getAnswerConcept().getConceptId())) {
						if (drugId == null && answerDrug == null)
							newAnswerConcept = false;
						else if ((drugId != null && answerDrug != null) && drugId.equals(answerDrug.getDrugId()))
							newAnswerConcept = false;
					}
				}
				// if the current request answer is new, add it to the originals
				if (newAnswerConcept) {
					Concept answer = cs.getConcept(id);
					Drug drug = null;
					if (drugId != null)
						drug = cs.getDrug(drugId);
					ConceptAnswer ac = new ConceptAnswer(answer, drug);
					originalConceptAnswers.add(ac);
				}
			}
			
			//loop over to set the order
			//as the list comes into 'requestConceptIds' in the order the user wants
			//  there are 2 conditions that will require the sort_weights to be reassigned
			//    1) any ConceptAnswer.sortWeight == NULL (meaning it is just added)
			//    2) the list is not in ASCENDING order (example sort order of the list is 1, 2, 10, 9)
			//  -startIdx (start index) is where in this list we will start to reassign the sort_weights
			Double lastWeightSeen = null;
			int startIdx = -1;//the idx to start at, if we have a NULL sort weight (new concept answer) or sort weights are not ascending
			for (int i = 0; i < requestConceptIds.size() - 1; i++) {
				Integer id1 = getConceptId(requestConceptIds.get(i));
				ConceptAnswer ca1 = getConceptAnswerFromOriginal(id1);
				
				if (ca1.getSortWeight() == null) {
					if (lastWeightSeen == null) {
						//start at 1, we're at the beginning
						lastWeightSeen = 1d;
					} else {
						//we start at +1
						lastWeightSeen += 1;
					}
					startIdx = i;
					break;
				}
				
				Integer id2 = getConceptId(requestConceptIds.get(i + 1));
				ConceptAnswer ca2 = getConceptAnswerFromOriginal(id2);
				int c = ca1.compareTo(ca2);
				if (c > 0) {
					startIdx = i;
					lastWeightSeen = ca1.getSortWeight();
					break;
				}
				
				lastWeightSeen = ca1.getSortWeight();
			}
			
			if (startIdx != -1) {
				//then we need to re-weight
				for (int i = startIdx; i < requestConceptIds.size(); i++) {
					Integer id = getConceptId(requestConceptIds.get(i));
					ConceptAnswer ca = getConceptAnswerFromOriginal(id);
					ca.setSortWeight(lastWeightSeen++);
				}
			}
			
			log.debug("originalConceptAnswers.getConceptId(): ");
			for (ConceptAnswer a : originalConceptAnswers)
				log.debug("id: " + a.getAnswerConcept().getConceptId());
			
			log.debug("requestConceptIds: ");
			for (String i : requestConceptIds)
				log.debug("id: " + i);
		} else {
			originalConceptAnswers.clear();
		}
		
		setValue(originalConceptAnswers);
	}
	
	/**
	 * find this conceptId in the original set and set its weight
	 */
	private ConceptAnswer getConceptAnswerFromOriginal(Integer id) {
		for (ConceptAnswer ca : originalConceptAnswers) {
			if (ca.getAnswerConcept().getConceptId().equals(id)) {
				return ca;
			}
		}
		return null;
	}
	
	/**
	 * Parses the string and returns the Integer concept id Expected string: "123" or "123^34"
	 * ("conceptId^drugId")
	 * 
	 * @param conceptId
	 * @return
	 */
	private Integer getConceptId(String conceptId) {
		if (conceptId.contains("^"))
			return Integer.valueOf(conceptId.substring(0, conceptId.indexOf("^")));
		else
			return Integer.valueOf(conceptId);
	}
	
	/**
	 * Parses the string and returns the Integer drug id or null if none Expected string: "123" or
	 * "123^34" ("conceptId^drugId")
	 * 
	 * @param conceptId
	 * @return
	 */
	private Integer getDrugId(String conceptId) {
		if (conceptId.contains("^"))
			return Integer.valueOf(conceptId.substring(conceptId.indexOf("^") + 1, conceptId.length()));
		
		return null;
	}
	
}