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 java.util.Arrays;
017import java.util.Collections;
018import java.util.List;
019import java.util.Locale;
020import java.util.Map;
021
022import org.apache.commons.lang3.StringUtils;
023import org.springframework.http.HttpHeaders;
024import org.springframework.web.context.request.WebRequest;
025
026import static org.gbif.ws.util.CommonWsUtils.getFirst;
027
028/**
029 * Provider class that extracts the requested locale based on http header or language query parameter.
030 * This allows resources to access a locale context very easily while keeping all logic in this class.
031 * The accept any language value * will be converted into a null locale.
032 * Example resource use:
033 * <pre>
034 * {@code
035 * public String getVernacularName(Locale locale) {
036 *   return "this is the " + locale + " vernacular name: xyz");
037 * }
038 * }
039 * </pre>
040 */
041public class LocaleProvider implements ContextProvider<Locale> {
042
043  private static final String LANGUAGE_PARAM = "language";
044  private static final String ANY_LANGUAGE = "*";
045
046  @Override
047  public Locale getValue(WebRequest webRequest) {
048    return getLocale(webRequest);
049  }
050
051  public static Locale getLocale(WebRequest webRequest) {
052    Map<String, String[]> params = webRequest.getParameterMap();
053
054    // try language parameter first
055    String languageParam = getFirst(params, LANGUAGE_PARAM);
056    if (languageParam != null) {
057      String lang = languageParam.trim().toLowerCase();
058      // iso language has to be 2 lower case letters!
059      if (StringUtils.isNotEmpty(lang) && lang.length() == 2) {
060        return new Locale(lang);
061      }
062    }
063
064    // try headers next
065    String[] acceptLanguageHeaderValues = webRequest.getHeaderValues(HttpHeaders.ACCEPT_LANGUAGE);
066
067    List<String> languages =
068        acceptLanguageHeaderValues != null
069            ? Arrays.asList(acceptLanguageHeaderValues)
070            : Collections.emptyList();
071
072    for (String lang : languages) {
073      // ignore accept any language value: * and non iso 2-letter codes
074      if (StringUtils.isNotEmpty(lang)
075          && !ANY_LANGUAGE.equalsIgnoreCase(lang)
076          && lang.length() == 2) {
077        return Locale.forLanguageTag(lang);
078      }
079    }
080
081    return null;
082  }
083}