001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.ws.server.provider;
015
016import org.gbif.api.model.common.search.FacetedSearchRequest;
017import org.gbif.api.model.common.search.SearchParameter;
018
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.commons.lang3.StringUtils;
025import org.springframework.web.context.request.WebRequest;
026
027import static org.gbif.ws.util.CommonWsUtils.getFirst;
028import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET;
029import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_LIMIT;
030import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_MINCOUNT;
031import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_MULTISELECT;
032import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_OFFSET;
033
034/**
035 * Provider class that transforms a set of HTTP parameters into a FacetedSearchRequest class instance.
036 * This assumes the existence of the following parameters in the HTTP request:
037 * 'page_size', 'offset', 'facet', 'q' and any of the search parameter enum member names case insensitively.
038 */
039public class FacetedSearchRequestProvider<
040        RT extends FacetedSearchRequest<P>, P extends Enum<?> & SearchParameter>
041    extends SearchRequestProvider<RT, P> {
042
043  private static final int DEFAULT_FACET_LIMIT = 10;
044
045  public FacetedSearchRequestProvider(Class<RT> requestType, Class<P> searchParameterClass) {
046    super(requestType, searchParameterClass);
047  }
048
049  public FacetedSearchRequestProvider(
050      Class<RT> requestType, Class<P> searchParameterClass, Integer maxPageSize) {
051    super(requestType, searchParameterClass, maxPageSize);
052  }
053
054  @Override
055  protected RT getSearchRequest(WebRequest webRequest, RT searchRequest) {
056    RT request = super.getSearchRequest(webRequest, searchRequest);
057
058    final Map<String, String[]> params = webRequest.getParameterMap();
059
060    final String facetMultiSelectValue = getFirstIgnoringCase(PARAM_FACET_MULTISELECT, params);
061    if (facetMultiSelectValue != null) {
062      searchRequest.setMultiSelectFacets(Boolean.parseBoolean(facetMultiSelectValue));
063    }
064
065    final String facetMinCountValue = getFirstIgnoringCase(PARAM_FACET_MINCOUNT, params);
066    if (facetMinCountValue != null) {
067      searchRequest.setFacetMinCount(Integer.parseInt(facetMinCountValue));
068    }
069
070    final String facetLimit = getFirstIgnoringCase(PARAM_FACET_LIMIT, params);
071    if (facetLimit != null) {
072      searchRequest.setFacetLimit(Integer.parseInt(facetLimit));
073    }
074
075    final String facetOffset = getFirstIgnoringCase(PARAM_FACET_OFFSET, params);
076    if (facetOffset != null) {
077      searchRequest.setFacetOffset(Integer.parseInt(facetOffset));
078    }
079
080    final List<String> facets =
081        params.get(PARAM_FACET) != null
082            ? Arrays.asList(params.get(PARAM_FACET))
083            : Collections.emptyList();
084    if (!facets.isEmpty()) {
085      for (String f : facets) {
086        P p = findSearchParam(f);
087        if (p != null) {
088          searchRequest.addFacets(p);
089          String pFacetOffset = getFirstIgnoringCase(f + '.' + PARAM_FACET_OFFSET, params);
090          String pFacetLimit = getFirstIgnoringCase(f + '.' + PARAM_FACET_LIMIT, params);
091          if (pFacetLimit != null) {
092            if (pFacetOffset != null) {
093              searchRequest.addFacetPage(
094                  p, Integer.parseInt(pFacetOffset), Integer.parseInt(pFacetLimit));
095            } else {
096              searchRequest.addFacetPage(p, 0, Integer.parseInt(pFacetLimit));
097            }
098          } else if (pFacetOffset != null) {
099            searchRequest.addFacetPage(p, Integer.parseInt(pFacetOffset), DEFAULT_FACET_LIMIT);
100          }
101        }
102      }
103    }
104
105    return request;
106  }
107
108  /**
109   * Get the first parameter value, the parameter is searched in a case-insensitive manner.
110   * First tries with the exact match, then the lowercase and finally the uppercase value of the parameter.
111   */
112  private static String getFirstIgnoringCase(String parameter, Map<String, String[]> params) {
113    String value = getFirst(params, parameter);
114    if (StringUtils.isNotEmpty(value)) {
115      return value;
116    }
117    value = getFirst(params, parameter.toLowerCase());
118    if (StringUtils.isNotEmpty(value)) {
119      return value;
120    }
121    value = getFirst(params, parameter.toUpperCase());
122    if (StringUtils.isNotEmpty(value)) {
123      return value;
124    }
125    return null;
126  }
127}