org.gvnix.web.datatables.util.impl.DatatablesUtilsBeanImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.gvnix.web.datatables.util.impl.DatatablesUtilsBeanImpl.java

Source

/*
 * gvNIX. Spring Roo based RAD tool for Generalitat Valenciana
 * Copyright (C) 2013 Generalitat Valenciana
 *
 * 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.
 */
package org.gvnix.web.datatables.util.impl;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.gvnix.web.datatables.query.SearchResults;
import org.gvnix.web.datatables.util.DatatablesUtilsBean;
import org.gvnix.web.datatables.util.EntityManagerProvider;
import org.gvnix.web.datatables.util.QuerydslUtilsBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import com.github.dandelion.datatables.core.ajax.ColumnDef;
import com.github.dandelion.datatables.core.ajax.ColumnDef.SortDirection;
import com.github.dandelion.datatables.core.ajax.DataSet;
import com.github.dandelion.datatables.core.ajax.DatatablesCriterias;
import com.github.dandelion.datatables.core.export.ExportConf;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder.BeforeEndStep;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder.ColumnStep;
import com.github.dandelion.datatables.core.html.HtmlTable;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.QueryModifiers;
import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.Order;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.BooleanExpression;
import com.mysema.query.types.path.PathBuilder;

/**
 * Default Datatables utility service implementation
 * 
 * @author gvNIX team
 */
public class DatatablesUtilsBeanImpl implements DatatablesUtilsBean {

    // Logger
    private static Logger LOGGER = LoggerFactory.getLogger(DatatablesUtilsBeanImpl.class);

    @Autowired
    private ConversionService conversionService;

    @Autowired
    private MessageSource messageSource;

    @Autowired
    private EntityManagerProvider entityManagerProvider;

    @Autowired
    private QuerydslUtilsBean querydslUtilsBean;

