/* * * * * * * Copyright 2019-2020 the original author or authors. * * * * * * 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 * * * * * * https://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.springdoc.core; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import org.apache.commons.lang3.StringUtils; import org.springdoc.core.customizers.ParameterCustomizer; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpMethod; import org.springframework.util.CollectionUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.HandlerMethod; import org.springframework.web.util.UriComponentsBuilder; import static org.springdoc.core.Constants.OPENAPI_ARRAY_TYPE; import static org.springdoc.core.Constants.OPENAPI_STRING_TYPE; import static org.springdoc.core.Constants.QUERY_PARAM; import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation; /** * The type Abstract request builder. * @author bnasslahsen */ public abstract class AbstractRequestBuilder { /** * The constant PARAM_TYPES_TO_IGNORE. */ private static final List<Class<?>> PARAM_TYPES_TO_IGNORE = new ArrayList<>(); /** * The constant ANNOTATIONS_FOR_REQUIRED. */ // using string litterals to support both validation-api v1 and v2 private static final String[] ANNOTATIONS_FOR_REQUIRED = { NotNull.class.getName(), "javax.validation.constraints.NotBlank", "javax.validation.constraints.NotEmpty" }; /** * The constant POSITIVE_OR_ZERO. */ private static final String POSITIVE_OR_ZERO = "javax.validation.constraints.PositiveOrZero"; /** * The constant NEGATIVE_OR_ZERO. */ private static final String NEGATIVE_OR_ZERO = "javax.validation.constraints.NegativeOrZero"; static { PARAM_TYPES_TO_IGNORE.add(WebRequest.class); PARAM_TYPES_TO_IGNORE.add(NativeWebRequest.class); PARAM_TYPES_TO_IGNORE.add(java.security.Principal.class); PARAM_TYPES_TO_IGNORE.add(HttpMethod.class); PARAM_TYPES_TO_IGNORE.add(java.util.Locale.class); PARAM_TYPES_TO_IGNORE.add(java.util.TimeZone.class); PARAM_TYPES_TO_IGNORE.add(java.io.InputStream.class); PARAM_TYPES_TO_IGNORE.add(java.time.ZoneId.class); PARAM_TYPES_TO_IGNORE.add(java.io.Reader.class); PARAM_TYPES_TO_IGNORE.add(java.io.OutputStream.class); PARAM_TYPES_TO_IGNORE.add(java.io.Writer.class); PARAM_TYPES_TO_IGNORE.add(java.util.Map.class); PARAM_TYPES_TO_IGNORE.add(org.springframework.ui.Model.class); PARAM_TYPES_TO_IGNORE.add(org.springframework.ui.ModelMap.class); PARAM_TYPES_TO_IGNORE.add(Errors.class); PARAM_TYPES_TO_IGNORE.add(BindingResult.class); PARAM_TYPES_TO_IGNORE.add(SessionStatus.class); PARAM_TYPES_TO_IGNORE.add(UriComponentsBuilder.class); PARAM_TYPES_TO_IGNORE.add(RequestAttribute.class); } /** * The Parameter builder. */ private final GenericParameterBuilder parameterBuilder; /** * The Request body builder. */ private final RequestBodyBuilder requestBodyBuilder; /** * The Operation builder. */ private final OperationBuilder operationBuilder; /** * The Local spring doc parameter name discoverer. */ private final LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer; /** * The Parameter customizers. */ private final Optional<List<ParameterCustomizer>> parameterCustomizers; /** * Instantiates a new Abstract request builder. * * @param parameterBuilder the parameter builder * @param requestBodyBuilder the request body builder * @param operationBuilder the operation builder * @param parameterCustomizers the parameter customizers * @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer */ protected AbstractRequestBuilder(GenericParameterBuilder parameterBuilder, RequestBodyBuilder requestBodyBuilder, OperationBuilder operationBuilder, Optional<List<ParameterCustomizer>> parameterCustomizers, LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer) { super(); this.parameterBuilder = parameterBuilder; this.requestBodyBuilder = requestBodyBuilder; this.operationBuilder = operationBuilder; if (parameterCustomizers.isPresent()) parameterCustomizers.get().removeIf(Objects::isNull); this.parameterCustomizers = parameterCustomizers; this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer; } /** * Add request wrapper to ignore. * * @param classes the classes */ public static void addRequestWrapperToIgnore(Class<?>... classes) { PARAM_TYPES_TO_IGNORE.addAll(Arrays.asList(classes)); } /** * Remove request wrapper to ignore. * * @param classes the classes */ public static void removeRequestWrapperToIgnore(Class<?>... classes) { List<Class<?>> classesToIgnore = Arrays.asList(classes); if (PARAM_TYPES_TO_IGNORE.containsAll(classesToIgnore)) PARAM_TYPES_TO_IGNORE.removeAll(Arrays.asList(classes)); } /** * Is request type to ignore boolean. * * @param rawClass the raw class * @return the boolean */ public static boolean isRequestTypeToIgnore(Class<?> rawClass) { return PARAM_TYPES_TO_IGNORE.stream().anyMatch(clazz -> clazz.isAssignableFrom(rawClass)); } /** * Build operation. * * @param handlerMethod the handler method * @param requestMethod the request method * @param operation the operation * @param methodAttributes the method attributes * @param openAPI the open api * @return the operation */ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, Operation operation, MethodAttributes methodAttributes, OpenAPI openAPI) { // Documentation String operationId = operationBuilder.getOperationId(handlerMethod.getMethod().getName(), operation.getOperationId(), openAPI); operation.setOperationId(operationId); // requests String[] pNames = this.localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod()); MethodParameter[] parameters = handlerMethod.getMethodParameters(); String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new); if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) pNames = reflectionParametersNames; parameters = DelegatingMethodParameter.customize(pNames, parameters); RequestBodyInfo requestBodyInfo = new RequestBodyInfo(); List<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>(); Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod()); Components components = openAPI.getComponents(); for (MethodParameter methodParameter : parameters) { // check if query param Parameter parameter = null; io.swagger.v3.oas.annotations.Parameter parameterDoc = AnnotatedElementUtils.findMergedAnnotation( AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()), io.swagger.v3.oas.annotations.Parameter.class); final String pName = methodParameter.getParameterName(); ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter); if (parameterDoc == null) parameterDoc = parametersDocMap.get(parameterInfo.getpName()); // use documentation as reference if (parameterDoc != null) { if (parameterDoc.hidden() || parameterDoc.schema().hidden()) continue; parameter = parameterBuilder.buildParameterFromDoc(parameterDoc, components, methodAttributes.getJsonViewAnnotation()); parameterInfo.setParameterModel(parameter); } if (!isParamToIgnore(methodParameter)) { parameter = buildParams(parameterInfo, components, requestMethod, methodAttributes.getJsonViewAnnotation()); // Merge with the operation parameters parameter = GenericParameterBuilder.mergeParameter(operationParameters, parameter); List<Annotation> parameterAnnotations = Arrays.asList(methodParameter.getParameterAnnotations()); if (isValidParameter(parameter)) applyBeanValidatorAnnotations(parameter, parameterAnnotations); else if (!RequestMethod.GET.equals(requestMethod)) { if (operation.getRequestBody() != null) requestBodyInfo.setRequestBody(operation.getRequestBody()); requestBodyBuilder.calculateRequestBodyInfo(components, methodAttributes, parameterInfo, requestBodyInfo); applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional()); } customiseParameter(parameter, parameterInfo); } } LinkedHashMap<String, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap); setParams(operation, new ArrayList<>(map.values()), requestBodyInfo); return operation; } /** * Gets parameter linked hash map. * * @param components the components * @param methodAttributes the method attributes * @param operationParameters the operation parameters * @param parametersDocMap the parameters doc map * @return the parameter linked hash map */ private LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) { LinkedHashMap<String, Parameter> map = operationParameters.stream() .collect(Collectors.toMap( parameter -> parameter.getName() != null ? parameter.getName() : Integer.toString(parameter.hashCode()), parameter -> parameter, (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new )); for (Map.Entry<String, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) { if (entry.getKey() != null && !map.containsKey(entry.getKey()) && !entry.getValue().hidden()) { //Convert Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components, methodAttributes.getJsonViewAnnotation()); map.put(entry.getKey(), parameter); } } getHeaders(methodAttributes, map); return map; } /** * Gets headers. * * @param methodAttributes the method attributes * @param map the map * @return the headers */ public static Collection<Parameter> getHeaders(MethodAttributes methodAttributes, Map<String, Parameter> map) { for (Map.Entry<String, String> entry : methodAttributes.getHeaders().entrySet()) { Parameter parameter = new Parameter().in(ParameterIn.HEADER.toString()).name(entry.getKey()).schema(new StringSchema().addEnumItem(entry.getValue())); if (map.containsKey(entry.getKey())) { parameter = map.get(entry.getKey()); parameter.getSchema().addEnumItemObject(entry.getValue()); parameter.setSchema(parameter.getSchema()); } map.put(entry.getKey(), parameter); } return map.values(); } /** * Customise parameter parameter. * * @param parameter the parameter * @param parameterInfo the parameter info * @return the parameter */ protected Parameter customiseParameter(Parameter parameter, ParameterInfo parameterInfo) { parameterCustomizers.ifPresent(customizers -> customizers.forEach(customizer -> customizer.customize(parameter, parameterInfo.getMethodParameter()))); return parameter; } /** * Is param to ignore boolean. * * @param parameter the parameter * @return the boolean */ public boolean isParamToIgnore(MethodParameter parameter) { if (parameterBuilder.isAnnotationToIgnore(parameter)) return true; if ((parameter.getParameterAnnotation(PathVariable.class) != null && parameter.getParameterAnnotation(PathVariable.class).required()) || (parameter.getParameterAnnotation(RequestParam.class) != null && parameter.getParameterAnnotation(RequestParam.class).required()) || (parameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null && parameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class).required())) return false; return isRequestTypeToIgnore(parameter.getParameterType()); } /** * Sets params. * * @param operation the operation * @param operationParameters the operation parameters * @param requestBodyInfo the request body info */ private void setParams(Operation operation, List<Parameter> operationParameters, RequestBodyInfo requestBodyInfo) { if (!CollectionUtils.isEmpty(operationParameters)) operation.setParameters(operationParameters); if (requestBodyInfo.getRequestBody() != null) operation.setRequestBody(requestBodyInfo.getRequestBody()); } /** * Is valid parameter boolean. * * @param parameter the parameter * @return the boolean */ public boolean isValidParameter(Parameter parameter) { return parameter != null && (parameter.getName() != null || parameter.get$ref() != null); } /** * Build params parameter. * * @param parameterInfo the parameter info * @param components the components * @param requestMethod the request method * @param jsonView the json view * @return the parameter */ public Parameter buildParams(ParameterInfo parameterInfo, Components components, RequestMethod requestMethod, JsonView jsonView) { MethodParameter methodParameter = parameterInfo.getMethodParameter(); RequestHeader requestHeader = parameterInfo.getRequestHeader(); RequestParam requestParam = parameterInfo.getRequestParam(); PathVariable pathVar = parameterInfo.getPathVar(); CookieValue cookieValue = parameterInfo.getCookieValue(); RequestInfo requestInfo; if (requestHeader != null) { requestInfo = new RequestInfo(ParameterIn.HEADER.toString(), parameterInfo.getpName(), requestHeader.required(), requestHeader.defaultValue()); return buildParam(parameterInfo, components, requestInfo, jsonView); } else if (requestParam != null && !parameterBuilder.isFile(parameterInfo.getMethodParameter())) { requestInfo = new RequestInfo(ParameterIn.QUERY.toString(), parameterInfo.getpName(), requestParam.required() && !methodParameter.isOptional(), requestParam.defaultValue()); return buildParam(parameterInfo, components, requestInfo, jsonView); } else if (pathVar != null) { requestInfo = new RequestInfo(ParameterIn.PATH.toString(), parameterInfo.getpName(), !methodParameter.isOptional(), null); return buildParam(parameterInfo, components, requestInfo, jsonView); } else if (cookieValue != null) { requestInfo = new RequestInfo(ParameterIn.COOKIE.toString(), parameterInfo.getpName(), cookieValue.required(), cookieValue.defaultValue()); return buildParam(parameterInfo, components, requestInfo, jsonView); } // By default DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter; if (RequestMethod.GET.equals(requestMethod) || (parameterInfo.getParameterModel() != null && (ParameterIn.PATH.toString().equals(parameterInfo.getParameterModel().getIn()))) || delegatingMethodParameter.isParameterObject()) return this.buildParam(QUERY_PARAM, components, parameterInfo, !methodParameter.isOptional(), null, jsonView); return null; } /** * Build param parameter. * * @param parameterInfo the parameter info * @param components the components * @param requestInfo the request info * @param jsonView the json view * @return the parameter */ private Parameter buildParam(ParameterInfo parameterInfo, Components components, RequestInfo requestInfo, JsonView jsonView) { Parameter parameter; String pName = parameterInfo.getpName(); String name = StringUtils.isBlank(requestInfo.value()) ? pName : requestInfo.value(); parameterInfo.setpName(name); if (!ValueConstants.DEFAULT_NONE.equals(requestInfo.defaultValue())) parameter = this.buildParam(requestInfo.type(), components, parameterInfo, false, requestInfo.defaultValue(), jsonView); else parameter = this.buildParam(requestInfo.type(), components, parameterInfo, requestInfo.required(), null, jsonView); return parameter; } /** * Build param parameter. * * @param in the in * @param components the components * @param parameterInfo the parameter info * @param required the required * @param defaultValue the default value * @param jsonView the json view * @return the parameter */ private Parameter buildParam(String in, Components components, ParameterInfo parameterInfo, Boolean required, String defaultValue, JsonView jsonView) { Parameter parameter = parameterInfo.getParameterModel(); String name = parameterInfo.getpName(); if (parameter == null) { parameter = new Parameter(); parameterInfo.setParameterModel(parameter); } if (StringUtils.isBlank(parameter.getName())) parameter.setName(name); if (StringUtils.isBlank(parameter.getIn())) parameter.setIn(in); if (required != null && parameter.getRequired() == null) parameter.setRequired(required); if (containsDeprecatedAnnotation(parameterInfo.getMethodParameter().getParameterAnnotations())) parameter.setDeprecated(true); if (parameter.getSchema() == null) { Schema<?> schema = parameterBuilder.calculateSchema(components, parameterInfo, null, jsonView); if (defaultValue != null) schema.setDefault(defaultValue); parameter.setSchema(schema); } return parameter; } /** * Apply bean validator annotations. * * @param parameter the parameter * @param annotations the annotations */ public void applyBeanValidatorAnnotations(final Parameter parameter, final List<Annotation> annotations) { Map<String, Annotation> annos = new HashMap<>(); if (annotations != null) annotations.forEach(annotation -> annos.put(annotation.annotationType().getName(), annotation)); boolean annotationExists = Arrays.stream(ANNOTATIONS_FOR_REQUIRED).anyMatch(annos::containsKey); if (annotationExists) parameter.setRequired(true); Schema<?> schema = parameter.getSchema(); applyValidationsToSchema(annos, schema); } /** * Apply bean validator annotations. * * @param requestBody the request body * @param annotations the annotations * @param isOptional the is optional */ public void applyBeanValidatorAnnotations(final RequestBody requestBody, final List<Annotation> annotations, boolean isOptional) { Map<String, Annotation> annos = new HashMap<>(); boolean requestBodyRequired = false; if (!CollectionUtils.isEmpty(annotations)) { annotations.forEach(annotation -> annos.put(annotation.annotationType().getName(), annotation)); requestBodyRequired = annotations.stream() .filter(annotation -> org.springframework.web.bind.annotation.RequestBody.class.equals(annotation.annotationType())) .anyMatch(annotation -> ((org.springframework.web.bind.annotation.RequestBody) annotation).required()); } boolean validationExists = Arrays.stream(ANNOTATIONS_FOR_REQUIRED).anyMatch(annos::containsKey); if (validationExists || (!isOptional && requestBodyRequired)) requestBody.setRequired(true); Content content = requestBody.getContent(); for (MediaType mediaType : content.values()) { Schema<?> schema = mediaType.getSchema(); applyValidationsToSchema(annos, schema); } } /** * Calculate size. * * @param annos the annos * @param schema the schema */ private void calculateSize(Map<String, Annotation> annos, Schema<?> schema) { if (annos.containsKey(Size.class.getName())) { Size size = (Size) annos.get(Size.class.getName()); if (OPENAPI_ARRAY_TYPE.equals(schema.getType())) { schema.setMinItems(size.min()); schema.setMaxItems(size.max()); } else if (OPENAPI_STRING_TYPE.equals(schema.getType())) { schema.setMinLength(size.min()); schema.setMaxLength(size.max()); } } } /** * Gets request body builder. * * @return the request body builder */ public RequestBodyBuilder getRequestBodyBuilder() { return requestBodyBuilder; } /** * Gets api parameters. * * @param method the method * @return the api parameters */ private Map<String, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Method method) { Class<?> declaringClass = method.getDeclaringClass(); Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDoc = AnnotatedElementUtils .findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameters.class); LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParametersMap = apiParametersDoc.stream() .flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDocDeclaringClass = AnnotatedElementUtils .findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameters.class); LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream() .flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParametersDocDeclaringClassMap); Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDoc = AnnotatedElementUtils .findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameter.class); LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParameterDocMap = apiParameterDoc.stream() .collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParameterDocMap); Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClass = AnnotatedElementUtils .findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameter.class); LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream() .collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParameterDocDeclaringClassMap); return apiParametersMap; } /** * Apply validations to schema. * * @param annos the annos * @param schema the schema */ private void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> schema) { if (annos.containsKey(Min.class.getName())) { Min min = (Min) annos.get(Min.class.getName()); schema.setMinimum(BigDecimal.valueOf(min.value())); } if (annos.containsKey(Max.class.getName())) { Max max = (Max) annos.get(Max.class.getName()); schema.setMaximum(BigDecimal.valueOf(max.value())); } calculateSize(annos, schema); if (annos.containsKey(DecimalMin.class.getName())) { DecimalMin min = (DecimalMin) annos.get(DecimalMin.class.getName()); if (min.inclusive()) schema.setMinimum(BigDecimal.valueOf(Double.parseDouble(min.value()))); else schema.setExclusiveMinimum(!min.inclusive()); } if (annos.containsKey(DecimalMax.class.getName())) { DecimalMax max = (DecimalMax) annos.get(DecimalMax.class.getName()); if (max.inclusive()) schema.setMaximum(BigDecimal.valueOf(Double.parseDouble(max.value()))); else schema.setExclusiveMaximum(!max.inclusive()); } if (annos.containsKey(POSITIVE_OR_ZERO)) schema.setMinimum(BigDecimal.ZERO); if (annos.containsKey(NEGATIVE_OR_ZERO)) schema.setMaximum(BigDecimal.ZERO); if (annos.containsKey(Pattern.class.getName())) { Pattern pattern = (Pattern) annos.get(Pattern.class.getName()); schema.setPattern(pattern.regexp()); } } }