org.easy.criteria.CriteriaComposer.java Source code

Java tutorial

Introduction

Here is the source code for org.easy.criteria.CriteriaComposer.java

Source

/**
 * Copyright  2011 Aftab Mahmood
 * 
 * This program 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
 * any later version.
 * 
 * This program 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 General Public License for more details <http://www.gnu.org/licenses/>.
 **/
package org.easy.criteria;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.common.base.Preconditions;

/**
 * Encapsulates all the information that may required to search entity(ies) or
 * tuple(s). This includes columns to select for Tuple, attributes to match in
 * where clause, _joinContainer order by, having, and group by attributes. All
 * this information is provided in form of metadata which makes this criteria
 * composer type safe.
 * 
 * <pre>
 * Example 1: 
 *  
 *  SQL : select person.name, sum(course.unit) from person inner join course_session inner join course group by person.name
 * 
 * Using this framework: 
 *     CriteriaComposer<Person> forStudentUnitCount = CriteriaComposer.createComposer(Person.class).select(Person_.name).groupBy(Person_.name);
 *     forStudentUnitCount.join(Person_.courseSessions).join(CourseSession_.course).select(AggregateFunction.SUM, Course_.unit);
 *      List<Tuple>  result = criteriaProcessor.findAllTuple(forStudentUnitCount);
 *      
 * Example 2:
 * 
 *    SQL: select * from course where course.last_update_date_time between ? and ?
 * 
 *       CriteriaComposer<Course> courseCriteria = new CriteriaComposer<Course>(Course.class);
 *        criteria.where(Course_.lastUpdateDateTime, ComparisonOperator.BETWEEN, thatDate, toDate);
 * 
 *    List<Course> result = criteriaProcessor.findAllEntity(courseCriteria);
 * </pre>
 * 
 * @author mahmood.aftab
 * 
 * @param <E>
 *            - The root entity for this criteria.
 */
public class CriteriaComposer<E> {
    public enum AggregateFunction {
        COUNT, AVG, SUM, MIN, MAX
    }

    public enum ComparisonOperator {
        EQUAL, LIKE, IN, BETWEEN, IS_NULL, LESS_THAN, LESS_THAN_EQUAL, GREATER_THAN, GREATER_THAN_EQUAL
    };

    private enum RangOperator {
        IN, BETWEEN
    }; //TBD remove if not needed

    private enum LogicOperator {
        AND, OR, NONE
    }

    public enum NegationOperator {
        NOT
    }

    private enum LastCallType {
        WHERE, HAVING
    };

    private LastCallType lastCallType = null;

    private Class<E> _entityClass;

    @SuppressWarnings("rawtypes")
    private From root;
    private List<WhereContainer<E>> _wheres = new ArrayList<WhereContainer<E>>(0);
    private Map<JoinContainer<E>, CriteriaComposer<?>> _joins = new LinkedHashMap<JoinContainer<E>, CriteriaComposer<?>>(
            0);
    private List<SelectContainer<E>> _selects = new ArrayList<SelectContainer<E>>(0);;
    private List<OrderByContainer<E>> _orderBys = new ArrayList<OrderByContainer<E>>(0);
    private List<GroupByContainer<E>> _groupBys = new ArrayList<GroupByContainer<E>>(0);
    private List<HavingContainer<E>> _havings = new ArrayList<HavingContainer<E>>(0);
    private List<Selection<?>> multiselect = new ArrayList<Selection<?>>(0);

    public void merge(CriteriaComposer<E> other) {
        this._joins.putAll(other._joins);
        this._selects.addAll(other._selects);
        this._orderBys.addAll(other._orderBys);
        this._groupBys.addAll(other._groupBys);
        this._havings.addAll(other._havings);
        this._wheres.addAll(other._wheres);
    }

    protected <V> Predicate getPredicate(SingularAttribute<E, V> attribute) {
        Preconditions.checkState(root != null, "root is null. Either join is not defined or not generated.");
        //TBD

        return null;
    }

    /**
     * Calls CriteriaBuilder's API for the respective operator. Add new case if
     * you add a new operator.
     * 
     * @author mahmood.aftab
     * 
     */
    private static class ComparisonOperatorProcessor {
        protected static Predicate negate(final CriteriaBuilder criteriaBuilder, final Predicate predicate) {
            return criteriaBuilder.not(predicate);
        }

        @SuppressWarnings("rawtypes")
        private static Predicate processSubquery(final CriteriaBuilder criteriaBuilder, final Expression attribute,
                final ComparisonOperator cOperator, final CriteriaComposer<?> value) {
            return null;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private static Predicate processParameter(final CriteriaBuilder criteriaBuilder, final Expression attribute,
                final ComparisonOperator cOperator, final Object value) {
            Predicate out;
            List<?> values;

            switch (cOperator) {
            case EQUAL:
                out = criteriaBuilder.equal(attribute, value);
                break;

            case IN:
                Preconditions.checkState(value instanceof java.util.List,
                        "Value for IN opeations should be of type List");
                values = (List<?>) value;
                Preconditions.checkArgument(values.size() > 0);

                out = attribute.in(values.toArray());
                break;

            case LIKE:
                out = criteriaBuilder.like(attribute, value.toString());
                break;

            case BETWEEN:
                Preconditions.checkState(value instanceof java.util.List,
                        "Values for between opeations should be of type Array");
                values = (List<?>) value;
                Preconditions.checkArgument(values.size() == 2,
                        "BETWEEN operation cannot be applied to more than two values.");
                Preconditions.checkState(values.get(0) instanceof java.lang.Comparable,
                        "Values for between opeations should implement Compareable");

                out = criteriaBuilder.between(attribute, (java.lang.Comparable) values.get(0),
                        (java.lang.Comparable) values.get(1));
                break;

            case IS_NULL:
                out = criteriaBuilder.isNull(attribute);
                break;

            case GREATER_THAN:
                Preconditions.checkState(value instanceof java.lang.Comparable,
                        "Value for GREATER_THAN opeations should implement Compareable");
                out = criteriaBuilder.greaterThan(attribute, (java.lang.Comparable) value);
                break;

            case GREATER_THAN_EQUAL:
                Preconditions.checkState(value instanceof java.lang.Comparable,
                        "Value for GREATER_THAN opeations should implement Compareable");
                out = criteriaBuilder.greaterThanOrEqualTo(attribute, (java.lang.Comparable) value);
                break;

            case LESS_THAN:
                Preconditions.checkState(value instanceof java.lang.Comparable,
                        "Value for GREATER_THAN opeations should implement Compareable");
                out = criteriaBuilder.lessThan(attribute, (java.lang.Comparable) value);
                break;

            case LESS_THAN_EQUAL:
                Preconditions.checkState(value instanceof java.lang.Comparable,
                        "Value for GREATER_THAN opeations should implement Compareable");
                out = criteriaBuilder.lessThanOrEqualTo(attribute, (java.lang.Comparable) value);
                break;

            default:
                throw new java.lang.IllegalStateException("Unknown operator found " + cOperator.toString());
            }

            return out;
        }

        @SuppressWarnings({ "rawtypes" })
        protected static Predicate process(final CriteriaBuilder criteriaBuilder, final Expression attribute,
                final ComparisonOperator cOperator, final Object value) {
            if (value instanceof CriteriaComposer)
                return processSubquery(criteriaBuilder, attribute, cOperator, (CriteriaComposer<?>) value);
            else
                return processParameter(criteriaBuilder, attribute, cOperator, value);
        }
    }