    /**
     * {@inheritDoc}
     */
    @Override
    public JPAQuery newJPAQuery(EntityManager em) {
        return new JPAQuery(em);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSpecialFilterParameters(String name) {
        if (name.startsWith(QuerydslUtilsBean.OPERATOR_PREFIX)) {
            return true;
        } else if (DatatablesUtilsBean.ROWS_ON_TOP_IDS_PARAM.equals(name)) {
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> SearchResults<T> findByCriteria(Class<T> entityClass, DatatablesCriterias datatablesCriterias) {
        return findByCriteria(entityClass, datatablesCriterias);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> SearchResults<T> findByCriteria(Class<T> entityClass, DatatablesCriterias datatablesCriterias,
            Map<String, Object> baseSearchValuesMap) {
        return findByCriteria(entityClass, null, null, datatablesCriterias, baseSearchValuesMap, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> SearchResults<T> findByCriteria(Class<T> entityClass, Map<String, List<String>> filterByAssociations,
            Map<String, List<String>> orderByAssociations, DatatablesCriterias datatablesCriterias) {
        return findByCriteria(entityClass, filterByAssociations, orderByAssociations, datatablesCriterias);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> SearchResults<T> findByCriteria(Class<T> entityClass, Map<String, List<String>> filterByAssociations,
            Map<String, List<String>> orderByAssociations, DatatablesCriterias datatablesCriterias,
            Map<String, Object> baseSearchValuesMap) {
        return findByCriteria(entityClass, filterByAssociations, orderByAssociations, datatablesCriterias,
                baseSearchValuesMap, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(Class<T> entityClass,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, Map<String, Object> baseSearchValuesMap, boolean distinct)
            throws IllegalArgumentException {

        Assert.notNull(entityClass);

        // Query DSL builder
        PathBuilder<T> entity = new PathBuilder<T>(entityClass, "entity");

        Object[] rowsOnTopIds = null;
        // Predicate for base query
        BooleanBuilder basePredicate;
        if (baseSearchValuesMap != null) {
            LOGGER.debug("findByCriteria handle baseSearch by map-of-values for entity '{}'...", entity.getType());
            // Handle ROWS_ON_TOP_IDS_PARAM param
            Object tmpObject = baseSearchValuesMap.get(ROWS_ON_TOP_IDS_PARAM);

            if (tmpObject != null) {
                // Check if value is an array, otherwise
                if (tmpObject.getClass().isArray()) {
                    rowsOnTopIds = (Object[]) tmpObject;
                } else {
                    rowsOnTopIds = new Object[] { tmpObject };
                }
                Map<String, Object> newBaseSearch = new HashMap<String, Object>(baseSearchValuesMap);
                newBaseSearch.remove(ROWS_ON_TOP_IDS_PARAM);
                LOGGER.trace("findByCriteria extract rows on top from map {}", rowsOnTopIds);
                basePredicate = querydslUtilsBean.createPredicateByAnd(entity, newBaseSearch);
            } else {
                basePredicate = querydslUtilsBean.createPredicateByAnd(entity, baseSearchValuesMap);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("findByCriteria baseSearch by map-of-values: {}", basePredicate.toString());
            }
        } else {
            basePredicate = new BooleanBuilder();
        }

        return findByCriteria(entityClass, filterByAssociations, orderByAssociations, datatablesCriterias,
                basePredicate, distinct, rowsOnTopIds);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(Class<T> entityClass,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate, boolean distinct)
            throws IllegalArgumentException {

        Assert.notNull(entityClass);

        // Query DSL builder
        PathBuilder<T> entity = new PathBuilder<T>(entityClass, "entity");

        return findByCriteria(entity, filterByAssociations, orderByAssociations, datatablesCriterias, basePredicate,
                distinct, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(Class<T> entityClass,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate, boolean distinct,
            Object[] rowsOnTopIds) throws IllegalArgumentException {

        Assert.notNull(entityClass);

        // Query DSL builder
        PathBuilder<T> entity = new PathBuilder<T>(entityClass, "entity");

        return findByCriteria(entity, filterByAssociations, orderByAssociations, datatablesCriterias, basePredicate,
                distinct, rowsOnTopIds);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate) throws IllegalArgumentException {
        return findByCriteria(entity, filterByAssociations, orderByAssociations, datatablesCriterias, basePredicate,
                false, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(PathBuilder<T> entity,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate) throws IllegalArgumentException {
        return findByCriteria(entity, null, null, datatablesCriterias, basePredicate, false, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(PathBuilder<T> entity,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate, Object[] rowsOnTopIds)
            throws IllegalArgumentException {
        return findByCriteria(entity, null, null, datatablesCriterias, basePredicate, false, rowsOnTopIds);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate, boolean distinct)
            throws IllegalArgumentException {

        return findByCriteria(entity, null, null, datatablesCriterias, basePredicate, false, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, E extends Comparable<?>> SearchResults<T> findByCriteria(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, Map<String, List<String>> orderByAssociations,
            DatatablesCriterias datatablesCriterias, BooleanBuilder basePredicate, boolean distinct,
            Object[] rowsOnTopIds) throws IllegalArgumentException {
        // Check arguments aren't null
        EntityManager entityManager = entityManagerProvider.getEntityManager(entity.getType());
        Assert.notNull(entityManager);
        Assert.notNull(datatablesCriterias);

        // If null, create empty Map to avoid control code overload
        if (CollectionUtils.isEmpty(filterByAssociations)) {
            filterByAssociations = new HashMap<String, List<String>>();
        }
        if (CollectionUtils.isEmpty(orderByAssociations)) {
            orderByAssociations = new HashMap<String, List<String>>();
        }

        // true if data results must be paginated
        boolean isPaged = datatablesCriterias.getDisplaySize() != null && datatablesCriterias.getDisplaySize() > 0;

        // true if the search must take in account all columns
        boolean findInAllColumns = StringUtils.isNotEmpty(datatablesCriterias.getSearch())
                && datatablesCriterias.hasOneFilterableColumn();

        LOGGER.debug("findByCriteria for entity '{}' (paged={} findInAllColumns={})", entity.getType(), isPaged,
                findInAllColumns);

        // ----- Create queries -----

        // query will take in account datatables search, order and paging
        // criterias
        JPAQuery query = newJPAQuery(entityManager);
        query = query.from(entity);

        // baseQuery will use base search values only in order to count
        // all for success paging
        JPAQuery baseQuery = newJPAQuery(entityManager);
        baseQuery = baseQuery.from(entity);

        // ----- Entity associations for Query JOINs, ORDER BY, ... -----

        Map<String, PathBuilder<?>> associationMap = new HashMap<String, PathBuilder<?>>();

        query = prepareQueryAssociationMap(entity, filterByAssociations, datatablesCriterias, findInAllColumns,
                query, associationMap);

        // ----- Query WHERE clauses -----

        // Filters by column. Using BooleanBuilder, a cascading builder for
        // Predicate expressions
        BooleanBuilder filtersByColumnPredicate = new BooleanBuilder();

        // Filters by table (for all columns)
        BooleanBuilder filtersByTablePredicate = new BooleanBuilder();

        try {

            // Build the filters by column expression
            if (datatablesCriterias.hasOneFilteredColumn()) {

                filtersByColumnPredicate = prepareQueryFilterPart(entity, filterByAssociations, datatablesCriterias,
                        associationMap, filtersByColumnPredicate);
            }

            // Build the query to search the given value in all columns
            filtersByTablePredicate = prepareQuerySearchPart(entity, filterByAssociations, datatablesCriterias,
                    findInAllColumns, associationMap, filtersByTablePredicate);
        } catch (Exception e) {
            LOGGER.error("Exception preparing filter for entity {}", entity.getType(), e);
            SearchResults<T> searchResults = new SearchResults<T>(new ArrayList<T>(0), 0, isPaged,
                    new Long(org.apache.commons.lang3.ObjectUtils
                            .defaultIfNull(datatablesCriterias.getDisplayStart(), 0)),
                    new Long(org.apache.commons.lang3.ObjectUtils
                            .defaultIfNull(datatablesCriterias.getDisplaySize(), 0)),
                    0);
            return searchResults;
        }

        // ----- Query ORDER BY -----

        List<OrderSpecifier<?>> orderSpecifiersList = prepareQueryOrder(entity, orderByAssociations,
                datatablesCriterias, associationMap);

        // ----- Query results paging -----

        Long offset = null;
        Long limit = null;

        if (isPaged) {
            limit = new Long(datatablesCriterias.getDisplaySize());
        }
        if (datatablesCriterias.getDisplayStart() != null && datatablesCriterias.getDisplayStart() >= 0) {
            offset = new Long(datatablesCriterias.getDisplayStart());
        }

        // ------- manage Rows-on-top ----

        List<T> firstRows = null;

        // Decrease limits if firstRowsIds is used
        if (rowsOnTopIds != null) {
            LOGGER.trace("Prepare rows on top: {}", rowsOnTopIds);

            // Coherce row-on-top ids types
            Object[] cohercedRowsOnTopId = new Object[rowsOnTopIds.length];

            EntityType<? extends T> entityMetamodel = entityManager.getMetamodel().entity(entity.getType());
            // We always have just one id. This id can be an Embedded Id
            Class<?> idType = entityMetamodel.getIdType().getJavaType();
            @SuppressWarnings("unchecked")
            SingularAttribute<? extends T, ?> idAttr = (SingularAttribute<? extends T, ?>) entityMetamodel
                    .getId(idType);

            Object curId;
            for (int i = 0; i < rowsOnTopIds.length; i++) {
                curId = rowsOnTopIds[i];
                if (curId.getClass() != idType) {
                    cohercedRowsOnTopId[i] = conversionService.convert(curId, idType);
                } else {
                    cohercedRowsOnTopId[i] = curId;
                }
            }

            // Create expression for rows-on-top
            BooleanExpression firstRowsInExpression = querydslUtilsBean.createCollectionExpression(entity,
                    idAttr.getName(), Arrays.asList(cohercedRowsOnTopId));

            LOGGER.trace("Expression for rowsOnTop: {}", firstRowsInExpression);

            // Exclude firstRows from base query
            basePredicate = basePredicate.and(firstRowsInExpression.not());

            LOGGER.trace("basePredicate to exclude rowsOnTop now is: {}", basePredicate);

            // Gets rows on top
            JPAQuery firstRowsQuery = newJPAQuery(entityManager);
            firstRowsQuery = firstRowsQuery.from(entity).where(firstRowsInExpression);

            LOGGER.trace("rowsOnTop query is: {}", firstRowsQuery);

            try {
                // TODO handle fieldSelector
                firstRows = firstRowsQuery.list(entity);
            } catch (PersistenceException exSql) {
                // Log query
                LOGGER.error("Error excecuting SQL for firstRow (sql = '{}' )", firstRowsQuery);
                throw exSql;
            }

            LOGGER.trace("Found {} rows for rowsOnTop", firstRows.size());
            // Adjust limit with rows-on-top found
            if (limit != null) {
                LOGGER.trace("Update main query limit: {} --> {}", limit, limit - firstRows.size());
                limit = limit - firstRows.size();
            }

        }

        // ----- Execute the query -----
        List<T> elements = null;

        // Compose the final query and update query var to be used to count
        // total amount of rows if needed

        if (distinct) {
            LOGGER.trace("Use distinct query!!!");
            query = query.distinct();
        }

        // Predicate for base query
        boolean hasBasePredicate = true;
        if (basePredicate == null) {
            basePredicate = new BooleanBuilder();
            hasBasePredicate = false;
        }

        // query projection to count all entities without paging
        baseQuery.where(basePredicate);

        // query projection to be used to get the results and to count filtered
        // results
        query = query.where(
                basePredicate.and(filtersByColumnPredicate.getValue()).and(filtersByTablePredicate.getValue()));

        // Calculate the total amount of rows taking in account datatables
        // search and paging criterias. When results are paginated we
        // must execute a count query, otherwise the size of matched rows List
        // is the total amount of rows
        long totalResultCount = 0;
        if (isPaged) {
            try {
                totalResultCount = query.count();
            } catch (PersistenceException exSql) {
                // Log query
                LOGGER.error("Error excecuting 'count' SQL: {}", query);
                throw exSql;
            }
        }

        if (offset == null) {
            offset = new Long(0);
        } else if (offset > totalResultCount) {
            // If offset value is bigger than total results,
            // offset needs start on 0
            offset = new Long(0);
        }

        // QueryModifiers combines limit and offset
        QueryModifiers queryModifiers = new QueryModifiers(limit, offset);
        LOGGER.trace("Set limit={} offset={}", limit, offset);

        // List ordered and paginated results. An empty list is returned for no
        // results.
        query = query.orderBy(orderSpecifiersList.toArray(new OrderSpecifier[orderSpecifiersList.size()]));

        LOGGER.debug("Execute query: {}", query);
        try {
            elements = query.restrict(queryModifiers).list(entity);
        } catch (PersistenceException exSql) {
            // Log query
            LOGGER.error("Error excecuting SQL: {}", query);
            throw exSql;
        }

        if (!isPaged) {
            totalResultCount = elements.size();
        }

        long totalBaseCount = totalResultCount;
        if (hasBasePredicate) {
            // Calculate the total amount of entities including base filters
            // only
            LOGGER.trace("Execute count query: {}", baseQuery);
            try {
                totalBaseCount = baseQuery.count();
            } catch (PersistenceException exSql) {
                // Log query
                LOGGER.error("Error excecuting 'count' SQL: {}", baseQuery);
                throw exSql;
            }
            LOGGER.trace("Found : {}", totalBaseCount);
        }

        if (firstRows != null) {
            // Adjust result with rows-on-top
            totalResultCount = totalResultCount + firstRows.size();
            totalBaseCount = totalBaseCount + firstRows.size();
            elements.addAll(0, firstRows);
        }

        // Create a new SearchResults instance
        if (limit == null) {
            limit = totalBaseCount;
        }
        SearchResults<T> searchResults = new SearchResults<T>(elements, totalResultCount, isPaged, offset, limit,
                totalBaseCount);

        LOGGER.debug("findByCriteria: return {} rows from {} (offset={} limit={})", totalResultCount,
                totalBaseCount, offset, limit);
        return searchResults;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> JPAQuery prepareQueryAssociationMap(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, DatatablesCriterias datatablesCriterias,
            boolean findInAllColumns, JPAQuery query, Map<String, PathBuilder<?>> associationMap) {
        LOGGER.debug("Preparing associationMap and joins for entity {}...", entity.getType());
        for (ColumnDef column : datatablesCriterias.getColumnDefs()) {

            // true if the search must include this column
            boolean findInColumn = StringUtils.isNotEmpty(column.getSearch());

            // If no joins given for this column, don't add the JOIN to query
            // to improve performance
            String associationName = unescapeDot(column.getName());
            if (!filterByAssociations.containsKey(associationName)) {
                continue;
            }

            // If column is not sortable and is not filterable, don't add the
            // JOIN to query to improve performance
            if (!column.isSortable() && !column.isFilterable()) {
                continue;
            }

            // If column is not sortable and no search value provided,
            // don't add the JOIN to query to improve performance
            if (!column.isSortable() && !findInColumn && !findInAllColumns) {
                continue;
            }

            // Here the column is sortable or it is filterable and column search
            // value or all-column search value is provided
            PathBuilder<?> associationPath = entity.get(associationName);
            query = query.join(associationPath);

            // Store join path for later use in where
            associationMap.put(associationName, associationPath);
            LOGGER.trace("Added join {} -> {} as {}...", entity.getType(), associationPath, associationName);
        }
        return query;
    }

    /**
     * Prepares filter part for a query of findByCriteria
     * 
     * @param entity
     * @param filterByAssociations
     * @param datatablesCriterias
     * @param associationMap
     * @param filtersByColumnPredicate
     * @return
     */
    private <T> BooleanBuilder prepareQueryFilterPart(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, DatatablesCriterias datatablesCriterias,
            Map<String, PathBuilder<?>> associationMap, BooleanBuilder filtersByColumnPredicate) {
        // Add filterable columns only

        LOGGER.debug("Preparing filter-column expression for entity {}...", entity.getType());

        Predicate filterExpression;

        for (ColumnDef column : datatablesCriterias.getColumnDefs()) {

            // Each column has its own search by value
            String searchStr = column.getSearch();

            // true if the search must include this column
            boolean findInColumn = column.isFilterable() && StringUtils.isNotEmpty(searchStr);

            if (findInColumn) {

                // Entity field name and type
                String fieldName = unescapeDot(column.getName());

                LOGGER.trace("Preparing filter for '{}' by '{}'...", fieldName, searchStr);

                // On column search, connect where clauses together by
                // AND
                // because we want found the records which columns
                // match with column filters
                filterExpression = querydslUtilsBean.createFilterExpression(entity, fieldName, searchStr);

                filtersByColumnPredicate = filtersByColumnPredicate.and(filterExpression);

                LOGGER.trace("filtersByColumnPredicate AND '{}'", filterExpression);

                // TODO: Este codigo se puede pasar a QuerydslUtils ?

                // If column is an association and there are given
                // join attributes, add those attributes to WHERE
                // predicates
                List<String> attributes = filterByAssociations.get(fieldName);
                if (attributes != null && attributes.size() > 0) {

                    // Filters of associated entity properties
                    BooleanBuilder filtersByAssociationPredicate = new BooleanBuilder();

                    PathBuilder<?> associationPath = associationMap.get(fieldName);
                    List<String> associationFields = filterByAssociations.get(fieldName);

                    for (String associationFieldName : associationFields) {

                        // On association search, connect
                        // associated entity where clauses by OR
                        // because all assoc entity properties are
                        // inside the same column and any of its
                        // property value can match with given search
                        // value
                        filterExpression = querydslUtilsBean.createFilterExpression(associationPath,
                                associationFieldName, searchStr);
                        filtersByAssociationPredicate = filtersByAssociationPredicate.or(filterExpression);

                        LOGGER.trace("filtersByAssociationPredicate OR '{}'", filterExpression);
                    }

                    filtersByColumnPredicate = filtersByColumnPredicate
                            .and(filtersByAssociationPredicate.getValue());

                    LOGGER.trace("filtersByColumnPredicate AND '{}'", filtersByAssociationPredicate.getValue());
                }
            }
        }

        LOGGER.debug("Final filtersByColumnPredicate  =  '{}'", filtersByColumnPredicate);
        return filtersByColumnPredicate;
    }

    /**
     * Prepare search part for a query of findByCriteria
     * 
     * @param entity
     * @param filterByAssociations
     * @param datatablesCriterias
     * @param findInAllColumns
     * @param associationMap
     * @param filtersByTablePredicate
     * @return
     */
    private <T> BooleanBuilder prepareQuerySearchPart(PathBuilder<T> entity,
            Map<String, List<String>> filterByAssociations, DatatablesCriterias datatablesCriterias,
            boolean findInAllColumns, Map<String, PathBuilder<?>> associationMap,
            BooleanBuilder filtersByTablePredicate) {
        String searchStr = datatablesCriterias.getSearch();
        if (StringUtils.isEmpty(searchStr)) {
            // Nothing to do
            return filtersByTablePredicate;
        }
        LOGGER.debug("Preparing search expression for '{}' string on entity {}...", searchStr, entity.getType());
        if (findInAllColumns) {
            boolean expressionExists = false;
            // Add filterable columns only
            for (ColumnDef column : datatablesCriterias.getColumnDefs()) {
                if (column.isFilterable()) {

                    // Entity field name and type
                    String fieldName = unescapeDot(column.getName());
                    LOGGER.trace("Check expression column {}...", fieldName);

                    // Find in all columns means we want to find given
                    // value in at least one entity property, so we must
                    // join the where clauses by OR
                    Predicate expression = querydslUtilsBean.createSearchExpression(entity, fieldName, searchStr);
                    if (expression != null) {
                        filtersByTablePredicate = filtersByTablePredicate.or(expression);
                        LOGGER.trace("Added expression {}", expression);
                        expressionExists = true;
                    }

                    // If column is an association and there are given
                    // join attributes, add those attributes to WHERE
                    // predicates
                    List<String> attributes = filterByAssociations.get(fieldName);
                    if (attributes != null && attributes.size() > 0) {
                        PathBuilder<?> associationPath = associationMap.get(fieldName);
                        List<String> associationFields = filterByAssociations.get(fieldName);

                        for (String associationFieldName : associationFields) {

                            expression = querydslUtilsBean.createSearchExpression(associationPath,
                                    associationFieldName, searchStr);
                            filtersByTablePredicate = filtersByTablePredicate.or(expression);

                            LOGGER.trace("Added expression (by association) {}", expression);
                        }
                    }
                }
            }
            // If expression is null returns error to returns an empty
            // DataSource
            if (!expressionExists) {
                throw new RuntimeException("Expression cannot be null");
            }
        }
        LOGGER.debug("Search expression: {}", filtersByTablePredicate);
        return filtersByTablePredicate;
    }

    /**
     * prepares order part for a query of findByCriteria
     * 
     * @param entity
     * @param orderByAssociations
     * @param datatablesCriterias
     * @param associationMap
     * @return
     */
    @SuppressWarnings("unchecked")
    private <E extends Comparable<?>, T> List<OrderSpecifier<?>> prepareQueryOrder(PathBuilder<T> entity,
            Map<String, List<String>> orderByAssociations, DatatablesCriterias datatablesCriterias,
            Map<String, PathBuilder<?>> associationMap) {
        List<OrderSpecifier<?>> orderSpecifiersList = new ArrayList<OrderSpecifier<?>>();

        if (datatablesCriterias.hasOneSortedColumn()) {
            LOGGER.debug("Preparing order for entity {}", entity.getType());

            OrderSpecifier<?> queryOrder;
            for (ColumnDef column : datatablesCriterias.getSortingColumnDefs()) {

                // If column is not sortable, don't add it to order by clauses
                if (!column.isSortable()) {
                    continue;
                }

                // If no sort direction provided, don't add this column to
                // order by clauses
                if (column.getSortDirection() == null) {
                    LOGGER.debug("Column {} ignored: not sortDirection", column.getName());
                    continue;
                }

                // Convert Datatables sort direction to Querydsl order
                Order order = Order.DESC;
                if (column.getSortDirection() == SortDirection.ASC) {
                    order = Order.ASC;
                }

                // Entity field name and type. Type must extend Comparable
                // interface
                String fieldName = unescapeDot(column.getName());

                LOGGER.trace("Adding column {} {}...", fieldName, order);

                Class<E> fieldType = (Class<E>) querydslUtilsBean.getFieldType(fieldName, entity);

                List<String> attributes = orderByAssociations.get(fieldName);
                try {
                    // If column is an association and there are given
                    // order by attributes, add those attributes to ORDER BY
                    // clauses
                    if (attributes != null && attributes.size() > 0) {
                        PathBuilder<?> associationPath = associationMap.get(fieldName);
                        List<String> associationFields = orderByAssociations.get(fieldName);

                        for (String associationFieldName : associationFields) {

                            // Get associated entity field type
                            Class<E> associationFieldType = (Class<E>) BeanUtils.findPropertyType(
                                    associationFieldName, ArrayUtils.<Class<?>>toArray(fieldType));
                            queryOrder = querydslUtilsBean.createOrderSpecifier(associationPath,
                                    associationFieldName, associationFieldType, order);
                            orderSpecifiersList.add(queryOrder);
                            LOGGER.trace("Added order: {}", queryOrder);
                        }
                    }
                    // Otherwise column is an entity property
                    else {
                        queryOrder = querydslUtilsBean.createOrderSpecifier(entity, fieldName, fieldType, order);
                        orderSpecifiersList.add(queryOrder);
                        LOGGER.trace("Added order: {}", queryOrder);
                    }
                } catch (ClassCastException ex) {
                    // Do nothing, on class cast exception order specifier will
                    // be null
                    LOGGER.debug("CastException preparing order for entity {}", entity.getType(), ex);
                    continue;
                } catch (Exception ex) {
                    LOGGER.warn("Exception preparing order for entity {}", entity.getType(), ex);
                    continue;
                }
            }
        }
        return orderSpecifiersList;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> DataSet<Map<String, String>> populateDataSet(List<T> entities, String pkFieldName, long totalRecords,
            long totalDisplayRecords, List<ColumnDef> columns, Map<String, Object> datePatterns) {

        // Check arguments aren't null
        Assert.notNull(pkFieldName);
        Assert.notNull(columns);
        Assert.notNull(conversionService);

        // Map of data rows
        List<Map<String, String>> rows = new ArrayList<Map<String, String>>(entities.size());

        if (CollectionUtils.isEmpty(entities)) {
            return new DataSet<Map<String, String>>(rows, 0l, 0l);
        }

        // If null, create empty Map to avoid control code overload
        if (CollectionUtils.isEmpty(datePatterns)) {
            datePatterns = new HashMap<String, Object>();
        }
        Map<String, SimpleDateFormat> dateFormatters = new HashMap<String, SimpleDateFormat>(datePatterns.size());

        // Prepare required fields
        Set<String> fields = new HashSet<String>();
        fields.add(pkFieldName);

        // Add fields from request
        for (ColumnDef colum : columns) {
            fields.add(colum.getName());
        }

        BeanWrapperImpl entityBean = null;
        String valueStr = null;
        // Populate each row, note a row is a Map containing
        // fieldName = fieldValue
        for (T entity : entities) {
            Map<String, String> row = new HashMap<String, String>(fields.size());
            if (entityBean == null) {
                entityBean = new BeanWrapperImpl(entity);
            } else {
                entityBean.setWrappedInstance(entity);
            }
            for (String fieldName : fields) {

                String unescapedFieldName = unescapeDot(fieldName);

                // check if property exists (trace it else)
                if (!entityBean.isReadableProperty(unescapedFieldName)) {
                    if (LOGGER.isTraceEnabled()) {
                        LOGGER.trace("Property [{}] not fond in bean {} [{}]", unescapedFieldName,
                                entity.getClass().getSimpleName(), entity);
                    }
                    row.put(fieldName, "");
                    continue;
                }

                // Convert field value to string
                valueStr = convertFieldValueToString(datePatterns, dateFormatters, entityBean, entity, fieldName,
                        unescapedFieldName);
                row.put(fieldName, valueStr);

                // Set PK value as DT_RowId
                // Note when entity has composite PK Roo generates the need
                // convert method and adds it to ConversionService, so
                // when processed field is the PK the valueStr is the
                // composite PK instance marshalled to JSON notation and
                // Base64 encoded
                if (pkFieldName.equalsIgnoreCase(fieldName)) {
                    row.put("DT_RowId", valueStr);
                }
            }
            rows.add(row);
        }
        DataSet<Map<String, String>> dataSet = new DataSet<Map<String, String>>(rows, totalRecords,
                totalDisplayRecords);
        return dataSet;
    }

    /**
     * Convert a field value to string
     * 
     * @param datePatterns
     * @param dateFormatters
     * @param conversionService
     * @param entityBean
     * @param entity
     * @param fieldName
     * @param unescapedFieldName
     * @return
     */
    private <T> String convertFieldValueToString(Map<String, Object> datePatterns,
            Map<String, SimpleDateFormat> dateFormatters, BeanWrapperImpl entityBean, T entity, String fieldName,
            String unescapedFieldName) {
        try {
            Object value = null;
            TypeDescriptor fieldDesc = entityBean.getPropertyTypeDescriptor(unescapedFieldName);
            TypeDescriptor strDesc = TypeDescriptor.valueOf(String.class);
            value = entityBean.getPropertyValue(unescapedFieldName);
            if (value == null) {
                return "";
            }

            // For dates
            if (Date.class.isAssignableFrom(value.getClass())
                    || Calendar.class.isAssignableFrom(value.getClass())) {
                SimpleDateFormat formatter = getDateFormatter(datePatterns, dateFormatters,
                        entityBean.getWrappedClass(), unescapedFieldName);
                if (formatter != null) {
                    if (Calendar.class.isAssignableFrom(value.getClass())) {
                        // Gets Date instance as SimpleDateFormat
                        // doesn't works with Calendar
                        value = ((Calendar) value).getTime();
                    }
                    return formatter.format(value);
                }
            }
            String stringValue;
            // Try to use conversion service (uses field descrition
            // to handle field format annotations)
            if (conversionService.canConvert(fieldDesc, strDesc)) {
                stringValue = (String) conversionService.convert(value, fieldDesc, strDesc);
                if (stringValue == null) {
                    stringValue = "";
                }
            } else {
                stringValue = ObjectUtils.getDisplayString(value);
            }
            return stringValue;
        } catch (Exception ex) {
            LOGGER.error(String.format("Error getting value of property [%s] in bean %s [%s]", unescapedFieldName,
                    entity.getClass().getSimpleName(),
                    org.apache.commons.lang3.ObjectUtils.firstNonNull(entity.toString(), "{unknow}")), ex);
            return "";
        }
    }

    /**
     * Get Date formatter by field name
     * <p/>
     * If no pattern found, try standard Roo key
     * {@code uncapitalize( ENTITY ) + "_" + lower_case( FIELD ) + "_date_format"}
     * 
     * @param datePatterns Contains field name and related data pattern
     * @param entityClass Entity class to which the field belong to
     * @param fieldName Field to search pattern
     * @return
     */
    private SimpleDateFormat getDateFormatter(Map<String, Object> datePatterns,
            Map<String, SimpleDateFormat> dateFormatters, Class<?> entityClass, String fieldName) {

        SimpleDateFormat result = null;
        String lowerCaseFieldName = fieldName.toLowerCase();
        result = dateFormatters.get(lowerCaseFieldName);
        if (result != null) {
            return result;
        } else if (dateFormatters.containsKey(lowerCaseFieldName)) {
            return null;
        }

        // Get pattern by field name
        String pattern = (String) datePatterns.get(lowerCaseFieldName);
        if (StringUtils.isEmpty(pattern)) {
            // Try to get the name of entity class (without javassit suffix)
            String baseClass = StringUtils.substringBefore(entityClass.getSimpleName(), "$");// );"_$");
            // try to get pattern by Roo key
            String rooKey = StringUtils.uncapitalize(baseClass).concat("_").concat(lowerCaseFieldName)
                    .concat("_date_format");

            pattern = (String) datePatterns.get(rooKey);
        }
        if (!StringUtils.isEmpty(pattern)) {
            result = new SimpleDateFormat(pattern);
        }
        dateFormatters.put(lowerCaseFieldName, result);
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HtmlTable makeHtmlTable(List<Map<String, String>> data, DatatablesCriterias criterias,
            ExportConf exportConf, HttpServletRequest request) {

        ColumnStep tableBuilder = new HtmlTableBuilder<Map<String, String>>().newBuilder("tableId", data, request);

        // Obtain exportable columns
        String exportTypeExtension = StringUtils.lowerCase(exportConf.getType().getExtension());
        String thisFormatExportColumnsStr = request.getParameter(exportTypeExtension.concat("ExportColumns"));
        if (StringUtils.isEmpty(thisFormatExportColumnsStr)) {
            thisFormatExportColumnsStr = "";
        }
        String allFormatExportColumnsStr = request.getParameter("allExportColumns");
        if (StringUtils.isEmpty(allFormatExportColumnsStr)) {
            allFormatExportColumnsStr = "";
        }
        List<String> thisFormatExporColumns = Arrays.asList(StringUtils.split(thisFormatExportColumnsStr, ","));
        List<String> allFormatExportColumns = Arrays.asList(StringUtils.split(allFormatExportColumnsStr, ","));

        BeforeEndStep columns = null;
        if (!allFormatExportColumns.isEmpty() || !thisFormatExporColumns.isEmpty()) {

            // Obtain the column titles
            Map<String, String> columnsTitleMap = new HashMap<String, String>();
            String columnsTitleStr = request.getParameter("columnsTitle");
            columnsTitleStr = StringUtils.substring(columnsTitleStr, 1, (columnsTitleStr.length() - 1));
            List<String> columnsTitleList = Arrays.asList(StringUtils.split(columnsTitleStr, ","));
            for (String columnsTitle : columnsTitleList) {
                String[] columsTitleArray = StringUtils.split(columnsTitle, "||");
                if (columsTitleArray.length == 2) {
                    columnsTitleMap.put(columsTitleArray[0].trim(), columsTitleArray[1].trim());
                }
            }

            List<ColumnDef> columnDefs = criterias.getColumnDefs();
            for (ColumnDef columnDef : columnDefs) {
                String columnProperty = columnDef.getName();
                if (allFormatExportColumns.contains(columnProperty)
                        || thisFormatExporColumns.contains(columnProperty)) {
                    String columnTitle = columnsTitleMap.get(columnProperty);
                    if (StringUtils.isBlank(columnTitle)) {
                        columnTitle = columnProperty;
                    }
                    columnTitle = StringUtils.replace(columnTitle, "~~", ",");
                    columns = tableBuilder.column().fillWithProperty(columnProperty).title(columnTitle);
                }
            }
        }
        if (columns == null) {
            columns = tableBuilder.column().fillWithProperty("-").title("---");
        }

        return columns.configureExport(exportConf).build();
    }

    /**
     * Unescapes the string to represent it with dot ".", that is, the default
     * character used to indicate attributes of an object (p.e. "user.name")
     * 
     * @param str the string to unescape.
     * @return the string unescaped.
     */
    private String unescapeDot(String str) {
        return str.replace(SEPARATOR_FIELDS_ESCAPED, SEPARATOR_FIELDS);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean checkFilterExpressions(Class<?> type, String expression) {
        // By default filter is not correct

        // Checking String filters
        if (String.class == type) {
            return checkStringFilters(expression);
        } else if (Boolean.class == type || boolean.class == type) {
            return checkBooleanFilters(expression);
        } else if (Number.class.isAssignableFrom(type) || QuerydslUtilsBean.NUMBER_PRIMITIVES.contains(type)) {
            return checkNumericFilters(expression);
        } else if (Date.class.isAssignableFrom(type) || Calendar.class.isAssignableFrom(type)) {
            return checkDateFilters(expression);
        }
        return false;
    }

    @Override
    public boolean checkStringFilters(String expression) {
        // All operations
        String endsOperation = "ENDS";
        String startsOperation = "STARTS";
        String containsOperation = "CONTAINS";
        String isEmptyOperation = "ISEMPTY";
        String isNotEmptyOperation = "ISNOTEMPTY";
        String isNullOperation = ISNULL_OPE;
        String isNotNullOperation = NOTNULL_OPE;

        if (messageSource != null) {
            endsOperation = messageSource.getMessage("global.filters.operations.string.ends", null,
                    LocaleContextHolder.getLocale());
            startsOperation = messageSource.getMessage("global.filters.operations.string.starts", null,
                    LocaleContextHolder.getLocale());
            containsOperation = messageSource.getMessage("global.filters.operations.string.contains", null,
                    LocaleContextHolder.getLocale());
            isEmptyOperation = messageSource.getMessage("global.filters.operations.string.isempty", null,
                    LocaleContextHolder.getLocale());
            isNotEmptyOperation = messageSource.getMessage("global.filters.operations.string.isnotempty", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_ISNULL_OPE, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_NOTNULL_OPE, null, LocaleContextHolder.getLocale());
        }

        // If written expression is ENDS operation
        Pattern endsOperator = Pattern.compile(String.format("%s[(]([a-zA-Z\\s\\d]*)[)]", endsOperation));
        Matcher endsMatcher = endsOperator.matcher(expression);

        if (endsMatcher.matches()) {
            return true;
        }

        // If written expression is STARTS operation
        Pattern startsOperator = Pattern.compile(String.format("%s[(](.+)[)]$", startsOperation));
        Matcher startsMatcher = startsOperator.matcher(expression);

        if (startsMatcher.matches()) {
            return true;
        }

        // If written expression is CONTAINS operation
        Pattern containsOperator = Pattern.compile(String.format("%s[(](.+)[)]$", containsOperation));
        Matcher containsMatcher = containsOperator.matcher(expression);

        if (containsMatcher.matches()) {
            return true;
        }

        // If written expression is ISEMPTY operation
        Pattern isEmptyOperator = Pattern.compile(String.format("%s", isEmptyOperation));
        Matcher isEmptyMatcher = isEmptyOperator.matcher(expression);
        if (isEmptyMatcher.matches()) {
            return true;

        }

        // If written expression is ISNOTEMPTY operation
        Pattern isNotEmptyOperator = Pattern.compile(String.format("%s", isNotEmptyOperation));
        Matcher isNotEmptyMatcher = isNotEmptyOperator.matcher(expression);
        if (isNotEmptyMatcher.matches()) {
            return true;

        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(expression);
        if (isNullMatcher.matches()) {
            return true;

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(expression);
        if (isNotNullMatcher.matches()) {
            return true;

        }

        // If written expression is a symbol operation expression

        // Getting expressions with symbols
        Pattern symbolOperator = Pattern.compile("[=]?(.+)");
        Matcher symbolMatcher = symbolOperator.matcher(expression);

        if (symbolMatcher.matches()) {
            return true;
        }

        return false;
    }

    @Override
    public boolean checkBooleanFilters(String expression) {
        // Getting all operations
        String trueOperation = "TRUE";
        String falseOperation = "FALSE";
        String isNullOperation = ISNULL_OPE;
        String isNotNullOperation = NOTNULL_OPE;

        if (messageSource != null) {
            trueOperation = messageSource.getMessage("global.filters.operations.boolean.true", null,
                    LocaleContextHolder.getLocale());
            falseOperation = messageSource.getMessage("global.filters.operations.boolean.false", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_ISNULL_OPE, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_NOTNULL_OPE, null, LocaleContextHolder.getLocale());
        }

        // If written function is TRUE
        Pattern trueOperator = Pattern.compile(String.format("%s", trueOperation));
        Matcher trueMatcher = trueOperator.matcher(expression);

        if (trueMatcher.matches()) {
            return true;
        }

        // If written function is FALSE
        Pattern falseOperator = Pattern.compile(String.format("%s", falseOperation));
        Matcher falseMatcher = falseOperator.matcher(expression);

        if (falseMatcher.matches()) {
            return true;
        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(expression);
        if (isNullMatcher.matches()) {
            return true;

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(expression);
        if (isNotNullMatcher.matches()) {
            return true;

        }

        return false;
    }

    @Override
    public boolean checkNumericFilters(String expression) {
        if (NumberUtils.isNumber(expression)) {
            return true;
        } else {
            // Getting expressions with symbols
            Pattern symbolOperator = Pattern.compile("([!=><][=>]?)([-]?[\\d.,]*)");
            Matcher symbolMatcher = symbolOperator.matcher(expression);

            if (symbolMatcher.matches()) {

                String symbolExpression = symbolMatcher.group(1);
                String value = symbolMatcher.group(2);

                if (!StringUtils.isBlank(value)) {
                    if (symbolExpression.equals("=") || symbolExpression.equals("==")) {
                        return true;
                    } else if (symbolExpression.equals(">") || symbolExpression.equals(">>")) {
                        return true;
                    } else if (symbolExpression.equals("<")) {
                        return true;
                    } else if (symbolExpression.equals(">=")) {
                        return true;
                    } else if (symbolExpression.equals("<=")) {
                        return true;
                    } else if (symbolExpression.equals("!=") || symbolExpression.equals("<>")) {
                        return true;
                    }
                }
            }

            // Get all operations
            String isNullOperation = ISNULL_OPE;
            String isNotNullOperation = NOTNULL_OPE;
            String betweenOperation = "BETWEEN";

            if (messageSource != null) {
                isNullOperation = messageSource.getMessage(G_ISNULL_OPE, null, LocaleContextHolder.getLocale());
                isNotNullOperation = messageSource.getMessage(G_NOTNULL_OPE, null, LocaleContextHolder.getLocale());
                betweenOperation = messageSource.getMessage("global.filters.operations.number.between", null,
                        LocaleContextHolder.getLocale());
            }

            // If written function is BETWEEN function
            Pattern betweenFunctionOperator = Pattern
                    .compile(String.format("%s[(]([-]?[\\d.,]*);([-]?[\\d.,]*)[)]", betweenOperation));
            Matcher betweenFunctionMatcher = betweenFunctionOperator.matcher(expression);

            if (betweenFunctionMatcher.matches()) {
                // Getting valueFrom and valueTo
                String valueFrom = betweenFunctionMatcher.group(1);
                String valueTo = betweenFunctionMatcher.group(2);
                if (!StringUtils.isBlank(valueFrom) && !StringUtils.isBlank(valueTo)) {
                    return true;
                }
            }

            // If written expression is ISNULL operation
            Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
            Matcher isNullMatcher = isNullOperator.matcher(expression);
            if (isNullMatcher.matches()) {
                return true;

            }

            // If written expression is ISNOTNULL operation
            Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
            Matcher isNotNullMatcher = isNotNullOperator.matcher(expression);
            if (isNotNullMatcher.matches()) {
                return true;

            }
        }
        return false;
    }

    @Override
    public boolean checkDateFilters(String expression) {

        // All possible operations
        String date = "DATE";
        String year = "YEAR";
        String month = "MONTH";
        String day = "DAY";
        String between = "BETWEEN";
        String isNullOperation = ISNULL_OPE;
        String isNotNullOperation = NOTNULL_OPE;

        String datePattern = "dd/MM/yyyy";

        if (messageSource != null) {
            date = messageSource.getMessage("global.filters.operations.date.date", null,
                    LocaleContextHolder.getLocale());
            year = messageSource.getMessage("global.filters.operations.date.year", null,
                    LocaleContextHolder.getLocale());
            month = messageSource.getMessage("global.filters.operations.date.month", null,
                    LocaleContextHolder.getLocale());
            day = messageSource.getMessage("global.filters.operations.date.day", null,
                    LocaleContextHolder.getLocale());
            between = messageSource.getMessage("global.filters.operations.date.between", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_ISNULL_OPE, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_NOTNULL_OPE, null, LocaleContextHolder.getLocale());
            datePattern = messageSource.getMessage("global.filters.operations.date.pattern", null,
                    LocaleContextHolder.getLocale());
        }

        // Getting simpleDateFormat
        DateFormat dateFormat = new SimpleDateFormat(datePattern);

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(expression);
        if (isNullMatcher.matches()) {
            return true;

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(expression);
        if (isNotNullMatcher.matches()) {
            return true;

        }

        // Creating regex to get DATE operator
        Pattern dateOperator = Pattern.compile(String.format("%s[(]([\\d\\/]*)[)]", date));
        Matcher dateMatcher = dateOperator.matcher(expression);

        if (dateMatcher.matches()) {
            try {
                String dateValue = dateMatcher.group(1);
                Date dateToFilter = dateFormat.parse(dateValue);

                Calendar searchCal = Calendar.getInstance();
                searchCal.setTime(dateToFilter);

                return true;
            } catch (ParseException e) {
                return false;
            }
        }

        // Creating regex to get YEAR operator
        Pattern yearOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", year));
        Matcher yearMatcher = yearOperator.matcher(expression);

        if (yearMatcher.matches()) {
            return true;
        }

        // Creating regex to get MONTH operator
        Pattern monthOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", month));
        Matcher monthMatcher = monthOperator.matcher(expression);

        if (monthMatcher.matches()) {
            return true;
        }

        // Creating regex to get DAY operator
        Pattern dayOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", day));
        Matcher dayMatcher = dayOperator.matcher(expression);

        if (dayMatcher.matches()) {
            return true;
        }

        // Creating regex to get BETWEEN operator
        Pattern betweenOperator = Pattern.compile(String.format("%s[(]([\\d\\/]*);([\\d\\/]*)[)]", between));
        Matcher betweenMatcher = betweenOperator.matcher(expression);

        if (betweenMatcher.matches()) {

            String valueFrom = betweenMatcher.group(1);
            String valueTo = betweenMatcher.group(2);

            if (StringUtils.isNotBlank(valueFrom) && StringUtils.isNotBlank(valueTo)) {

                try {

                    Date dateFrom = dateFormat.parse(valueFrom);
                    Date dateTo = dateFormat.parse(valueTo);

                    Calendar dateFromCal = Calendar.getInstance();
                    dateFromCal.setTime(dateFrom);

                    Calendar dateToCal = Calendar.getInstance();
                    dateToCal.setTime(dateTo);

                    return true;

                } catch (Exception e) {
                    return false;
                }
            }

        }

        return false;
    }

}