org.geolatte.common.cql.hibernate.HibernateCriteriaBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.geolatte.common.cql.hibernate.HibernateCriteriaBuilder.java

Source

/*
 * This file is part of the GeoLatte project.
 *
 *     GeoLatte is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     GeoLatte is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with GeoLatte.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2010 - 2010 and Ownership of code is shared by:
 * Qmino bvba - Romeinsestraat 18 - 3001 Heverlee  (http://www.qmino.com)
 * Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com)
 */

package org.geolatte.common.cql.hibernate;

import org.geolatte.common.reflection.EntityClassReader;
import org.geolatte.common.cql.AbstractBuilder;
import org.geolatte.common.cql.node.*;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import java.util.*;

/**
 * <p>
 * Treewalker that builds a Hibernate Criteria based on a given CQL AST. Use as follows:
 * <pre>
 * {@code
 * HibernateCriteriaBuilder builder = new HibernateCriteriaBuilder(clazz); // builder for the given class
 * tree.apply(builder); // with tree, the root element of the AST as returned by the parser.
 * }
 * </pre>
 *
 * </p>
 * <p>
 * <i>Creation-Date</i>: 31-May-2010<br>
 * <i>Creation-Time</i>:  09:47:51<br>
 * </p>
 *
 * @author Bert Vanhooff
 * @author <a href="http://www.qmino.com">Qmino bvba</a>
 * @since SDK1.5
 */
class HibernateCriteriaBuilder extends AbstractBuilder {

    // The criteria that is built incrementally by walking the tree
    private DetachedCriteria criteria;
    EntityClassReader reader;

    // A map of all translated nodes as they are visited depth first.
    private HashMap<Node, Criterion> translatedExpressions = new HashMap<Node, Criterion>();

    public HibernateCriteriaBuilder(Class clazz) {
        criteria = DetachedCriteria.forClass(clazz);
        reader = EntityClassReader.getClassReaderFor(clazz);
    }

    public DetachedCriteria getCriteria() {
        return criteria;
    }

    @Override
    public void caseStart(Start node) {

        node.getPExpr().apply(this);

        criteria.add(translatedExpressions.get(node.getPExpr()));
    }

    @Override
    public void outAAndExpr(AAndExpr node) {

        translatedExpressions.put(node, Restrictions.and(translatedExpressions.get(node.getLeft()),
                translatedExpressions.get(node.getRight())));
    }

    @Override
    public void outAOrExpr(AOrExpr node) {

        translatedExpressions.put(node, Restrictions.or(translatedExpressions.get(node.getLeft()),
                translatedExpressions.get(node.getRight())));
    }

    @Override
    public void outANotExpr(ANotExpr node) {

        translatedExpressions.put(node, Restrictions.not(translatedExpressions.get(node.getExpr())));
    }