    /**
     * @author mahmood.aftab
     * 
     *         SELECT column_name, aggregate_function(column_name) FROM
     *         table_name WHERE column_name operator value GROUP BY column_name
     */
    private class GroupByContainer<T> {
        SingularAttribute<? super T, ?> attribute;
        Integer rank;

        public GroupByContainer(SingularAttribute<? super T, ?> attribute, int rank) {
            this.attribute = attribute;
            this.rank = rank;
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            GroupByContainer<T> other = (GroupByContainer<T>) obj;
            if (attribute == null) {
                if (other.attribute != null)
                    return false;
            } else if (!attribute.equals(other.attribute))
                return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((attribute == null) ? 0 : attribute.hashCode());
            result = prime * result + ((rank == null) ? 0 : rank.hashCode());
            return result;
        }

        @Override
        public String toString() {
            return "GroupByContainer [attribute=" + attribute + ", rank=" + rank + "]";
        }
    }

    /**
     * Stores information for a single having clause.
     * 
     * @param <T>
     */
    private class HavingContainer<T> {
        AggregateFunction function = null;
        ComparisonOperator comparisionOperator = ComparisonOperator.EQUAL;
        LogicOperator logicOperator = LogicOperator.NONE;
        NegationOperator notOperator = null;
        Object value = null;
        SingularAttribute<? super T, ?> attribute = null;

        <V> HavingContainer(AggregateFunction function, SingularAttribute<? super T, V> attribute,
                ComparisonOperator comparisionOperator, V value) {
            this(function, attribute, null, comparisionOperator, value);
        }

        <V> HavingContainer(AggregateFunction function, SingularAttribute<? super T, V> attribute,
                NegationOperator notOperator, ComparisonOperator comparisionOperator, V value) {
            this.function = function;
            this.comparisionOperator = comparisionOperator;
            this.notOperator = notOperator;
            this.value = value;
            this.attribute = attribute;
        }

        @Override
        public String toString() {
            return "HavingContainer [function=" + function + ", comparisionOperator=" + comparisionOperator
                    + ", logicOperator=" + logicOperator + ", notOperator=" + notOperator + ", value=" + value
                    + ", attribute=" + attribute + "]";
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + getOuterType().hashCode();
            result = prime * result + ((function == null) ? 0 : function.hashCode());
            result = prime * result + ((value == null) ? 0 : value.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            HavingContainer other = (HavingContainer) obj;
            if (!getOuterType().equals(other.getOuterType()))
                return false;
            if (function != other.function)
                return false;
            if (value == null) {
                if (other.value != null)
                    return false;
            } else if (!value.equals(other.value))
                return false;
            return true;
        }

        private CriteriaComposer getOuterType() {
            return CriteriaComposer.this;
        }
    }

    /**
     * Internal class that contains attributes for Joins.
     * 
     * @author mahmood.aftab
     * 
     * @param <T>
     */
    private class JoinContainer<T> {
        JoinType joinType;
        SingularAttribute<? super T, ?> singularAttribute;
        SetAttribute<? super T, ?> setAttribute;

        JoinContainer(JoinType joinType, SetAttribute<? super T, ?> setAttribute) {
            this.joinType = joinType;
            this.setAttribute = setAttribute;
        }

        JoinContainer(JoinType joinType, SingularAttribute<? super T, ?> singularAttribute) {
            this.singularAttribute = singularAttribute;
            this.joinType = joinType;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            JoinContainer<T> other = (JoinContainer) obj;
            if (setAttribute == null) {
                if (other.setAttribute != null)
                    return false;
            } else if (!setAttribute.equals(other.setAttribute))
                return false;
            if (singularAttribute == null) {
                if (other.singularAttribute != null)
                    return false;
            } else if (!singularAttribute.equals(other.singularAttribute))
                return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((setAttribute == null) ? 0 : setAttribute.hashCode());
            result = prime * result + ((singularAttribute == null) ? 0 : singularAttribute.hashCode());
            return result;
        }

        @Override
        public String toString() {
            return "JoinContainer [joinType=" + joinType + ", singularAttribute=" + singularAttribute
                    + ", setAttribute=" + setAttribute + "]";
        }
    }

    /**
     * @author mahmood.aftab
     * 
     */
    private class OrderByContainer<T> {
        SingularAttribute<? super T, ?> attribute;
        boolean ascending;
        Integer rank;

        OrderByContainer(SingularAttribute<? super T, ?> attribute, boolean ascending, int rank) {
            this.attribute = attribute;
            this.ascending = ascending;
            this.rank = rank;
        }

