AcceptHeaderLocaleTzResolver.java

/*
 * Copyright 2022 Global Crop Diversity Trust
 *
 * 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 org.genesys.spring;

import java.time.ZoneId;
import java.time.zone.ZoneRulesException;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.genesys.server.servlet.filter.LocaleURLFilter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.LocaleContextResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import org.apache.commons.lang3.StringUtils;

@Slf4j
public class AcceptHeaderLocaleTzResolver extends AcceptHeaderLocaleResolver implements LocaleContextResolver {
	
	public static final String TIMEZONE_HEADER = "X-Timezone";

	private final TimeZone defaultTimeZone;

	public AcceptHeaderLocaleTzResolver(Locale defaultLocale, TimeZone defaultTimeZone) {
		this.defaultTimeZone = defaultTimeZone;
		this.setDefaultLocale(defaultLocale);
	}

	@Override
	public LocaleContext resolveLocaleContext(HttpServletRequest request) {

		final var locale = resolveLocale(request);
		final var timeZone = resolveTimezone(request);

		return new TimeZoneAwareLocaleContext() {
			public Locale getLocale() {
				return locale;
			}

			@Nullable
			public TimeZone getTimeZone() {
				return timeZone;
			}
		};
	}

	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		var localeUrlLocale = request.getAttribute(LocaleURLFilter.REQUEST_LOCALE_ATTR);
		if (localeUrlLocale != null) {
			log.debug("Using LocaleURLFilter locale {}", localeUrlLocale);
			return (Locale) localeUrlLocale;
		}
		return super.resolveLocale(request);
	}

	private TimeZone resolveTimezone(HttpServletRequest request) {
		TimeZone timeZone = null;
		var timeZoneId = request.getHeader(TIMEZONE_HEADER);
		if (StringUtils.isNotBlank(timeZoneId)) {
			try {
				timeZone = TimeZone.getTimeZone(ZoneId.of(timeZoneId));
			} catch (ZoneRulesException e) {
				log.warn("Unknown time-zone ID from request. Unknown time-zone ID: {}", timeZoneId);
			}
		}
		if (timeZone == null) {
			timeZone = defaultTimeZone;
		}
		return timeZone;
	}

	@Override
	public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
		throw new UnsupportedOperationException("Cannot change HTTP Accept-Language, X-Timezone headers - use a different locale resolution strategy");
	}
}