org.openmrs.logic.db.hibernate.HibernateLogicEncounterDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.logic.db.hibernate.HibernateLogicEncounterDAO.java

Source

/**
 * 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.logic.db.hibernate;

import static org.openmrs.logic.datasource.EncounterDataSource.ENCOUNTER_KEY;
import static org.openmrs.logic.datasource.EncounterDataSource.LOCATION_KEY;
import static org.openmrs.logic.datasource.EncounterDataSource.PROVIDER_KEY;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.openmrs.Cohort;
import org.openmrs.Encounter;
import org.openmrs.logic.LogicContext;
import org.openmrs.logic.LogicCriteria;
import org.openmrs.logic.Duration;
import org.openmrs.logic.LogicException;
import org.openmrs.logic.LogicExpression;
import org.openmrs.logic.LogicExpressionBinary;
import org.openmrs.logic.LogicTransform;
import org.openmrs.logic.db.LogicEncounterDAO;
import org.openmrs.logic.op.OperandCollection;
import org.openmrs.logic.op.OperandDate;
import org.openmrs.logic.op.OperandNumeric;
import org.openmrs.logic.op.OperandText;
import org.openmrs.logic.op.Operator;
import org.openmrs.logic.util.LogicExpressionToCriterion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
 * This class builds the hibernate statements needed to execute logic evaluations for the
 * EncounterDatasource
 */
@Repository
public class HibernateLogicEncounterDAO extends LogicExpressionToCriterion implements LogicEncounterDAO {

    protected final Log log = LogFactory.getLog(getClass());

    /**
     * Hibernate session factory
     */
    @Autowired
    private SessionFactory sessionFactory;