        @SuppressWarnings("rawtypes")
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            OrderByContainer other = (OrderByContainer) obj;
            if (attribute == null) {
                if (other.attribute != null)
                    return false;
            } else if (!attribute.equals(other.attribute))
                return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + (ascending ? 1231 : 1237);
            result = prime * result + ((attribute == null) ? 0 : attribute.hashCode());
            result = prime * result + ((rank == null) ? 0 : rank.hashCode());
            return result;
        }

        @Override
        public String toString() {
            return "OrderByContainer [attribute=" + attribute + ", ascending=" + ascending + ", rank=" + rank + "]";
        }
    }

    /**
     * /** Internal class that contains attributes that Tuple should select as
     * part of the result set.
     * 
     * @author mahmood.aftab
     * 
     * @param <T>
     */

    private class SelectContainer<T> {
        SingularAttribute<? super T, ?> singularAttribute;
        String alias;

        AggregateFunction function;

        SelectContainer(AggregateFunction function, SingularAttribute<? super T, ?> singularAttribute,
                String alias) {
            this.singularAttribute = singularAttribute;
            this.alias = alias;
            this.function = function;
        }

        SelectContainer(SingularAttribute<? super T, ?> singularAttribute, String alias) {
            this.singularAttribute = singularAttribute;
            this.alias = alias;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            SelectContainer<T> other = (SelectContainer) obj;
            if (singularAttribute == null) {
                if (other.singularAttribute != null)
                    return false;
            } else if (!singularAttribute.equals(other.singularAttribute))
                return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((function == null) ? 0 : function.hashCode());
            result = prime * result + ((alias == null) ? 0 : alias.hashCode());
            result = prime * result + ((singularAttribute == null) ? 0 : singularAttribute.hashCode());
            return result;
        }

        @Override
        public String toString() {
            return "SelectContainer [singularAttribute=" + singularAttribute + ", alias=" + alias + ", function="
                    + function + "]";
        }
    }

    /**
     * Internal class that contains attributes to match for where clause.
     * 
     * @author mahmood.aftab
     * 
     * @param <T>
     */
    public class WhereContainer<T> {

        ComparisonOperator comparisionOperator = ComparisonOperator.EQUAL;
        LogicOperator logicOperator = LogicOperator.NONE;
        NegationOperator notOperator = null;
        Object value = null;
        SingularAttribute<? super T, ?> attribute = null;
        boolean containsSubQuery = false;

        // SetAttribute<E, ?> setattribute;

        <V> WhereContainer(SingularAttribute<? super T, V> attribute, ComparisonOperator cOperator, V value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.value = value;
        }

        <V> WhereContainer(SingularAttribute<? super T, V> attribute, NegationOperator nOperator,
                ComparisonOperator cOperator, V value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.notOperator = nOperator;
            this.value = value;
        }

        <V> WhereContainer(SingularAttribute<? super T, V> attribute, ComparisonOperator cOperator,
                CriteriaComposer<V> value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.value = value;
            this.containsSubQuery = true;
        }

        <V> WhereContainer(SingularAttribute<? super T, ?> attribute, NegationOperator nOperator,
                ComparisonOperator cOperator, CriteriaComposer<V> value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.notOperator = nOperator;
            this.value = value;
            this.containsSubQuery = true;
        }

        <V> WhereContainer(SingularAttribute<? super T, V> attribute, ComparisonOperator cOperator, List<V> value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.value = value;
        }

        <V> WhereContainer(SingularAttribute<? super T, V> attribute, NegationOperator nOperator,
                ComparisonOperator cOperator, List<V> value) {
            this.attribute = attribute;
            this.comparisionOperator = cOperator;
            this.notOperator = nOperator;
            this.value = value;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            WhereContainer<T> other = (WhereContainer) obj;
            if (attribute == null) {
                if (other.attribute != null)
                    return false;
            } else if (!attribute.equals(other.attribute))
                return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((attribute == null) ? 0 : attribute.hashCode());
            result = prime * result + ((comparisionOperator == null) ? 0 : comparisionOperator.hashCode());
            result = prime * result + ((notOperator == null) ? 0 : notOperator.hashCode());
            result = prime * result + ((logicOperator == null) ? 0 : logicOperator.hashCode());
            result = prime * result + ((value == null) ? 0 : value.hashCode());
            return result;
        }

        @Override
        public String toString() {
            return "WhereContainer [attributeToValueOperator=" + comparisionOperator + ", nextWhereOperator="
                    + logicOperator + ", negationOperator=" + notOperator + ", value=" + value + ", attribute="
                    + attribute + "]";
        }

    }

    private static Log log = LogFactory.getLog(CriteriaComposer.class);

    /**
     * @param forClass
     * @return
     * @deprecated  As of release 2.0 beta, replaced by {@link #from(Class<E> forClass)}
     */
    @Deprecated
    public static <E> CriteriaComposer<E> createComposer(Class<E> forClass) {
        return new CriteriaComposer<E>(forClass);
    }

    /**
     * @param forClass
     * @return
     */
    public static <E> CriteriaComposer<E> from(Class<E> forClass) {
        return new CriteriaComposer<E>(forClass);
    }

    /**
     * @param forClass
     */
    private CriteriaComposer(Class<E> forClass) {
        this._entityClass = forClass;
    }

    protected void generateExampleBy() {

    }

    /**
     * @param criteriaBuilder
     * @param out
     */
    @SuppressWarnings("unchecked")
    protected void generateGroupBy(final CriteriaBuilder criteriaBuilder, Map<Integer, Expression<?>> out) {
        log.trace("generateOrderBy");

        Preconditions.checkNotNull(out);
        Preconditions.checkNotNull(root);
        Preconditions.checkNotNull(criteriaBuilder);

        // First others
        if (_joins != null) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> join : allSetJoins) {
                CriteriaComposer<?> subCriteria = join.getValue();

                if (subCriteria != null)
                    subCriteria.generateGroupBy(criteriaBuilder, out);
            }
        }

