net.sf.companymanager.qbe.SearchParameters.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.companymanager.qbe.SearchParameters.java

Source

/*******************************************************************************
 * Copyright (c) 2014 antoniomariasanchez at gmail.com.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     antoniomaria - initial API and implementation
 ******************************************************************************/
/*
 * Copyright 2012 JAXIO http://www.jaxio.com 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 net.sf.companymanager.qbe;

import static net.sf.companymanager.qbe.Ranges.RangeDate.rangeDate;
import static net.sf.companymanager.qbe.Ranges.RangeLocalDate.rangeLocalDate;
import static net.sf.companymanager.qbe.Ranges.RangeLocalDateTime.rangeLocalDateTime;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.JoinType;
import javax.persistence.metamodel.SingularAttribute;

import net.sf.companymanager.domain.support.Persistable;
import net.sf.companymanager.qbe.Ranges.RangeDate;
import net.sf.companymanager.qbe.Ranges.RangeInteger;
import net.sf.companymanager.qbe.Ranges.RangeLocalDate;
import net.sf.companymanager.qbe.Ranges.RangeLocalDateTime;

import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * The SearchParameters is used to pass search parameters to the DAO layer.
 * 
 * Its usage keeps 'find' method signatures in the DAO/Service layer simple.
 * 
 * A SearchParameters helps you drive your search in the following areas:
 * <ul>
 * <li>Configure the search mode (EQUALS, LIKE, ...)</li>
 * <li>Pagination: it allows you to limit your search results to a specific
 * range.</li>
 * <li>Allow you to specify ORDER BY and ASC/DESC</li>
 * <li>Enable/disable case sensitivity</li>
 * <li>Enable/disable 2d level cache</li>
 * <li>LIKE search against all string values: simply set the searchPattern
 * property</li>
 * <li>Named query: if you set a named query it will be executed. Named queries
 * can be defined in annotation or src/main/resources/META-INF/orm.xml</li>
 * </ul>
 * 
 * Note : All requests are limited to a maximum number of elements to prevent
 * resource exhaustion.
 * 
 * @see ByExampleSpecification
 * @see SearchMode
 * @see OrderBy
 * @see Range
 * @see NamedQueryUtil
 * @see PropertySelector
 * @see EntitySelector
 */
public class SearchParameters {

    private boolean andMode = true;

    private SearchMode searchMode = SearchMode.EQUALS;

    // cache
    private Boolean cacheable = true;

    private String cacheRegion;
    // technical parameters
    private boolean caseSensitive = false;

    // distinct
    private Boolean distinct = false;

    // entity selectors
    private final List<EntitySelector<?, ? extends Persistable, ?>> entities = new ArrayList<EntitySelector<?, ? extends Persistable, ?>>();

    private int firstResult = 0;
    // Joins
    private final Map<JoinType, List<SingularAttribute<?, ?>>> joinAttributes = new HashMap<JoinType, List<SingularAttribute<?, ?>>>();

    // Pagination
    private int maxResults = 500;

    // named query related
    private String namedQuery;

    private final List<OrderBy> orders = new ArrayList<OrderBy>();

    private Map<String, Object> parameters = new HashMap<String, Object>();

    // property selectors
    private final List<PropertySelector<?, ?>> properties = new ArrayList<PropertySelector<?, ?>>();
    // ranges
    private final List<Range<?, ?>> ranges = new ArrayList<Range<?, ?>>();

    // pattern to match against all strings.
    private String searchPattern;

    public SearchParameters() {

    }

    // -----------------------------------
    // Predicate mode
    // -----------------------------------
    public boolean isAndMode() {
        return andMode;
    }

    public void setAndMode(boolean andMode) {
        this.andMode = andMode;
    }

    // -----------------------------------
    // SearchMode
    // -----------------------------------

    public SearchParameters(final EntitySelector<?, ? extends Persistable, ?> entitySelector) {
        addEntity(entitySelector);
    }

    // -----------------------------------
    // Search by property selector support
    // -----------------------------------

    public List<PropertySelector<?, ?>> getProperties() {
        return properties;
    }

    public void addProperty(PropertySelector<?, ?> propertySelector) {
        properties.add(propertySelector);
    }

