org.opentestsystem.shared.search.domain.AbstractSearchRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.shared.search.domain.AbstractSearchRequest.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System
 * Copyright (c) 2013 American Institutes for Research
 *
 * Distributed under the AIR Open Source License, Version 1.0
 * See accompanying file AIR-License-1_0.txt or at
 * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package org.opentestsystem.shared.search.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

/**
 * This is a psuedo-domain object, not really existing in the persistent store,
 * but instead is used as part of the Gateway interface for convey complex
 * search criteria. Note: page counting in mongo is zero indexed.
 */
public abstract class AbstractSearchRequest implements Serializable {

    private static final long serialVersionUID = 1853144907289436990L;

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSearchRequest.class);

    public static final int DEFAULT_PAGE_SIZE = 10;
    public static final int DEFAULT_CURRENT_PAGE = 0;
    public static final String[] DEFAULT_SORT_KEY = new String[] { "_id" };
    public static final Sort.Direction DEFAULT_SORT_DIR = Sort.Direction.ASC;

    public static final String PAGE_SIZE = "pageSize";
    public static final String CURRENT_PAGE = "currentPage";
    public static final String SORT_KEY = "sortKey";
    public static final String SORT_DIR = "sortDir";

    private static final int ONE = 1;
    private static final int TWO = 2;

    private final Map<String, SearchFilter> searchFilterMap = new HashMap<String, SearchFilter>();
    private final Map<String, String[]> searchCriteria = new HashMap<String, String[]>();
    private int pageSize = DEFAULT_PAGE_SIZE;
    private int currentPage = DEFAULT_CURRENT_PAGE;
    private String[] sortKeys = DEFAULT_SORT_KEY;
    private Sort.Direction[] sortDirections = new Direction[] { DEFAULT_SORT_DIR };

    protected abstract List<SearchFilter> getSearchFilters();

    protected abstract String getSearchResource();

    protected void addSearchableFields(final Set<String> searchableFields) {

    }

    public AbstractSearchRequest(final Map<String, String[]> requestMap) {
        parseRequestMap(requestMap);
    }

    public AbstractSearchRequest(final Map<String, String[]> requestMap, final Set<String> searchableFields) {
        addSearchableFields(searchableFields);
        parseRequestMap(requestMap);
    }

    private void parseRequestMap(final Map<String, String[]> requestMap) {
        for (final SearchFilter filter : getSearchFilters()) {
            searchFilterMap.put(filter.getSearchApiName(), filter);
        }

        for (final Entry<String, String[]> entry : requestMap.entrySet()) {
            final String[] pruned = pruneArrayOfEmpty(entry.getValue());
            if (searchFilterMap.containsKey(entry.getKey()) && pruned.length > 0) {
                searchCriteria.put(entry.getKey(), pruned);
            } else if (entry.getKey().equals(PAGE_SIZE)) {
                setPageSize(entry.getValue()[0]);
            } else if (entry.getKey().equals(CURRENT_PAGE)) {
                setCurrentPage(entry.getValue()[0]);
            } else if (entry.getKey().equals(SORT_KEY)) {
                sortKeys = pullOutMultipleValues(entry);
            } else if (entry.getKey().equals(SORT_DIR)) {
                final String[] dirs = pullOutMultipleValues(entry);
                final List<Direction> directions = new ArrayList<Direction>();
                for (final String dir : dirs) {
                    directions.add(Direction.fromString(dir));
                }
                sortDirections = directions.toArray(new Direction[0]);
            }
        }
    }

    private String[] pullOutMultipleValues(final Entry<String, String[]> entry) {
        String[] tempSortKeys = new String[0];
        if (entry.getValue().length == ONE) {
            tempSortKeys = entry.getValue()[0].split(",");
        } else if (entry.getValue().length > ONE) {
            tempSortKeys = entry.getValue();
        }
        return tempSortKeys;
    }

    private String[] pruneArrayOfEmpty(final String[] arrayToPrune) {
        final List<String> ret = new ArrayList<String>();
        if (arrayToPrune != null) {
            for (final String val : arrayToPrune) {
                if (val != null && StringUtils.isNotBlank(val)) {
                    ret.add(val);
                }
            }
        }
        return ret.toArray(new String[0]);
    }

    private void setCurrentPage(final String currentPageAsStr) {
        try {
            final int newCurrentPage = Integer.parseInt(currentPageAsStr);
            this.currentPage = newCurrentPage < 0 ? DEFAULT_CURRENT_PAGE : newCurrentPage;
            LOGGER.debug("Setting new '" + CURRENT_PAGE + "' to " + this.currentPage);
        } catch (final NumberFormatException nfe) {
            LOGGER.error("Invalid '" + CURRENT_PAGE + "' request: " + currentPageAsStr);
        }
    }

    private void setPageSize(final String pageSizeAsStr) {
        try {
            final int newPageSize = Integer.parseInt(pageSizeAsStr);
            this.pageSize = newPageSize <= 0 ? DEFAULT_PAGE_SIZE : newPageSize;
            LOGGER.debug("Setting new '" + PAGE_SIZE + "' to " + this.pageSize);
        } catch (final NumberFormatException nfe) {
            LOGGER.error("Invalid '" + PAGE_SIZE + "' request: " + pageSizeAsStr);
        }
    }

    public boolean isValid() {
        return true;
    }

    public Query buildQuery() {
        final Map<String, Criteria> criteriaMap = buildCriteria();
        return composeQuery(criteriaMap.values());
    }

    protected Query composeQuery(final Collection<Criteria> inCriteria) {
        final Query qry = new Query();
        for (final Criteria cri : inCriteria) {
            qry.addCriteria(cri);
        }

        final List<Order> orders = new ArrayList<Order>();
        if (sortKeys != null) {
            for (int i = 0; i < sortKeys.length; i++) {
                Direction sortDir = null;
                final String key = sortKeys[i];
                if (sortDirections.length >= i + 1) {
                    sortDir = sortDirections[i];
                }
                final Order order = new Order(sortDir, key);
                orders.add(order);
            }
        }
        final Sort sort = new Sort(orders);

        return qry.with(new PageRequest(currentPage, pageSize, sort));

    }

    private boolean hasAValue(final String... inArgs) {
        boolean notEmpty = false;
        for (final String arg : inArgs) {
            if (StringUtils.isNotEmpty(arg)) {
                notEmpty = true;
            }
        }
        return notEmpty;
    }

    protected Map<String, Criteria> buildCriteria() {

        final Map<String, Criteria> criteriaMap = new HashMap<>();

        for (final String searchKey : searchCriteria.keySet()) {
            final SearchFilter filter = searchFilterMap.get(searchKey);
            final String[] searchValues = searchCriteria.get(searchKey);
            if (hasAValue(searchValues)) {

                final Object[] queryValues = filter.getSearchValue(searchValues);
                final String domainName = filter.getDomainName();
                Criteria criteria = criteriaMap.get(filter.getDomainName());

                if (criteria == null) {
                    criteria = Criteria.where(domainName);
                }

                switch (filter.getOperator()) {
                case EQ:
                    if (queryValues.length == ONE) {
                        criteria = criteria.is(queryValues[0]);
                    } else {
                        criteria = criteria.in(queryValues);
                    }
                    break;
                case GTH:
                    criteria = criteria.gt(queryValues[0]);
                    break;
                case LTH:
                    criteria = criteria.lt(queryValues[0]);
                    break;
                case GEQ:
                    criteria = criteria.gte(queryValues[0]);
                    break;
                case LEQ:
                    criteria = criteria.lte(queryValues[0]);
                    break;
                case NOTNULL:
                    criteria = criteria.ne(null);
                    break;
                case CASE_INSENSITIVE:
                    if (queryValues.length == ONE) {
                        criteria = buildCaseInsensitiveRegex(criteria, queryValues[0]);
                    } else {
                        final Criteria[] critList = new Criteria[queryValues.length];
                        for (int i = 0; i < queryValues.length; i++) {
                            critList[i] = buildCaseInsensitiveRegex(Criteria.where(domainName), queryValues[i]);
                        }
                        criteria = new Criteria().orOperator(critList);
                    }
                    break;
                case WLD_REG:
                    if (queryValues.length == ONE) {
                        criteria = buildRegex(criteria, queryValues[0]);
                    } else {
                        final Criteria[] critList = new Criteria[queryValues.length];
                        for (int i = 0; i < queryValues.length; i++) {
                            critList[i] = buildRegex(Criteria.where(domainName), queryValues[i]);
                        }
                        criteria = new Criteria().orOperator(critList);
                    }
                    break;
                case DYNAMIC_ELEMENT_NAME:
                    // modifying domain name, recreate the where
                    criteria = Criteria.where(
                            domainName + (searchValues[0] == null ? "" : replaceEscapeChars(searchValues[0])));
                    if (searchValues.length == ONE) {
                        // if only the first searchValue is specified (the element name)
                        // match any element (with the searchValue name that has a value
                        criteria = buildRegex(criteria, ".");
                    } else if (searchValues.length == TWO) {
                        criteria = criteria.is(searchValues[1]);
                    } else {
                        criteria = criteria.in((Object[]) Arrays.copyOfRange(searchValues, 1, searchValues.length));
                    }
                    break;
                default:
                    break;
                }
                criteriaMap.put(domainName, criteria);
            }
        }
        final Map<String, Criteria> returnMap = new HashMap<String, Criteria>();
        for (final Criteria crit : criteriaMap.values()) {
            returnMap.put(crit.getKey(), crit);
        }
        return returnMap;
    }

    protected String replaceEscapeChars(final String searchValue) {
        // base impl just return the value
        return searchValue;
    }

    private Criteria buildRegex(final Criteria criteria, final Object queryValue) {
        final Criteria newCriteria = criteria.regex("^.*\\Q" + queryValue + "\\E", "i");
        return newCriteria;
    }

    private Criteria buildCaseInsensitiveRegex(final Criteria criteria, final Object queryValue) {
        final Criteria newCriteria = criteria.regex("^" + queryValue + "$", "i");
        return newCriteria;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public String[] getSortKeys() {
        return sortKeys;
    }

    public Sort.Direction[] getSortDirections() {
        return sortDirections;
    }

    public Map<String, String[]> getSearchCriteria() {
        return searchCriteria;
    }

    public boolean isSearchCriteriaRequired() {
        return false;
    }

}