        // Then me. I will overwrite rank of others.
        if (_groupBys != null && _groupBys.size() > 0) {
            for (GroupByContainer<E> groupByContainer : _groupBys) {
                out.put(groupByContainer.rank, root.get(groupByContainer.attribute));
                log.debug("adding _groupBy " + groupByContainer.attribute.getName());
            }
        }

    }

    /**
     * @param criteriaBuilder
     * @param out
     */
    @SuppressWarnings("unchecked")
    protected void generateHaving(final CriteriaBuilder criteriaBuilder, final List<Predicate> out) {
        // log.debug("generateHaving :");

        Preconditions.checkNotNull(out);
        Preconditions.checkNotNull(criteriaBuilder);
        Preconditions.checkNotNull(root);

        if (_joins != null && _joins.size() > 0) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> joinEntry : allSetJoins) {
                CriteriaComposer<?> subCriteria = joinEntry.getValue();

                if (subCriteria != null)
                    subCriteria.generateHaving(criteriaBuilder, out);
            }
        }

        if (_havings != null) {
            List<Predicate> andPredicates = new ArrayList<Predicate>(0);
            List<Predicate> orPredicates = new ArrayList<Predicate>(0);

            LogicOperator lastOperator = LogicOperator.NONE;

            for (HavingContainer<E> havingContainer : _havings) {

                @SuppressWarnings("rawtypes")
                Expression expression = null;

                switch (havingContainer.function) {
                case COUNT:
                    expression = criteriaBuilder.count(root.get(havingContainer.attribute));
                    break;

                case AVG:
                    expression = criteriaBuilder.avg(root.get(havingContainer.attribute));
                    break;

                case SUM:
                    expression = criteriaBuilder.sum(root.get(havingContainer.attribute));
                    break;

                case MAX:
                    expression = criteriaBuilder.max(root.get(havingContainer.attribute));
                    break;

                case MIN:
                    expression = criteriaBuilder.min(root.get(havingContainer.attribute));
                    break;
                }

                Predicate predicate = ComparisonOperatorProcessor.process(criteriaBuilder, expression,
                        havingContainer.comparisionOperator, havingContainer.value);

                if (havingContainer.notOperator != null)
                    predicate = ComparisonOperatorProcessor.negate(criteriaBuilder, predicate);

                LogicOperator nextOperator = havingContainer.logicOperator;

                if (nextOperator == LogicOperator.NONE && !nextOperator.equals(lastOperator))
                    nextOperator = lastOperator;

                switch (nextOperator) {
                case AND:
                case NONE:
                    andPredicates.add(predicate);
                    break;
                case OR:
                    orPredicates.add(predicate);
                    break;
                default:
                    throw new java.lang.IllegalStateException(
                            "Unknown operator found " + havingContainer.comparisionOperator.toString());
                }

                lastOperator = nextOperator;

            } // end for

            if (andPredicates != null && andPredicates.size() > 0)
                out.add(criteriaBuilder.and(andPredicates.toArray(new Predicate[andPredicates.size()])));

            if (orPredicates != null && orPredicates.size() > 0)
                out.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));

        } // end if
    }

    /**
     * Creates all Singular or Set _joinContainer for this entity and all the
     * associated entities.
     * 
     * @param root
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void generateJoins(final From root) {
        this.root = root;
        Join<E, ?> join = null;

        if (_joins != null && _joins.size() > 0) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> joinEntry : allSetJoins) {
                JoinContainer<E> joinContainer = joinEntry.getKey();

                if (joinContainer.setAttribute != null)
                    join = root.join(joinContainer.setAttribute, joinContainer.joinType);

                else if (joinContainer.singularAttribute != null)
                    join = root.join(joinContainer.singularAttribute, joinContainer.joinType);

                CriteriaComposer<?> subCriteria = joinEntry.getValue();

                if (subCriteria != null)
                    subCriteria.generateJoins(join);
            }
        }
    }

    /**
     * @param criteriaBuilder
     * @param out
     */
    @SuppressWarnings("unchecked")
    protected void generateOrderBy(final CriteriaBuilder criteriaBuilder, Map<Integer, Order> out) {
        log.trace("generateOrderBy");

        Preconditions.checkNotNull(out);
        Preconditions.checkNotNull(root);
        Preconditions.checkNotNull(criteriaBuilder);

        // First others
        if (_joins != null) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> join : allSetJoins) {
                CriteriaComposer<?> subCriteria = join.getValue();

                if (subCriteria != null)
                    subCriteria.generateOrderBy(criteriaBuilder, out);
            }
        }

        // Then me. I will overwrite rank of others.
        if (_orderBys != null && _orderBys.size() > 0) {
            for (OrderByContainer<E> orderByContainer : _orderBys) {
                boolean ascending = orderByContainer.ascending;

                if (ascending) {
                    if (log.isDebugEnabled())
                        log.debug("ascending _orderBy " + orderByContainer.attribute.getName());
                    out.put(orderByContainer.rank, criteriaBuilder.asc(root.get(orderByContainer.attribute)));
                } else {
                    if (log.isDebugEnabled())
                        log.debug("descending _orderBy " + orderByContainer.attribute.getName());
                    out.put(orderByContainer.rank, criteriaBuilder.desc(root.get(orderByContainer.attribute)));
                }
            }
        }

    }

    /**
     * @param out
     */
    @SuppressWarnings("unchecked")
    protected void generateSelect(final CriteriaBuilder criteriaBuilder, final List<Selection<?>> out) {
        Preconditions.checkNotNull(out);

        if (multiselect != null && multiselect.size() > 0) {
            out.addAll(multiselect);
        }

        if (_selects != null && _selects.size() > 0) {
            for (SelectContainer<E> selectContainer : _selects) {
                if (selectContainer.function == null) {
                    Selection<?> selection = root.get(selectContainer.singularAttribute);
                    selection.alias(selectContainer.alias);
                    out.add(selection);
                } else {
                    @SuppressWarnings("rawtypes")
                    Expression numExp = null;
                    switch (selectContainer.function) {
                    case COUNT:
                        numExp = criteriaBuilder.count(root.get(selectContainer.singularAttribute));
                        numExp.alias(selectContainer.alias);
                        break;

                    case AVG:
                        numExp = criteriaBuilder.avg(root.get(selectContainer.singularAttribute));
                        numExp.alias(selectContainer.alias);
                        break;

                    case SUM:
                        numExp = criteriaBuilder.sum(root.get(selectContainer.singularAttribute));
                        numExp.alias(selectContainer.alias);
                        break;

                    case MAX:
                        numExp = criteriaBuilder.max(root.get(selectContainer.singularAttribute));
                        numExp.alias(selectContainer.alias);
                        break;

                    case MIN:
                        numExp = criteriaBuilder.min(root.get(selectContainer.singularAttribute));
                        numExp.alias(selectContainer.alias);
                        break;
                    }

                    if (numExp != null)
                        out.add(numExp);
                }

            }
        }

        if (_joins != null) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> join : allSetJoins) {
                CriteriaComposer<?> subCriteria = join.getValue();

                if (subCriteria != null)
                    subCriteria.generateSelect(criteriaBuilder, out);
            }
        }
    }

    /**
     * Adds all attributes to match and their values to the predicate.
     * 
     * @param criteriaBuilder
     * @param out
     *            - predicates
     */
    @SuppressWarnings("unchecked")
    protected void generateWhere(final CriteriaBuilder criteriaBuilder, final List<Predicate> out) {
        // log.debug("generateWhereClaus :");

        Preconditions.checkNotNull(out);
        Preconditions.checkNotNull(criteriaBuilder);
        Preconditions.checkNotNull(root);

        if (_joins != null && _joins.size() > 0) {
            Set<Entry<JoinContainer<E>, CriteriaComposer<?>>> allSetJoins = _joins.entrySet();

            for (Entry<JoinContainer<E>, CriteriaComposer<?>> joinEntry : allSetJoins) {
                CriteriaComposer<?> subCriteria = joinEntry.getValue();

                if (subCriteria != null)
                    subCriteria.generateWhere(criteriaBuilder, out);
            }
        }

        if (_wheres != null) {
            List<Predicate> andPredicates = new ArrayList<Predicate>(0);
            List<Predicate> orPredicates = new ArrayList<Predicate>(0);

            LogicOperator lastOperator = LogicOperator.NONE;

            for (WhereContainer<E> where : _wheres) {
                Predicate predicate = ComparisonOperatorProcessor.process(criteriaBuilder,
                        root.get(where.attribute), where.comparisionOperator, where.value);

                if (where.notOperator != null)
                    predicate = criteriaBuilder.not(predicate);

                LogicOperator nextOperator = where.logicOperator;

                if (nextOperator == LogicOperator.NONE && !nextOperator.equals(lastOperator))
                    nextOperator = lastOperator;

                switch (nextOperator) {
                case AND:
                case NONE:
                    andPredicates.add(predicate);
                    break;
                case OR:
                    orPredicates.add(predicate);
                    break;
                default:
                    throw new java.lang.IllegalStateException(
                            "Unknown operator found " + where.comparisionOperator.toString());
                }

                lastOperator = nextOperator;

            } // end for

            if (andPredicates != null && andPredicates.size() > 0)
                out.add(criteriaBuilder.and(andPredicates.toArray(new Predicate[andPredicates.size()])));

            if (orPredicates != null && orPredicates.size() > 0)
                out.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));

        } // end if
    }

    public Class<E> getEntityClass() {
        return this._entityClass;
    }

    /**
     * Get the sub criteria by going through joins.
     * 
     * @return
     */
    /*   public CriteriaComposer<?>[] getSubCriterias()
       {
          if (_joins != null)
          {
     CriteriaComposer<?>[] out = new CriteriaComposer<?>[_joins.size()];
     _joins.values().toArray(out);
     return out;
          }
          else
     return new CriteriaComposer<?>[0];
              
              
          //tbd
          //call recursively
       }*/

    /**
     * Get the sub criteria by going through joins.
     * 
     * @return
     */
    /*   public <T> CriteriaComposer<T> getSubCriteria(Class<T> forClass)
       {
              
          //tbd
          //????
          return null;
           
       }   */

    /**
     * Example:
     * CriteriaComposer.createComposer(Person.class).select(Person_.name
     * ).groupBy(Person_.name);
     * forStudentUnitCount.join(Person_.courseSessions).
     * join(CourseSession_.course).select(SUM, Course_.unit);
     * 
     * @param attribute
     * @return
     */
    public <V> CriteriaComposer<E> groupBy(SingularAttribute<? super E, V> attribute) {
        return this.groupBy(attribute, 1);
    }

    public <V> CriteriaComposer<E> groupBy(SingularAttribute<? super E, V>... attributes) {

        for (int i = 0; i < attributes.length; i++)
            this.groupBy(attributes[i], i);

        return this;
    }

    /**
     * 
     * @param attribute
     * @param rank
     *            - precedence order.
     * @return
     */
    public <V> CriteriaComposer<E> groupBy(SingularAttribute<? super E, V> attribute, int rank) {
        Preconditions.checkNotNull(attribute);
        Preconditions.checkArgument(_selects.size() > 0, "Please use select clause first.");

        GroupByContainer<E> groupByContainer = new GroupByContainer<E>(attribute, rank);

        int index = this._groupBys.indexOf(groupByContainer);

        // overwrite
        if (index >= 0)
            this._groupBys.add(index, groupByContainer);
        else
            this._groupBys.add(groupByContainer);

        return this;
    }

    /**
     * Adds attribute and its value to having clause and adds logic operation
     * for the next having clause.
     * 
     * @param function
     *            - {@link AggregateFunction}
     * @param attribute
     *            - SingularAttribute of the entity for which this search was
     *            created.
     * @param cOperator
     *            - {@link ComparisonOperator}
     * @param value
     *            - value to match with the attribute.
     * @return
     */
    /*   public <V> CriteriaComposer<E> having(AggregateFunction function,
       SingularAttribute<E, V> attribute, ComparisonOperator cOperator,
       V value)
       {
          return this.having(function, attribute, cOperator, value,
          LogicOperator.NONE);
       }*/

    /**
     * Adds the attribute to having clause of the entity
     * 
     * @param function
     *            - {@link AggregateFunction}
     * @param attribute
     *            - SingularAttribute of the entity for which this search was
     *            created.
     * @param cOperator
     *            - {@link ComparisonOperator}
     * @param value
     *            - value to match with the attribute.
     * @param lOperator
     *            - {@link LogicOperator} logic operator that will be applied
     *            with next having.
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <V> CriteriaComposer<E> having(AggregateFunction function, SingularAttribute<? super E, V> attribute,
            ComparisonOperator cOperator, V value) {
        Preconditions.checkArgument(_selects.indexOf(new SelectContainer(attribute, "")) >= 0,
                "Unable to find related select clasue.");

        Preconditions.checkNotNull(function);
        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(cOperator);

        HavingContainer<E> havingContainer = new HavingContainer<E>(function, attribute, cOperator, value);

        int index = _havings.indexOf(havingContainer);

        if (index >= 0)
            _havings.add(index, havingContainer);
        else
            _havings.add(havingContainer);

        log.debug("Adding having " + havingContainer.toString());

        lastCallType = LastCallType.HAVING;
        return this;
    }

    /**
     * Adds attribute and its value to having clause and adds logic operation
     * for the next having clause.
     * 
     * @param function
     *            - {@link AggregateFunction}
     * @param attribute
     *            - SingularAttribute of the entity for which this search was
     *            created.
     * @param nOperator
     *            - {@link NegationOperator}
     * @param cOperator
     *            - {@link ComparisonOperator}
     * @param value
     *            - value to match with the attribute.
     * @return
     */
    /*   public <V> CriteriaComposer<E> having(AggregateFunction function,
       SingularAttribute<? super E, V> attribute, NegationOperator nOperator,
       ComparisonOperator cOperator, V value)
       {
          return this.having(function, attribute, nOperator, cOperator, value,
          LogicOperator.NONE);
       }*/

    /**
     * Adds the attribute to having clause of the entity
     * 
     * @param function
     *            - {@link AggregateFunction}
     * @param attribute
     *            - SingularAttribute of the entity for which this search was
     *            created.
     * @param nOperator
     *            - {@link NegationOperator}
     * @param cOperator
     *            - {@link ComparisonOperator}
     * @param value
     *            - value to match with the attribute.
     * @param lOperator
     *            - {@link LogicOperator} logic operator that will be applied
     *            with next having.
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public <V> CriteriaComposer<E> having(AggregateFunction function, SingularAttribute<? super E, V> attribute,
            NegationOperator nOperator, ComparisonOperator cOperator, V value) {

        Preconditions.checkArgument(_selects.indexOf(new SelectContainer(attribute, "")) >= 0,
                "Unable to find related select clasue.");

        Preconditions.checkNotNull(function);
        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(nOperator);
        Preconditions.checkNotNull(cOperator);

        HavingContainer<E> havingContainer = new HavingContainer<E>(function, attribute, nOperator, cOperator,
                value);

        int index = _havings.indexOf(havingContainer);

        if (index >= 0)
            _havings.add(index, havingContainer);
        else
            _havings.add(havingContainer);

        log.debug("Adding having for " + attribute.getName() + " " + cOperator.toString() + " value "
                + value.toString());

        lastCallType = LastCallType.HAVING;

        return this;
    }

    /**
     * Creates a set join with the next entity.
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SetAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next entity
     */
    public <V> CriteriaComposer<V> join(JoinType joinType, SetAttribute<E, V> attribute) {
        return join(joinType, attribute, null);
    }

    /**
     * Creates a set join with the next entity.
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SetAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next entity
     */
    @SuppressWarnings("unchecked")
    public <V> CriteriaComposer<V> join(JoinType joinType, SetAttribute<? super E, V> attribute,
            CriteriaComposer<V> subCriteria) {
        Preconditions.checkNotNull(attribute);

        Class<V> classToJoin = attribute.getBindableJavaType();

        JoinContainer<E> join = new JoinContainer<E>(joinType, attribute);

        // Dont overwrite join
        if (_joins.containsKey(join))
            return (CriteriaComposer<V>) _joins.get(join);

        if (subCriteria == null)
            subCriteria = new CriteriaComposer<V>(classToJoin);

        _joins.put(join, subCriteria);

        log.debug("Addeding join " + joinType.toString() + " on " + classToJoin.getSimpleName() + " "
                + attribute.getName());

        return subCriteria;
    }

    /**
     * Addes attribute for orderBy clause in ascending. Auto increments the rank
     * from left to right.
     * 
     * @param attributes
     * @return
     * 
     *         public CriteriaContainer<E> orderBy(SingularAttribute<E, ?> ...
     *         attributes) {
     * 
     *         Preconditions.checkNotNull(attributes); int rank=1; for
     *         (SingularAttribute<E, ?> attribute: attributes) {
     *         log.debug("Adding _orderBy for " + attribute.getName());
     *         _orderBy.add(new OrderByContainer<E>(attribute, true, rank));
     *         rank++; }
     * 
     *         return this; }
     */

    /**
     * Creates a singular join with the next entity.
     * 
     * @param <R>
     * @param attribute
     *            - SingularAttribute of this entity that has reference to next
     *            entity
     * @return - Sub criteria for the next entity
     */
    public <V> CriteriaComposer<V> join(JoinType joinType, SingularAttribute<? super E, V> attribute) {
        return join(joinType, attribute, null);
    }

    /**
     * Creates a singular join with the next entity.
     * 
     * @param <R>
     *            - The entity type to join with. (right side)
     * @param attribute
     *            - SingularAttribute of this entity that has reference to next
     *            entity
     * @return - Sub criteria for the next entity
     */
    @SuppressWarnings("unchecked")
    public <V> CriteriaComposer<V> join(JoinType joinType, SingularAttribute<? super E, V> attribute,
            CriteriaComposer<V> subCriteria) {
        Preconditions.checkNotNull(attribute);

        Class<V> classToJoin = attribute.getBindableJavaType();

        JoinContainer<E> join = new JoinContainer<E>(joinType, attribute);

        // Don't overwrite join
        if (_joins.containsKey(join))
            return (CriteriaComposer<V>) _joins.get(join);

        if (subCriteria == null)
            subCriteria = new CriteriaComposer<V>(classToJoin);

        _joins.put(join, subCriteria);

        log.debug("Addeding join " + joinType.toString() + " on " + classToJoin.getSimpleName() + " "
                + attribute.getName());

        return subCriteria;
    }

    /**
     * @param _selectContainer
     * 
     *            public CriteriaContainer<E> groupBy(SingularAttribute<E, ?>...
     *            attributes) { int rank=1; for (SingularAttribute<E, ?>
     *            attribute : attributes) { GroupByContainer<E> groupBy = new
     *            GroupByContainer<E>(attribute, rank);
     *            this._groupBy.add(groupBy); rank++; } return this; }
     */

    /**
     * Creates a set join with the next entity. Uses JoinType.INNER as default
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SetAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next joined entity
     */
    public <V> CriteriaComposer<V> join(SetAttribute<? super E, V> attribute) {
        return join(JoinType.INNER, attribute, null);
    }

    /**
     * Creates a set join with the next entity. Users JoinType.INNER as default.
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SetAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next entity
     */
    public <V> CriteriaComposer<V> join(SetAttribute<E, V> attribute, CriteriaComposer<V> subCriteria) {
        return join(JoinType.INNER, attribute, subCriteria);
    }

    /**
     * Creates a set join with the next entity. Uses JoinType.INNER as default
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SingularAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next joined entity
     */
    public <V> CriteriaComposer<V> join(SingularAttribute<? super E, V> attribute) {
        return join(JoinType.INNER, attribute, null);
    }

    /**
     * Creates a set join with the next entity. Users JoinType.INNER as default.
     * 
     * @param <R>
     *            - Class of the next entity
     * @param attribute
     *            - SingularAttribute of this entity that has reference to next
     *            entity
     * @return Sub criteria for the next joined entity
     */
    public <V> CriteriaComposer<V> join(SingularAttribute<? super E, V> attribute,
            CriteriaComposer<V> subCriteria) {
        return join(JoinType.INNER, attribute, subCriteria);
    }

    /**
     * Ascending orderBy.
     * @param attributes
     * @return
     */
    public <V> CriteriaComposer<E> orderBy(SingularAttribute<? super E, V>... attributes) {
        for (int i = 0; i < attributes.length; i++)
            this.orderBy(attributes[i], true, i);

        return this;
    }

    /**
     * @param attribute
     * @param ascending
     * @param rank
     *            - precedence order - should be greater then zero and should be
     *            unique across the entire graph. Subsequent occurrences of the
     *            attribute with the same ranked number will be ignored and will
     *            not be added into orderBy clause.
     * 
     */
    public <V> CriteriaComposer<E> orderBy(SingularAttribute<? super E, V> attribute, boolean ascending, int rank) {

        Preconditions.checkArgument(rank > 0);
        Preconditions.checkNotNull(attribute);

        log.debug("Adding _orderBy for " + attribute.getName());

        OrderByContainer<E> orderByContainer = new OrderByContainer<E>(attribute, ascending, rank);

        int index = _orderBys.indexOf(orderByContainer);

        if (index > -0)
            _orderBys.add(index, orderByContainer);
        else
            _orderBys.add(orderByContainer);

        return this;
    }

    /**
     * @param function
     * @param attribute
     * @return
     */
    public <V> CriteriaComposer<E> select(AggregateFunction function, SingularAttribute<? super E, V> attribute) {
        Preconditions.checkNotNull(function);
        Preconditions.checkNotNull(attribute);

        return this.select(function, attribute, "");
    }

    /**
     * @param function
     * @param attribute
     * @param alias
     * @return
     */
    public <V> CriteriaComposer<E> select(AggregateFunction function, SingularAttribute<? super E, V> attribute,
            String alias) {
        Preconditions.checkNotNull(function);
        Preconditions.checkNotNull(attribute);

        if (alias == null || alias.trim().length() == 0)
            alias = function.toString().toLowerCase() + "." + this._entityClass.getSimpleName() + "."
                    + attribute.getName();

        SelectContainer<E> selectContainer = new SelectContainer<E>(function, attribute, alias);

        int index = _selects.indexOf(selectContainer);

        if (index >= 0)
            _selects.add(index, selectContainer);
        else
            _selects.add(selectContainer);

        log.debug("Added select " + attribute.toString());

        return this;
    }

    /**
     * @param attribute
     * @return
     */
    public <V> CriteriaComposer<E> select(SingularAttribute<? super E, V> attribute) {
        return this.select(attribute, "");
    }

    /**
     * @param attributesToSelect
     * @return
     */
    public CriteriaComposer<E> select(SingularAttribute<? super E, ?>... attributesToSelect) {

        for (SingularAttribute<? super E, ?> attribute : attributesToSelect) {
            this.select(attribute, "");
        }

        return this;
    }

    /**
     * @param attribute
     * @param alias
     */
    public <V> CriteriaComposer<E> select(SingularAttribute<? super E, V> attribute, String alias) {
        Preconditions.checkNotNull(attribute);

        if (alias == null || alias.trim().length() == 0)
            alias = this._entityClass.getSimpleName() + "." + attribute.getName();

        SelectContainer<E> selectContainer = new SelectContainer<E>(attribute, alias);

        // overwrite if exists.
        int index = _selects.indexOf(selectContainer);

        if (index >= 0)
            _selects.add(index, selectContainer);
        else
            _selects.add(selectContainer);

        log.debug("Added select for " + attribute.getName() + "with alias " + alias);

        return this;
    }

    @Override
    public String toString() {
        return "CriteriaContainer [root=" + _entityClass.getSimpleName() + "]";
    }

    /**
     * for IN and BETWEEN
     * @param attribute
     * @param cOperator
     * @param values
     * @return
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       ComparisonOperator cOperator, List<V> values)
       {
          return where(attribute, cOperator, values, LogicOperator.NONE);
       }*/

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param cOperator
     * @param values
     * @param lOperator
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, ComparisonOperator cOperator,
            List<V> values) {
        Preconditions.checkArgument(
                cOperator.equals(ComparisonOperator.BETWEEN) || cOperator.equals(ComparisonOperator.IN));
        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, cOperator, values);

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " value count "
                + values.size());

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param cOperator
     * @param values
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, ComparisonOperator cOperator,
            V... values) {
        return where(attribute, cOperator, new ArrayList<V>(Arrays.asList(values)));
    }

    /**
     * Adds attribute and its value to where clause and adds logic operation for
     * the next where clause.
     * 
     * @param attribute
     * @param operator
     * @param value
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       ComparisonOperator operator, V value)
       {
          return this.where(attribute, operator, value, LogicOperator.NONE);
       }*/

    /**
     * Adds the attribute to where clause of the entity
     * 
     * @param attribute
     * @param cOperator
     * @param value
     * @param lOperator
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, ComparisonOperator cOperator,
            V value) {
        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, cOperator, value);

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " value "
                + value.toString());

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param values
     * @return
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       NegationOperator nOperator, ComparisonOperator cOperator,
       List<V> values)
       {
          return where(attribute, nOperator, cOperator, values,
          LogicOperator.NONE);
       }*/

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param values
     * @param lOperator
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, NegationOperator nOperator,
            ComparisonOperator cOperator, List<V> values) {
        Preconditions.checkArgument(
                cOperator.equals(ComparisonOperator.BETWEEN) || cOperator.equals(ComparisonOperator.IN));

        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(nOperator);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, nOperator, cOperator, values);

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " value count "
                + values.size());

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param values
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, NegationOperator nOperator,
            ComparisonOperator cOperator, V... values) {
        return where(attribute, nOperator, cOperator, new ArrayList<V>(Arrays.asList(values)));
    }

    /**
     * Adds attribute and its value to where clause and adds logic operation for
     * the next where clause.
     * 
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param value
     * @return
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       NegationOperator nOperator, ComparisonOperator cOperator, V value)
       {
          return this.where(attribute, nOperator, cOperator, value);
       }*/

    /**
     * Adds the attribute to where clause of the entity
     * 
     * @param attribute
     *            - SingularAttribute of the entity for which this search was
     *            created.
     * @param value
     *            - value to match with the attribute.
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, NegationOperator nOperator,
            ComparisonOperator cOperator, V value) {
        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(nOperator);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, nOperator, cOperator, value);

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " value "
                + value.toString());

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /***********************************************************************************************/
    /**
     * for IN and BETWEEN
     * @param attribute
     * @param cOperator
     * @param values
     * @return
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       ComparisonOperator cOperator, CriteriaComposer<V> subCriteria)
       {
          return where(attribute, cOperator, subCriteria, LogicOperator.NONE);
       }*/

    public <V> CriteriaComposer<E> and() {
        switch (lastCallType) {
        case WHERE:
            if (this._wheres != null && this._wheres.size() > 0) {
                this._wheres.get(this._wheres.size() - 1).logicOperator = LogicOperator.AND;
            }
            break;

        case HAVING:
            if (this._havings != null && this._havings.size() > 0) {
                this._havings.get(this._havings.size() - 1).logicOperator = LogicOperator.AND;
            }
            break;
        }

        return this;
    }

    public <V> CriteriaComposer<E> or() {
        switch (lastCallType) {
        case WHERE:
            if (this._wheres != null && this._wheres.size() > 0) {
                this._wheres.get(this._wheres.size() - 1).logicOperator = LogicOperator.OR;
            }
            break;

        case HAVING:
            if (this._havings != null && this._havings.size() > 0) {
                this._havings.get(this._havings.size() - 1).logicOperator = LogicOperator.OR;
            }
            break;

        }

        return this;
    }

    /**
     * @param attribute
     * @param cOperator
     * @param subCriteria
     * @param lOperator
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, ComparisonOperator cOperator,
            CriteriaComposer<V> subCriteria) {
        Preconditions.checkArgument(cOperator.equals(ComparisonOperator.IN));

        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, cOperator, subCriteria);

        whereContainer.logicOperator = LogicOperator.AND;

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)//overwrite
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " sub criteria "
                + subCriteria);

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /**
     * for IN and BETWEEN
     * 
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param values
     * @return
     */
    /*   public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute,
       NegationOperator nOperator, ComparisonOperator cOperator,
       CriteriaComposer<V> subCriteria)
       {
          return where(attribute, nOperator, cOperator, subCriteria,
          LogicOperator.NONE);
       }*/

    /**
     * @param attribute
     * @param nOperator
     * @param cOperator
     * @param subCriteria
     * @param lOperator
     * @return
     */
    public <V> CriteriaComposer<E> where(SingularAttribute<? super E, V> attribute, NegationOperator nOperator,
            ComparisonOperator cOperator, CriteriaComposer<V> subCriteria) {
        Preconditions.checkArgument(cOperator.equals(ComparisonOperator.IN));

        Preconditions.checkNotNull(attribute);
        Preconditions.checkNotNull(nOperator);
        Preconditions.checkNotNull(cOperator);

        WhereContainer<E> whereContainer = new WhereContainer<E>(attribute, nOperator, cOperator, subCriteria);

        int index = _wheres.indexOf(whereContainer);

        if (index >= 0)
            _wheres.add(index, whereContainer);
        else
            _wheres.add(whereContainer);

        log.debug("Adding where for " + attribute.getName() + " " + cOperator.toString() + " sub criteria "
                + subCriteria);

        lastCallType = LastCallType.WHERE;

        return this;
    }

    /**
     *
     * @return
     */
    public From getFrom() {
        return this.root;

    }

    /**
     * Added the JPA Selection objects as it is to internal collection. 
     * GenerateSelect then copied to the final list, which is then used by 
     * CriteriaProcessor to select particular columns of the entity(ies).
     *   
     *    @see {@link javax.persistence.criteria.Selection}
     *  
     * @param selectedColumns
     */
    public void select(List<Selection<?>> selectedColumns) {
        this.multiselect = selectedColumns;
    }

    /*   protected void generateSubQuery(CriteriaBuilder cb, CriteriaQuery cq)
       {
          //TBD cq.subquery(arg0)
       }*/

}