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.ws.WebApplicationException; 017 018import java.text.MessageFormat; 019import java.text.ParseException; 020import java.text.SimpleDateFormat; 021import java.util.Calendar; 022import java.util.Date; 023import java.util.Optional; 024 025import org.springframework.core.MethodParameter; 026import org.springframework.http.HttpStatus; 027import org.springframework.web.bind.support.WebDataBinderFactory; 028import org.springframework.web.context.request.NativeWebRequest; 029import org.springframework.web.method.support.HandlerMethodArgumentResolver; 030import org.springframework.web.method.support.ModelAndViewContainer; 031 032/** 033 * Provider that accepts and transforms partial Dates. For example: 01-2018 or 01/2018 will be 034 * translated into 01-01-2018. 035 */ 036public class PartialDateHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { 037 038 private static final String YEAR_ONLY_FORMAT = "yyyy"; 039 private static final String[] SUPPORTED_FORMATS = 040 new String[] {"yyyy/MM", "yyyy-MM", YEAR_ONLY_FORMAT}; 041 042 @Override 043 public boolean supportsParameter(MethodParameter parameter) { 044 return Date.class.equals(parameter.getParameterType()) 045 && parameter.getParameterAnnotation(PartialDate.class) != null; 046 } 047 048 @Override 049 public Object resolveArgument( 050 MethodParameter parameter, 051 ModelAndViewContainer mavContainer, 052 NativeWebRequest webRequest, 053 WebDataBinderFactory binderFactory) { 054 final String paramName = parameter.getParameterName(); 055 final String paramValue = paramName != null ? webRequest.getParameter(paramName) : null; 056 return Optional.ofNullable(paramValue) 057 .map(value -> tryDateParse(value, paramName)) 058 .orElse(null); 059 } 060 061 /** 062 * Tries to parse the input using the supported formats. Adjust the date to the first or last day 063 * of the month depending on the param name. 064 */ 065 private Date tryDateParse(String dateValue, String paramName) { 066 if (dateValue != null && !dateValue.isEmpty()) { 067 for (String dateFormat : SUPPORTED_FORMATS) { 068 try { 069 Date date = new SimpleDateFormat(dateFormat).parse(dateValue); 070 Calendar cal = Calendar.getInstance(); 071 cal.setTime(date); 072 if (paramName.startsWith("from")) { 073 cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)); 074 } else if (paramName.startsWith("to")) { 075 if (YEAR_ONLY_FORMAT.equals(dateFormat)) { 076 cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH)); 077 } 078 cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 079 } 080 return cal.getTime(); 081 } catch (ParseException ex) { 082 // DO NOTHING 083 } 084 } 085 } 086 throw new WebApplicationException( 087 MessageFormat.format("Unaccepted parameter value {0} : {1}", paramName, dateValue), 088 HttpStatus.BAD_REQUEST); 089 } 090}