    public boolean hasProperties() {
        return !properties.isEmpty();
    }

    // -----------------------------------
    // Search by range support
    // -----------------------------------
    public SearchParameters(final Range<?, ?> range) {
        addRange(range);
    }

    public SearchParameters(final SearchMode searchMode) {
        setSearchMode(searchMode);
    }

    public void addEntity(final EntitySelector<?, ? extends Persistable, ?> entitySelector) {
        entities.add(entitySelector);
    }

    public void addNamedQueryParameter(final String name, final Object value) {
        Validate.notNull(name, "name must not be null");
        Validate.notNull(value, "value must not be null");
        parameters.put(name, value);
    }

    public void addOrderBy(final OrderBy orderBy) {
        Validate.notNull(orderBy, "orderBy must not be null");
        orders.add(orderBy);
    }

    public void addOrderBy(final SingularAttribute<? extends Persistable, ? extends Serializable> attribute) {
        Validate.notNull(attribute, "attribute must not be null");
        orders.add(new OrderBy(attribute));
    }

    // -----------------------------------
    // Named query support
    // -----------------------------------

    public void addOrderBy(final SingularAttribute<? extends Persistable, ? extends Serializable> attribute,
            final OrderByDirection direction) {
        Validate.notNull(attribute, "fieldName must not be null");
        Validate.notNull(direction, "direction must not be null");
        orders.add(new OrderBy(attribute, direction));
    }

    public void addOrderBy(final String fieldName) {
        Validate.notNull(fieldName, "fieldName must not be null");
        orders.add(new OrderBy(fieldName));
    }

    public void addOrderBy(final String fieldName, final OrderByDirection direction) {
        Validate.notNull(fieldName, "fieldName must not be null");
        Validate.notNull(direction, "direction must not be null");
        orders.add(new OrderBy(fieldName, direction));
    }

    public void addRange(final Range<?, ?> range) {
        ranges.add(range);
    }

    public SearchParameters after(final SingularAttribute<?, Date> field, final Date from) {
        RangeDate<?> rangeDate = rangeDate(field);
        rangeDate.setFrom(from);
        addRange(rangeDate);
        return this;
    }

    public SearchParameters after(final SingularAttribute<?, LocalDate> field, final LocalDate from) {
        RangeLocalDate<?> rangeLocalDate = rangeLocalDate(field);
        rangeLocalDate.setFrom(from);
        addRange(rangeLocalDate);
        return this;
    }

    public SearchParameters after(final SingularAttribute<?, LocalDateTime> field, final LocalDateTime from) {
        RangeLocalDateTime<?> rangeLocalDateTime = rangeLocalDateTime(field);
        rangeLocalDateTime.setFrom(from);
        addRange(rangeLocalDateTime);
        return this;
    }

    public SearchParameters anywhere() {
        return searchMode(SearchMode.ANYWHERE);
    }

    public SearchParameters before(final SingularAttribute<?, Date> field, final Date setToto) {
        RangeDate<?> rangeDate = rangeDate(field);
        rangeDate.setTo(setToto);
        addRange(rangeDate);
        return this;
    }

    // -----------------------------------
    // Search pattern support
    // -----------------------------------

    public SearchParameters before(final SingularAttribute<?, LocalDate> field, final LocalDate setToto) {
        RangeLocalDate<?> rangeLocalDate = rangeLocalDate(field);
        rangeLocalDate.setTo(setToto);
        addRange(rangeLocalDate);
        return this;
    }

    public SearchParameters before(final SingularAttribute<?, LocalDateTime> field, final LocalDateTime setToto) {
        RangeLocalDateTime<?> rangeLocalDateTime = rangeLocalDateTime(field);
        rangeLocalDateTime.setTo(setToto);
        addRange(rangeLocalDateTime);
        return this;
    }

    public SearchParameters cacheable(final boolean cacheable) {
        setCacheable(cacheable);
        return this;
    }

    public SearchParameters cacheRegion(final String cacheRegion) {
        setCacheRegion(cacheRegion);
        return this;
    }

    // -----------------------------------
    // Case sensitiveness support
    // -----------------------------------