    @Override
    public void outAGtExpr(AGtExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.gt(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outAGteExpr(AGteExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.ge(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outALtExpr(ALtExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.lt(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outALteExpr(ALteExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.le(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outAEqExpr(AEqExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.eq(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outANeqExpr(ANeqExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        translatedExpressions.put(node, Restrictions.ne(propertyAlias, reader.parseAsPropertyType(
                translatedLiterals.get(node.getRight()).toString(), getPropertyPath(node.getLeft()))));
    }

    @Override
    public void outALikeExpr(ALikeExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        EscapingLikeExpression likeExpression = new EscapingLikeExpression(propertyAlias,
                translatedLiterals.get(node.getRight()).toString());
        translatedExpressions.put(node, likeExpression);
    }

    @Override
    public void outANotLikeExpr(ANotLikeExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        EscapingLikeExpression likeExpression = new EscapingLikeExpression(propertyAlias,
                translatedLiterals.get(node.getRight()).toString());
        translatedExpressions.put(node, Restrictions.not(likeExpression));
    }

    @Override
    public void outAIlikeExpr(AIlikeExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        EscapingLikeExpression likeExpression = new EscapingLikeExpression(propertyAlias,
                translatedLiterals.get(node.getRight()).toString(), true);
        translatedExpressions.put(node, likeExpression);
    }

    @Override
    public void outANotIlikeExpr(ANotIlikeExpr node) {

        String propertyAlias = createAlias(node.getLeft());
        EscapingLikeExpression likeExpression = new EscapingLikeExpression(propertyAlias,
                translatedLiterals.get(node.getRight()).toString(), true);
        translatedExpressions.put(node, Restrictions.not(likeExpression));
    }

    @Override
    public void outAIsNullExpr(AIsNullExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node, Restrictions.isNull(propertyAlias));
    }

    @Override
    public void outAIsNotNullExpr(AIsNotNullExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node, Restrictions.isNotNull(propertyAlias));
    }

    @Override
    public void outAExistsExpr(AExistsExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node, new PropertyExistsCriterion(propertyAlias));
    }

    @Override
    public void outADoesNotExistExpr(ADoesNotExistExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node, new PropertyDoesNotExistCriterion(propertyAlias));
    }

    @Override
    public void outABeforeExpr(ABeforeExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node,
                Restrictions.lt(propertyAlias, parseDate(node.getDateTime().toString().trim())));
    }

    @Override
    public void outAAfterExpr(AAfterExpr node) {

        String propertyAlias = createAlias(node.getAttr());
        translatedExpressions.put(node,
                Restrictions.gt(propertyAlias, parseDate(node.getDateTime().toString().trim())));
    }

    @Override
    public void outADuringExpr(ADuringExpr node) {

        PTimespanLiteral timespan = node.getTimeSpan();

        Criterion greaterThan;
        Criterion lowerThan;

        if (timespan instanceof AFromToTimespanLiteral) {

            AFromToTimespanLiteral fromToTimespan = (AFromToTimespanLiteral) timespan;
            greaterThan = Restrictions.gt(node.getAttr().toString().trim(),
                    parseDate(fromToTimespan.getFrom().getText().trim()));
            lowerThan = Restrictions.lt(node.getAttr().toString().trim(),
                    parseDate(fromToTimespan.getTo().getText().trim()));
        } else if (timespan instanceof AFromDurationTimespanLiteral) {

            AFromDurationTimespanLiteral fromDurationTimespan = (AFromDurationTimespanLiteral) timespan;
            Date fromDate = parseDate(fromDurationTimespan.getFrom().getText().trim());
            Duration duration = (Duration) translatedLiterals.get(fromDurationTimespan.getDuration());

            // Calculate to 'to' date
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(fromDate);
            calendar.add(Calendar.YEAR, duration.getYears());
            calendar.add(Calendar.MONTH, duration.getMonths());
            calendar.add(Calendar.DATE, duration.getDays());
            calendar.add(Calendar.HOUR, duration.getHours());
            calendar.add(Calendar.MINUTE, duration.getMinutes());
            calendar.add(Calendar.SECOND, duration.getSeconds());
            Date toDate = calendar.getTime();

            greaterThan = Restrictions.gt(node.getAttr().toString().trim(), fromDate);
            lowerThan = Restrictions.lt(node.getAttr().toString().trim(), toDate);

        } else { // if (timespan instanceof ADurationToTimespanLiteral)

            greaterThan = null;
            lowerThan = null;
        }

        Criterion combined = Restrictions.and(greaterThan, lowerThan);
        translatedExpressions.put(node, combined);
    }

    // List of property paths for which aliasses are created.
    Map<String, String> createdAliasses = new HashMap<String, String>();

    /**
     * Creates an alias for the property's parent if necessary (the property is compound)
     * E.g. for a property path "address.street.number", an alias is created for "address.street", which can be used to
     * access the number property.
     *
     * @param attr The attribute/property node to create an alias for.
     * @return The aliased Property name
     */
    private String createAlias(PAttr attr) {

        // This is a simple property -> should not create an alias
        if (attr instanceof AIdAttr)
            return ((AIdAttr) attr).getIdentifier().getText().trim();

        // In the other case, we have a compound property -> must create an alias (or reuse an existing one) and return the alias name to the property path

        // Loop through all parts and create aliasses along the way.
        List<String> propertyParts = getPropertyParts(attr);
        String currentPropertyPath = ""; // the current property path
        String currentAlias = "";
        for (int i = 0; i < propertyParts.size() - 1; i++) { // last part is the final property itself.. must not create an alias for that one

            String currentPropertyPart = propertyParts.get(i);
            currentPropertyPath += currentPropertyPart;

            if (createdAliasses.containsKey(currentPropertyPath)) // already have an alias
                currentAlias = createdAliasses.get(currentPropertyPath);
            else {

                String newAlias = currentAlias + currentPropertyPart + "01";
                criteria = criteria.createAlias(
                        (currentAlias.length() == 0 ? "" : (currentAlias + ".")) + currentPropertyPart, newAlias);
                createdAliasses.put(currentPropertyPath, newAlias);
                currentAlias = newAlias;
            }

            currentPropertyPath += ".";
        }

        String lastPropertyPart = propertyParts.get(propertyParts.size() - 1); // Last (missing) part of the property path.
        return currentAlias.length() == 0 ? lastPropertyPart : currentAlias + "." + lastPropertyPart;
    }
}