Java tutorial
/******************************************************************************* * 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; } }