    /**
     * Fluently set the case sensitiveness to false.
     * @return SearchParameters
     */
    public SearchParameters caseInsensitive() {
        setCaseSensitive(false);
        return this;
    }

    /**
     * Fluently set the case sensitiveness to true.
     * 
     * @return SearchParameters
     */
    public SearchParameters caseSensitive() {
        setCaseSensitive(true);
        return this;
    }

    /**
     * Fluently set the case sensitiveness. Defaults to false.
     * 
     * @param caseSensitive
     * @return SearchParameters
     */
    public SearchParameters caseSensitive(final boolean caseSensitive) {
        setCaseSensitive(caseSensitive);
        return this;
    }

    public void clearEntity() {
        entities.clear();
    }

    public void clearOrders() {
        orders.clear();
    }

    public void clearProperties() {
        properties.clear();
    }

    // -----------------------------------
    // Order by support
    // -----------------------------------

    public void clearRanges() {
        ranges.clear();
    }

    public SearchParameters disableCache() {
        setCacheable(false);
        return this;
    }

    public SearchParameters distinct() {
        distinct = true;
        return this;
    }

    public SearchParameters distinct(final boolean value) {
        distinct = value;
        return this;
    }

    public SearchParameters enableCache() {
        setCacheable(true);
        return this;
    }

    /**
     * Use the ENDING_LIKE
     * 
     * @return SearchParameters
     */
    public SearchParameters endingLike() {
        return searchMode(SearchMode.ENDING_LIKE);
    }