    /**
     * Set session factory
     * 
     * @param sessionFactory
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * Convenience method to get the list of hibernate queries for this expression
     * 
     * @param logicExpression
     * @param indexDate
     * @param criteria Criteria object so that certain expressions can add aliases, etc
     * @return Criterion to be added to the Criteria
     */
    public Criterion getCriterion(LogicExpression logicExpression, Date indexDate, Criteria criteria) {
        Operator operator = logicExpression.getOperator();
        Object rightOperand = logicExpression.getRightOperand();
        Object leftOperand = null;
        if (logicExpression instanceof LogicExpressionBinary) {
            leftOperand = ((LogicExpressionBinary) logicExpression).getLeftOperand();
        }
        List<Criterion> criterion = new ArrayList<Criterion>();

        //if the leftOperand is a String and does not match any components,
        //see if it is a concept name and restrict accordingly
        //a null operator implies a concept restriction
        if (leftOperand instanceof LogicExpression) {
            // no restrictions if there is no operator
            // TODO restrict on provider != null for encounterProvider token?
        }

        String token = logicExpression.getRootToken();

        if (operator == null) {
            // no restrictions if there is no operator
            // TODO restrict on provider != null for encounterProvider token?
        } else if (operator == Operator.BEFORE || operator == Operator.LT) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandDate) {
                criterion.add(Restrictions.lt("encounterDatetime", rightOperand));
            } else {
                throw new LogicException("'before' is not a valid operator on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.AFTER || operator == Operator.GT) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandDate) {
                criterion.add(Restrictions.gt("encounterDatetime", rightOperand));
            } else {
                throw new LogicException("'after' is not a valid operator on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.AND || operator == Operator.OR) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandDate) {
                Criterion leftCriteria = null;
                Criterion rightCriteria = null;
                if (leftOperand instanceof LogicExpression) {
                    leftCriteria = this.getCriterion((LogicExpression) leftOperand, indexDate, criteria);
                }
                if (rightOperand instanceof LogicExpression) {
                    rightCriteria = this.getCriterion((LogicExpression) rightOperand, indexDate, criteria);
                }

                if (leftCriteria != null && rightCriteria != null) {
                    if (operator == Operator.AND) {
                        criterion.add(Restrictions.and(leftCriteria, rightCriteria));
                    }
                    if (operator == Operator.OR) {
                        criterion.add(Restrictions.or(leftCriteria, rightCriteria));
                    }
                }
            } else {
                throw new LogicException("'and/or' are not valid operators on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.NOT) {

            Criterion rightCriteria = null;

            if (rightOperand instanceof LogicExpression) {
                rightCriteria = this.getCriterion((LogicExpression) rightOperand, indexDate, criteria);
            }

            if (rightCriteria != null) {
                criterion.add(Restrictions.not(rightCriteria));
            }

        } else if (operator == Operator.CONTAINS) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandText) {
                criteria.createAlias("encounterType", "encounterType");
                criterion.add(Expression.eq("encounterType.name", ((OperandText) rightOperand).asString()));
            } else if (LOCATION_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandText) {
                criteria.createAlias("location", "location");
                criterion.add(Restrictions.eq("location.name", ((OperandText) rightOperand).asString()));
            } else if (PROVIDER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandNumeric) {
                criteria.createAlias("provider", "provider");
                criterion.add(Restrictions.eq("provider.personId", ((OperandNumeric) rightOperand).asInteger()));
            } else {
                throw new LogicException("'contains' is not a valid operator on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.IN) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandCollection) {
                criteria.createAlias("encounterType", "encounterType");
                criterion.add(
                        Expression.in("encounterType.name", ((OperandCollection) rightOperand).asCollection()));
            } else if (LOCATION_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandCollection) {
                criteria.createAlias("location", "location");
                criterion.add(Restrictions.in("location.name", ((OperandCollection) rightOperand).asCollection()));
            } else if (PROVIDER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandCollection) {
                criteria.createAlias("provider", "provider");
                criterion.add(
                        Restrictions.in("provider.systemId", ((OperandCollection) rightOperand).asCollection()));
            } else {
                throw new LogicException("'in' is not a valid operator on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.EQUALS) {
            if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandDate) {
                criterion.add(Restrictions.eq("encounterDatetime", rightOperand));
            } else if (ENCOUNTER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandText) {
                criteria.createAlias("encounterType", "encounterType");
                criterion.add(Restrictions.eq("encounterType.name", ((OperandText) rightOperand).asString()));
            } else if (LOCATION_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandText) {
                criteria.createAlias("location", "location");
                criterion.add(Restrictions.eq("location.name", ((OperandText) rightOperand).asString()));
            } else if (PROVIDER_KEY.equalsIgnoreCase(token) && rightOperand instanceof OperandText) {
                criteria.createAlias("provider", "provider");
                criterion.add(Restrictions.eq("provider.systemId", ((OperandText) rightOperand).asString()));
            } else {
                throw new LogicException("'equals' is not a valid operator on " + token + " and " + rightOperand);
            }
        } else if (operator == Operator.LTE) {
            if (rightOperand instanceof OperandDate)
                criterion.add(Restrictions.le("encounterDatetime", rightOperand));
            else
                throw new LogicException(
                        "'less than or equals' is not a valid operator on " + token + " and " + rightOperand);
        } else if (operator == Operator.GTE) {
            if (rightOperand instanceof OperandDate)
                criterion.add(Restrictions.ge("encounterDatetime", rightOperand));
            else
                throw new LogicException(
                        "'greater than or equals' is not a valid operator on " + token + " and " + rightOperand);
        } else if (operator == Operator.LT) {
            if (rightOperand instanceof OperandDate)
                criterion.add(Restrictions.lt("encounterDatetime", rightOperand));
            else
                throw new LogicException(
                        "'less than' is not a valid operator on " + token + " and " + rightOperand);

        } else if (operator == Operator.GT) {
            if (rightOperand instanceof OperandDate)
                criterion.add(Restrictions.gt("encounterDatetime", rightOperand));
            else
                throw new LogicException(
                        "'greater than' is not a valid operator on " + token + " and " + rightOperand);

        } else if (operator == Operator.EXISTS) {
            // EXISTS can be handled on the higher level (above
            // LogicService, even) by coercing the Result into a Boolean for
            // each patient
        } else if (operator == Operator.ASOF && rightOperand instanceof Date) {
            indexDate = (Date) rightOperand;
            criterion.add(Restrictions.le("encounterDatetime", indexDate));

        } else if (operator == Operator.WITHIN) {
            if (rightOperand instanceof Duration) {
                Duration duration = (Duration) rightOperand;
                Calendar within = Calendar.getInstance();
                within.setTime(indexDate);

                if (duration.getUnits() == Duration.Units.YEARS) {
                    within.add(Calendar.YEAR, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.MONTHS) {
                    within.add(Calendar.MONTH, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.WEEKS) {
                    within.add(Calendar.WEEK_OF_YEAR, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.DAYS) {
                    within.add(Calendar.DAY_OF_YEAR, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.HOURS) {
                    within.add(Calendar.HOUR_OF_DAY, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.MINUTES) {
                    within.add(Calendar.MINUTE, duration.getDuration().intValue());
                } else if (duration.getUnits() == Duration.Units.SECONDS) {
                    within.add(Calendar.SECOND, duration.getDuration().intValue());
                }

                if (indexDate.compareTo(within.getTime()) > 0) {
                    criterion.add(Restrictions.between("encounterDatetime", within.getTime(), indexDate));
                } else {
                    criterion.add(Restrictions.between("encounterDatetime", indexDate, within.getTime()));
                }
            } else {
                throw new LogicException("'within' is not a valid operator on " + token + " and " + rightOperand);
            }
        }

        Criterion c = null;

        for (Criterion crit : criterion) {
            if (c == null) {
                c = crit;
            } else {
                c = Restrictions.and(c, crit);
            }
        }
        return c;
    }

    // Helper function, converts logic service's criteria into Hibernate's
    // criteria
    @SuppressWarnings("unchecked")
    private List<Encounter> logicToHibernate(LogicExpression expression, Cohort who, LogicContext logicContext)
            throws LogicException {
        Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class);

        Date indexDate = logicContext.getIndexDate();
        Operator transformOperator = null;
        LogicTransform transform = expression.getTransform();
        Integer numResults = null;

        if (transform != null) {
            transformOperator = transform.getTransformOperator();
            numResults = transform.getNumResults();
        }

        if (numResults == null) {
            numResults = 1;
        }

        // set the transform and evaluate the right criteria
        // if there is any
        if (transformOperator == Operator.LAST) {
            criteria.addOrder(Order.desc("encounterDatetime")).addOrder(Order.desc("dateCreated"))
                    .addOrder(Order.desc("encounterId"));
        } else if (transformOperator == Operator.FIRST) {
            criteria.addOrder(Order.asc("encounterDatetime")).addOrder(Order.asc("encounterId"));
        } else if (transformOperator == Operator.DISTINCT) {
            criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        } else {
            criteria.addOrder(Order.desc("encounterDatetime"));
        }

        Criterion c = this.getCriterion(expression, indexDate, criteria);
        if (c != null) {
            criteria.add(c);
        }

        List<Encounter> results = new ArrayList<Encounter>();

        criteria.add(Restrictions.eq("voided", false));
        criteria.add(Restrictions.in("patient.personId", who.getMemberIds()));
        results.addAll(criteria.list());

        //return a single result per patient for these operators
        //I don't see an easy way to do this in hibernate so I am
        //doing some postprocessing
        if (transformOperator == Operator.FIRST || transformOperator == Operator.LAST) {
            HashMap<Integer, ArrayList<Encounter>> nResultMap = new HashMap<Integer, ArrayList<Encounter>>();

            for (Encounter currResult : results) {
                Integer currPersonId = currResult.getPatient().getPersonId();
                ArrayList<Encounter> prevResults = nResultMap.get(currPersonId);
                if (prevResults == null) {
                    prevResults = new ArrayList<Encounter>();
                    nResultMap.put(currPersonId, prevResults);
                }

                if (prevResults.size() < numResults) {
                    prevResults.add(currResult);
                }
            }

            if (nResultMap.values().size() > 0) {
                results.clear();

                for (ArrayList<Encounter> currPatientEncounter : nResultMap.values()) {
                    results.addAll(currPatientEncounter);
                }
            }
        }
        return results;
    }

    /**
     * @see org.openmrs.api.db.EncounterDAO#getEncounters(org.openmrs.Patient, org.openmrs.Location,
     *      Date, Date, java.util.Collection, java.util.Collection, java.util.Collection, boolean)
     */
    public List<Encounter> getEncounters(Cohort who, LogicCriteria logicCriteria, LogicContext logicContext)
            throws LogicException {
        return logicToHibernate(logicCriteria.getExpression(), who, logicContext);
    }

}