org.openhealthtools.openatna.audit.persistence.dao.hibernate.HibernateQueryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.openhealthtools.openatna.audit.persistence.dao.hibernate.HibernateQueryBuilder.java

Source

/**
 *  Copyright (c) 2009-2011 University of Cardiff and others
 *
 *  Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  permissions and limitations under the License.
 *
 *  Contributors:
 *    University of Cardiff - initial API and implementation
 *    -
 */

package org.openhealthtools.openatna.audit.persistence.dao.hibernate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.openhealthtools.openatna.audit.persistence.model.Query;

/**
 * Builds a hibernate Criteria from a query
 * <p/>
 * NOTE: a current restriction is that the two restriction of OR and AND will only work on the same object.
 * This means for example, that
 * you cannot do an OR on, say a participant id and a source id, because these are properties of different objects,
 * but you can on participant id and participant name.
 *
 * @author Andrew Harrison
 * @version $Revision:$
 * @created Sep 13, 2009: 7:10:40 PM
 * @date $Date:$ modified by $Author:$
 */

public class HibernateQueryBuilder {

    static Log log = LogFactory
            .getLog("org.openhealthtools.openatna.audit.persistence.dao.hibernate.HibernateQueryBuilder");

    private Criteria messageCriteria;
    private Criteria idCriteria;

    public HibernateQueryBuilder(HibernateMessageDao messageDao) {
        this.messageCriteria = messageDao.criteria();
        this.idCriteria = messageDao.criteria();
    }

    private Criterion createConditional(CriteriaNode root, CriteriaNode node, Query.ConditionalStatement value,
            String name) {
        Criteria c = node.getCriteria();
        Query.Conditional con = value.getConditional();
        Object val = value.getValue();
        Criterion cron = null;

        switch (con) {
        case MAX_NUM:
            idCriteria.setMaxResults((Integer) val);
            break;
        case START_OFFSET:
            idCriteria.setFirstResult((Integer) val);
            break;
        case AFTER:
            cron = Restrictions.ge(name, val);
            break;
        case BEFORE:
            cron = Restrictions.le(name, val);
            break;
        case CASE_INSENSITIVE_LIKE:
            cron = Restrictions.ilike(name, val);
            break;
        case EQUALS:
            cron = Restrictions.eq(name, val);
            break;
        case GREATER_THAN:
            cron = Restrictions.gt(name, val);
            break;
        case GREATER_THAN_OR_EQUAL:
            cron = Restrictions.ge(name, val);
            break;
        case LESS_THAN:
            cron = Restrictions.lt(name, val);
            break;
        case LESS_THAN_OR_EQUAL:
            cron = Restrictions.le(name, val);
            break;
        case LIKE:
            cron = Restrictions.like(name, val);
            break;
        case NOT_EQUAL:
            cron = Restrictions.ne(name, val);
            break;
        case OR:
            processJoint(root, value, false);
            break;
        case AND:
            processJoint(root, value, true);
            break;
        case NULLITY:
            Boolean b = (Boolean) val;
            cron = b ? Restrictions.isNull(name) : Restrictions.isNotNull(name);
            break;
        case ASC:
            messageCriteria.addOrder(Order.asc((String) val));
            break;
        case DESC:
            messageCriteria.addOrder(Order.desc((String) val));
            break;
        default:
            break;
        }
        if (cron != null && c != null) {
            c.add(cron);
        }
        return cron;
    }

    private void processJoint(CriteriaNode root, Query.ConditionalStatement value, boolean and) {
        try {
            Query.ConditionalStatement[] values = (Query.ConditionalStatement[]) value.getValue();
            if (values == null || values.length != 2) {
                log.info("Joint takes two conditional values");
                return;
            }
            Query.TargetPath tp = Query.createPath(values[0].getTarget());
            Query.TargetPath tp1 = Query.createPath(values[1].getTarget());
            if (!tp.getPaths().equals(tp1.getPaths())) {
                throw new IllegalArgumentException(
                        "cannot perform OR on two values with a different depth, i.e. relating to a different object");
            }
            CriteriaNode cn = getNode(root, tp);
            Criterion c0 = createConditional(root, cn, values[0], tp.getTarget());

            CriteriaNode cn1 = getNode(root, tp1);
            Criterion c1 = createConditional(root, cn1, values[1], tp1.getTarget());
            if (c0 != null && c1 != null) {
                if (and) {
                    cn1.getCriteria().add(Restrictions.and(c0, c1));
                } else {
                    cn1.getCriteria().add(Restrictions.or(c0, c1));
                }
            }
        } catch (Exception e) {
            log.info(e);
        }
    }

    /**
     * NOTE: HAVE TO USE TWO QUERIES HERE. There is NO way around that I can see to ensure getting
     * back both distinct, and correct max results restrictions.
     *
     * @param query
     * @return
     */
    public Criteria build(Query query) {

        Map<Query.Target, Set<Query.ConditionalStatement>> map = query.getConditionals();

        CriteriaNode root = new CriteriaNode(idCriteria, "MESSAGE");
        idCriteria.setProjection(Projections.id());
        Set<Query.Target> targets = map.keySet();
        for (Query.Target target : targets) {
            Query.TargetPath tp = Query.createPath(target);
            CriteriaNode node = getNode(root, tp);
            Set<Query.ConditionalStatement> values = map.get(target);
            for (Query.ConditionalStatement value : values) {
                createConditional(root, node, value, tp.getTarget());
            }
        }
        idCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        idCriteria.addOrder(Order.asc("id"));
        List<Long> ids = idCriteria.list();
        if (ids == null || ids.size() == 0) {
            return null;
        }
        if (ids.size() == 1) {
            messageCriteria.add(Restrictions.eq("id", ids.get(0)));
        } else {
            messageCriteria.add(Restrictions.in("id", ids));
        }
        return messageCriteria;
    }

    public CriteriaNode getNode(CriteriaNode parent, Query.TargetPath tp) {
        List<String> paths = tp.getPaths();
        CriteriaNode dest = parent;
        for (String path : paths) {
            dest = getNode(dest, path);
        }
        return dest;
    }

    public CriteriaNode getNode(CriteriaNode parent, String path) {
        List<CriteriaNode> children = parent.getChildren();
        boolean create = true;
        CriteriaNode node = null;
        for (CriteriaNode child : children) {
            if (child.getCriteriaName().equals(path)) {
                create = false;
                node = child;
                break;
            }
        }
        if (create) {
            node = new CriteriaNode(parent.getCriteria().createCriteria(path), path);
            parent.addChild(node);
        }
        return node;
    }

    private static class CriteriaNode {

        private Criteria criteria;
        private String criteriaName;
        private List<CriteriaNode> children = new ArrayList<CriteriaNode>();

        private CriteriaNode(Criteria criteria, String criteriaName) {
            this.criteria = criteria;
            this.criteriaName = criteriaName;
        }

        public Criteria getCriteria() {
            return criteria;
        }

        public String getCriteriaName() {
            return criteriaName;
        }

        public List<CriteriaNode> getChildren() {
            return children;
        }

        public void addChild(CriteriaNode child) {
            this.children.add(child);
        }
    }

}