    /**
     * Add the passed {@link EntitySelector} in order to construct an OR
     * predicate for the underlying foreign key.
     * 
     * @return SearchParameters
     */
    public SearchParameters entity(final EntitySelector<?, ? extends Persistable, ?> entitySelector) {
        addEntity(entitySelector);
        return this;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SearchParameters entity(final SingularAttribute<?, ?> field, final Persistable... values) {
        return entity(new EntitySelector(field, values));
    }

    /**
     * Use the EQUALS @{link SearchMode}.
     * 
     * @see SearchMode#EQUALS
     * 
     * @return SearchParameters
     */
    public SearchParameters equals() {
        return searchMode(SearchMode.EQUALS);
    }

    public SearchParameters firstResult(final int firstResult) {
        setFirstResult(firstResult);
        return this;
    }

    public String getCacheRegion() {
        return cacheRegion;
    }

    public List<EntitySelector<?, ? extends Persistable, ?>> getEntities() {
        return entities;
    }

    public int getFirstResult() {
        return firstResult;
    }

    /**
     * Returns the attribute (x-to-one association) which must be fetched with
     * an inner join.
     * @return List
     */
    public List<SingularAttribute<?, ?>> getInnerJoinAttributes() {
        return getJoinAttributes(JoinType.INNER);
    }

    public Map<JoinType, List<SingularAttribute<?, ?>>> getJoinAttributes() {
        return joinAttributes;
    }

    /**
     * Returns the attribute (x-to-one association) which must be fetched with a
     * left join.
     * @return List
     */
    public List<SingularAttribute<?, ?>> getLeftJoinAttributes() {
        return getJoinAttributes(JoinType.LEFT);
    }

    public int getMaxResults() {
        return maxResults;
    }

    /**
     * Return the name of the named query to be used by the DAO layer.
     * @return String
     */
    public String getNamedQuery() {
        return namedQuery;
    }

    /**
     * Return the value of the passed parameter name.
     * @return value
     */
    public Object getNamedQueryParameter(final String parameterName) {
        return parameters.get(parameterName);
    }

    /**
     * The parameters associated with the named query, if any.
     * @return Map
     */
    public Map<String, Object> getNamedQueryParameters() {
        return parameters;
    }

    public List<OrderBy> getOrders() {
        return orders;
    }

    public List<Range<?, ?>> getRanges() {
        return ranges;
    }

    /**
     * Return the @{link SearchMode}. It defaults to EQUALS.
     * 
     * @see SearchMode#EQUALS
     * @return SearchMode
     */
    public SearchMode getSearchMode() {
        return searchMode;
    }

    /**
     * Returns the search pattern to be used by the DAO layer.
     * @return String
     */
    public String getSearchPattern() {
        return searchPattern;
    }

    public SearchParameters greather(final SingularAttribute<?, Integer> field, final Integer value) {
        RangeInteger<?> rangeInteger = RangeInteger.rangeInteger(field);
        rangeInteger.setFrom(value);
        addRange(rangeInteger);
        return this;
    }

    public boolean hasCacheRegion() {
        return isNotBlank(cacheRegion);
    }

    /**
     * Returns true if a named query has been set, false otherwise. When it
     * returns true, the DAO layer will call the
     * namedQuery.
     * @return boolean
     */
    public boolean hasNamedQuery() {
        return isNotBlank(namedQuery);
    }

    public boolean hasOrders() {
        return !orders.isEmpty();
    }

    /**
     * When it returns true, it indicates to the DAO layer to use the passed
     * searchPattern on all string properties.
     * @return boolean
     */
    public boolean hasSearchPattern() {
        return isNotBlank(searchPattern);
    }

    /**
     * The passed attribute (x-to-one association) will be fetched with a inner
     * join.
     * @return SearchParameters
     */
    public SearchParameters innerJoin(final SingularAttribute<?, ?> xToOneAttribute) {
        getInnerJoinAttributes().add(xToOneAttribute);
        return this;
    }

    public boolean isCacheable() {
        return cacheable;
    }

    public boolean isCaseInsensitive() {
        return !caseSensitive;
    }

    public boolean isCaseSensitive() {
        return caseSensitive;
    }

    public boolean isDistinct() {
        return distinct;
    }

    /**
     * The passed attribute (x-to-one association) will be fetched with a left
     * join.
     * @return SearchParameters
     */
    public SearchParameters leftJoin(final SingularAttribute<?, ?> xToOneAttribute) {
        getLeftJoinAttributes().add(xToOneAttribute);
        return this;
    }

    // -----------------------------------
    // Search by entity selector support
    // -----------------------------------

    /**
     * Use the LIKE @{link SearchMode}.
     * 
     * @see SearchMode#LIKE
     * @return SearchParameters
     */
    public SearchParameters like() {
        return searchMode(SearchMode.LIKE);
    }

    public SearchParameters lower(final SingularAttribute<?, Integer> field, final Integer value) {
        RangeInteger<?> rangeInteger = RangeInteger.rangeInteger(field);
        rangeInteger.setTo(value);
        addRange(rangeInteger);
        return this;
    }

    public SearchParameters maxResults(final int maxResults) {
        setMaxResults(maxResults);
        return this;
    }

    /**
     * Fluently set the named query to be used by the DAO layer. Null by
     * default.
     * @return SearchParameters
     */
    public SearchParameters namedQuery(final String namedQuery) {
        setNamedQuery(namedQuery);
        return this;
    }

    /**
     * Fluently set the parameters for the named query.
     * @return SearchParameters
     */
    public SearchParameters namedQueryParameter(final String name, final Object value) {
        addNamedQueryParameter(name, value);
        return this;
    }

    /**
     * Fluently set the parameters for the named query.
     * @return SearchParameters
     */
    public SearchParameters namedQueryParameters(final Map<String, Object> parameters) {
        setNamedQueryParameters(parameters);
        return this;
    }

    // -----------------------------------
    // Pagination support
    // -----------------------------------

    public SearchParameters noLimit() {
        setMaxResults(-1);
        return this;
    }

    public SearchParameters orderBy(final OrderBy orderBy) {
        addOrderBy(orderBy);
        return this;
    }

    public SearchParameters orderBy(
            final SingularAttribute<? extends Persistable, ? extends Serializable> attribute) {
        addOrderBy(attribute);
        return this;
    }

    public SearchParameters orderBy(
            final SingularAttribute<? extends Persistable, ? extends Serializable> attribute,
            final OrderByDirection direction) {
        addOrderBy(attribute, direction);
        return this;
    }

    public SearchParameters orderBy(final String fieldName) {
        addOrderBy(fieldName);
        return this;
    }

    public SearchParameters orderBy(final String fieldName, final OrderByDirection direction) {
        addOrderBy(fieldName, direction);
        return this;
    }

    /**
     * Add the passed {@link PropertySelector} in order to construct an OR
     * predicate for the corresponding property.
     * @return SearchParameters
     */
    public SearchParameters property(final PropertySelector<?, ?> propertySelector) {
        addProperty(propertySelector);
        return this;
    }

    /**
     * Add the passed {@link Range} in order to create a 'range' predicate on
     * the corresponding property.
     * @return SearchParameters
     */
    public SearchParameters range(final Range<?, ?> range) {
        addRange(range);
        return this;
    }

    public SearchParameters range(final SingularAttribute<?, Date> field, final Date from, final Date to) {
        addRange(rangeDate(field, from, to));
        return this;
    }

    public SearchParameters range(final SingularAttribute<?, LocalDate> field, final LocalDate from,
            final LocalDate to) {
        addRange(rangeLocalDate(field, from, to));
        return this;
    }

    public SearchParameters range(final SingularAttribute<?, LocalDateTime> field, final LocalDateTime from,
            final LocalDateTime to) {
        addRange(rangeLocalDateTime(field, from, to));
        return this;
    }

    /**
     * Fluently set the @{link SearchMode}. It defaults to EQUALS.
     * 
     * @see SearchMode#EQUALS
     * @return SearchParameters
     */
    public SearchParameters searchMode(final SearchMode searchMode) {
        setSearchMode(searchMode);
        return this;
    }

    // -----------------------------------
    // Caching support
    // -----------------------------------

    /**
     * Fluently set the pattern which may contains wildcards (ex: "e%r%ka" ).
     * The passed searchPattern is used by the
     * DAO layer on all string properties. Null by default.
     * @return SearchParameters
     */
    public SearchParameters searchPattern(final String searchPattern) {
        setSearchPattern(searchPattern);
        return this;
    }

    /**
     * Default to true.
     */
    public void setCacheable(final boolean cacheable) {
        this.cacheable = cacheable;
    }

    public void setCacheRegion(final String cacheRegion) {
        this.cacheRegion = cacheRegion;
    }

    /**
     * Set the case sensitiveness. Defaults to false.
     * 
     * @param caseSensitive
     */
    public void setCaseSensitive(final boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    }

    public void setFirstResult(final int firstResult) {
        this.firstResult = firstResult;
    }

    /**
     * Set the maximum number of results to retrieve. Pass -1 for no limits.
     */
    public void setMaxResults(final int maxResults) {
        this.maxResults = maxResults;
    }

    /**
     * Set the named query to be used by the DAO layer. Null by default.
     */
    public void setNamedQuery(final String namedQuery) {
        this.namedQuery = namedQuery;
    }

    /**
     * Set the parameters for the named query.
     */
    public void setNamedQueryParameters(final Map<String, Object> parameters) {
        Validate.notNull(parameters, "parameters must not be null");
        this.parameters = parameters;
    }

    /**
     * Fluently set the @{link SearchMode}. It defaults to EQUALS.
     * 
     * @see SearchMode#EQUALS
     */
    public void setSearchMode(final SearchMode searchMode) {
        Validate.notNull(searchMode, "searchMode must not be null");
        this.searchMode = searchMode;
    }

    // -----------------------------------
    // Distinct
    // -----------------------------------

    /**
     * Set the pattern which may contains wildcards (ex: "e%r%ka" ). The passed
     * searchPattern is used by the DAO layer
     * on all string properties. Null by default.
     */
    public void setSearchPattern(final String searchPattern) {
        this.searchPattern = searchPattern;
    }

    /**
     * Use the STARTING_LIKE @{link SearchMode}.
     * 
     * @see SearchMode#STARTING_LIKE
     * @return SearchParameters
     */
    public SearchParameters startingLike() {
        return searchMode(SearchMode.STARTING_LIKE);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    private List<SingularAttribute<?, ?>> getJoinAttributes(final JoinType inner) {
        List<SingularAttribute<?, ?>> left = joinAttributes.get(inner);
        if (left == null) {
            left = new ArrayList<SingularAttribute<?, ?>>();
            joinAttributes.put(inner, left);
        }
        return left;
    }
}