All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.httppart.HttpPartSchema Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you 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.apache.juneau.httppart;

import static java.util.Collections.*;
import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.common.internal.ThrowableUtils.*;
import static org.apache.juneau.httppart.HttpPartDataType.*;
import static org.apache.juneau.httppart.HttpPartFormat.*;
import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.math.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.regex.*;

import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.common.internal.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.reflect.*;

/**
 * Represents an OpenAPI schema definition.
 *
 * 

* The schema definition can be applied to any HTTP parts such as bodies, headers, query/form parameters, and URL path parts. *
The API is generic enough to apply to any path part although some attributes may only applicable for certain parts. * *

* Schema objects are created via builders instantiated through the {@link #create()} method. * *

Notes:
    *
  • This class is thread safe and reusable. *
* *
See Also:
*/ public class HttpPartSchema { //------------------------------------------------------------------------------------------------------------------- // Static //------------------------------------------------------------------------------------------------------------------- /** Reusable instance of this object, all default settings. */ public static final HttpPartSchema DEFAULT = HttpPartSchema.create().allowEmptyValue(true).build(); /** Boolean type */ public static final HttpPartSchema T_BOOLEAN = HttpPartSchema.tBoolean().build(); /** File type */ public static final HttpPartSchema T_FILE = HttpPartSchema.tFile().build(); /** Integer type */ public static final HttpPartSchema T_INTEGER = HttpPartSchema.tInteger().build(); /** Int32 type */ public static final HttpPartSchema T_INT32 = HttpPartSchema.tInt32().build(); /** Int64 type */ public static final HttpPartSchema T_INT64 = HttpPartSchema.tInt64().build(); /** No type */ public static final HttpPartSchema T_NONE = HttpPartSchema.tNone().build(); /** Number type */ public static final HttpPartSchema T_NUMBER = HttpPartSchema.tNumber().build(); /** Float type */ public static final HttpPartSchema T_FLOAT = HttpPartSchema.tFloat().build(); /** Double type */ public static final HttpPartSchema T_DOUBLE = HttpPartSchema.tDouble().build(); /** String type */ public static final HttpPartSchema T_STRING = HttpPartSchema.tString().build(); /** Byte type */ public static final HttpPartSchema T_BYTE = HttpPartSchema.tByte().build(); /** Binary type */ public static final HttpPartSchema T_BINARY = HttpPartSchema.tBinary().build(); /** Spaced binary type */ public static final HttpPartSchema T_BINARY_SPACED = HttpPartSchema.tBinarySpaced().build(); /** Date type */ public static final HttpPartSchema T_DATE = HttpPartSchema.tDate().build(); /** Date-time type */ public static final HttpPartSchema T_DATETIME = HttpPartSchema.tDateTime().build(); /** UON-formated simple type */ public static final HttpPartSchema T_UON = HttpPartSchema.tUon().build(); /** Array type */ public static final HttpPartSchema T_ARRAY = HttpPartSchema.tArray().build(); /** Comma-delimited array type */ public static final HttpPartSchema T_ARRAY_CSV = HttpPartSchema.tArrayCsv().build(); /** Pipe-delimited array type */ public static final HttpPartSchema T_ARRAY_PIPES = HttpPartSchema.tArrayPipes().build(); /** Space-delimited array type */ public static final HttpPartSchema T_ARRAY_SSV = HttpPartSchema.tArraySsv().build(); /** Tab-delimited array type */ public static final HttpPartSchema T_ARRAY_TSV = HttpPartSchema.tArrayTsv().build(); /** UON-formatted array type */ public static final HttpPartSchema T_ARRAY_UON = HttpPartSchema.tArrayUon().build(); /** Multi-part array type */ public static final HttpPartSchema T_ARRAY_MULTI = HttpPartSchema.tArrayMulti().build(); /** Object type */ public static final HttpPartSchema T_OBJECT = HttpPartSchema.tObject().build(); /** Comma-delimited object type */ public static final HttpPartSchema T_OBJECT_CSV = HttpPartSchema.tObjectCsv().build(); /** Pipe-delimited object type */ public static final HttpPartSchema T_OBJECT_PIPES = HttpPartSchema.tObjectPipes().build(); /** Space-delimited object type */ public static final HttpPartSchema T_OBJECT_SSV = HttpPartSchema.tObjectSsv().build(); /** Tab-delimited object type */ public static final HttpPartSchema T_OBJECT_TSV = HttpPartSchema.tObjectTsv().build(); /** UON-formated object type */ public static final HttpPartSchema T_OBJECT_UON = HttpPartSchema.tObjectUon().build(); /** * Instantiates a new builder for this object. * * @return A new builder for this object. */ public static Builder create() { return new Builder(); } /** * Shortcut for create().type(HttpPartDataType.BOOLEAN). * * @return A new builder for this object. */ public static Builder tBoolean() { return create().tBoolean(); } /** * Shortcut for create().type(HttpPartDataType.FILE). * * @return A new builder for this object. */ public static Builder tFile() { return create().tFile(); } /** * Shortcut for create().type(HttpPartDataType.INTEGER). * * @return A new builder for this object. */ public static Builder tInteger() { return create().tInteger(); } /** * Shortcut for create().type(HttpPartDataType.INTEGER).format(HttpPartFormat.INT32). * * @return A new builder for this object. */ public static Builder tInt32() { return create().tInteger().fInt32(); } /** * Shortcut for create().type(HttpPartDataType.INTEGER).format(HttpPartFormat.INT64). * * @return A new builder for this object. */ public static Builder tInt64() { return create().tInteger().fInt64(); } /** * Shortcut for create().type(HttpPartDataType.NONE). * * @return A new builder for this object. */ public static Builder tNone() { return create().tNone(); } /** * Shortcut for create().type(HttpPartDataType.NUMBER). * * @return A new builder for this object. */ public static Builder tNumber() { return create().tNumber(); } /** * Shortcut for create().type(HttpPartDataType.NUMBER).format(HttpPartFormat.FLOAT). * * @return A new builder for this object. */ public static Builder tFloat() { return create().tNumber().fFloat(); } /** * Shortcut for create().type(HttpPartDataType.NUMBER).format(HttpPartFormat.DOUBLE). * * @return A new builder for this object. */ public static Builder tDouble() { return create().tNumber().fDouble(); } /** * Shortcut for create().type(HttpPartDataType.STRING). * * @return A new builder for this object. */ public static Builder tString() { return create().tString(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.BYTE). * * @return A new builder for this object. */ public static Builder tByte() { return create().tString().fByte(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.BINARY). * * @return A new builder for this object. */ public static Builder tBinary() { return create().tString().fBinary(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.BINARY_SPACED). * * @return A new builder for this object. */ public static Builder tBinarySpaced() { return create().tString().fBinarySpaced(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.DATE). * * @return A new builder for this object. */ public static Builder tDate() { return create().tString().fDate(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.DATE_TIME). * * @return A new builder for this object. */ public static Builder tDateTime() { return create().tString().fDateTime(); } /** * Shortcut for create().type(HttpPartDataType.STRING).format(HttpPartFormat.UON). * * @return A new builder for this object. */ public static Builder tUon() { return create().tString().fUon(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY). * * @return A new builder for this object. */ public static Builder tArray() { return create().tArray(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArray(Builder items) { return create().tArray().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.CSV). * * @return A new builder for this object. */ public static Builder tArrayCsv() { return create().tArray().cfCsv(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.CSV).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArrayCsv(Builder items) { return create().tArray().cfCsv().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.PIPES). * * @return A new builder for this object. */ public static Builder tArrayPipes() { return create().tArray().cfPipes(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.PIPES).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArrayPipes(Builder items) { return create().tArray().cfPipes().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.SSV). * * @return A new builder for this object. */ public static Builder tArraySsv() { return create().tArray().cfSsv(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.SSV).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArraySsv(Builder items) { return create().tArray().cfSsv().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.TSV). * * @return A new builder for this object. */ public static Builder tArrayTsv() { return create().tArray().cfTsv(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.TSV).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArrayTsv(Builder items) { return create().tArray().cfTsv().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.UONC). * * @return A new builder for this object. */ public static Builder tArrayUon() { return create().tArray().cfUon(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.UONC).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArrayUon(Builder items) { return create().tArray().cfUon().items(items); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.MULTI). * * @return A new builder for this object. */ public static Builder tArrayMulti() { return create().tArray().cfMulti(); } /** * Shortcut for create().type(HttpPartDataType.ARRAY).collectionFormat(HttpPartCollectionFormat.MULTI).items(items). * * @param items The schema of the array items. * @return A new builder for this object. */ public static Builder tArrayMulti(Builder items) { return create().tArray().cfMulti().items(items); } /** * Shortcut for create().type(HttpPartDataType.OBJECT). * * @return A new builder for this object. */ public static Builder tObject() { return create().tObject(); } /** * Shortcut for create().type(HttpPartDataType.OBJECT).collectionFormat(HttpPartCollectionFormat.CSV). * * @return A new builder for this object. */ public static Builder tObjectCsv() { return create().tObject().cfCsv(); } /** * Shortcut for create().type(HttpPartDataType.OBJECT).collectionFormat(HttpPartCollectionFormat.PIPES). * * @return A new builder for this object. */ public static Builder tObjectPipes() { return create().tObject().cfPipes(); } /** * Shortcut for create().type(HttpPartDataType.OBJECT).collectionFormat(HttpPartCollectionFormat.SSV). * * @return A new builder for this object. */ public static Builder tObjectSsv() { return create().tObject().cfSsv(); } /** * Shortcut for create().type(HttpPartDataType.OBJECT).collectionFormat(HttpPartCollectionFormat.TSV). * * @return A new builder for this object. */ public static Builder tObjectTsv() { return create().tObject().cfTsv(); } /** * Shortcut for create().type(HttpPartDataType.OBJECT).collectionFormat(HttpPartCollectionFormat.UON). * * @return A new builder for this object. */ public static Builder tObjectUon() { return create().tObject().cfUon(); } /** * Finds the schema information for the specified method parameter. * *

* This method will gather all the schema information from the annotations at the following locations: *

    *
  • The method parameter. *
  • The method parameter class. *
  • The method parameter parent classes and interfaces. *
* * @param c * The annotation to look for. *
Valid values: *
    *
  • {@link Content} *
  • {@link Header} *
  • {@link Query} *
  • {@link FormData} *
  • {@link Path} *
  • {@link Response} *
  • {@link HasQuery} *
  • {@link HasFormData} *
* @param mpi The Java method parameter. * @return The schema information about the parameter. */ public static HttpPartSchema create(Class c, ParamInfo mpi) { return create().applyAll(c, mpi).build(); } /** * Finds the schema information for the specified method return. * *

* This method will gather all the schema information from the annotations at the following locations: *

    *
  • The method. *
  • The method return class. *
  • The method return parent classes and interfaces. *
* * @param c * The annotation to look for. *
Valid values: *
    *
  • {@link Content} *
  • {@link Header} *
  • {@link Query} *
  • {@link FormData} *
  • {@link Path} *
  • {@link Response} *
  • {@link HasQuery} *
  • {@link HasFormData} *
* @param m * The Java method with the return type being checked. * @return The schema information about the parameter. */ public static HttpPartSchema create(Class c, Method m) { return create().applyAll(c, m).build(); } /** * Finds the schema information for the specified class. * *

* This method will gather all the schema information from the annotations on the class and all parent classes/interfaces. * * @param c * The annotation to look for. *
Valid values: *

    *
  • {@link Content} *
  • {@link Header} *
  • {@link Query} *
  • {@link FormData} *
  • {@link Path} *
  • {@link Response} *
  • {@link HasQuery} *
  • {@link HasFormData} *
* @param t * The class containing the parameter. * @return The schema information about the parameter. */ public static HttpPartSchema create(Class c, java.lang.reflect.Type t) { return create().applyAll(c, t).build(); } /** * Shortcut for calling create().type(type); * * @param type The schema type value. * @return A new builder. */ public static Builder create(String type) { return create().type(type); } /** * Shortcut for calling create().type(type).format(format); * * @param type The schema type value. * @param format The schema format value. * @return A new builder. */ public static Builder create(String type, String format) { return create().type(type).format(format); } /** * Finds the schema information on the specified annotation. * * @param a * The annotation to find the schema information on.. * @return The schema information found on the annotation. */ public static HttpPartSchema create(Annotation a) { return create().apply(a).build(); } /** * Finds the schema information on the specified annotation. * * @param a * The annotation to find the schema information on.. * @param defaultName The default part name if not specified on the annotation. * @return The schema information found on the annotation. */ public static HttpPartSchema create(Annotation a, String defaultName) { return create().name(defaultName).apply(a).build(); } //------------------------------------------------------------------------------------------------------------------- // Builder //------------------------------------------------------------------------------------------------------------------- /** * Builder class. */ public static class Builder { String name, _default; Set codes; Set _enum; Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty; HttpPartCollectionFormat collectionFormat = HttpPartCollectionFormat.NO_COLLECTION_FORMAT; HttpPartDataType type = HttpPartDataType.NO_TYPE; HttpPartFormat format = HttpPartFormat.NO_FORMAT; Pattern pattern; Number maximum, minimum, multipleOf; Long maxLength, minLength, maxItems, minItems, maxProperties, minProperties; Map properties; Object items, additionalProperties; boolean noValidate; Class parser; Class serializer; /** * Instantiates a new {@link HttpPartSchema} object based on the configuration of this builder. * *

* This method can be called multiple times to produce new schema objects. * * @return * A new {@link HttpPartSchema} object. *
Never null. */ public HttpPartSchema build() { return new HttpPartSchema(this); } Builder apply(Class c, ParamInfo mpi) { apply(c, mpi.getParameterType().innerType()); mpi.forEachDeclaredAnnotation(c, x -> true, this::apply); return this; } Builder applyAll(Class c, ParamInfo mpi) { return apply(Schema.class, mpi).apply(c, mpi); } Builder apply(Class c, Method m) { apply(c, m.getGenericReturnType()); Annotation a = m.getAnnotation(c); if (a != null) return apply(a); return this; } Builder applyAll(Class c, Method m) { return apply(Schema.class, m).apply(c, m); } Builder apply(Class c, java.lang.reflect.Type t) { if (t instanceof Class) { ClassInfo.of((Class)t).forEachAnnotation(c, x -> true, this::apply); } else if (Value.isType(t)) { apply(c, Value.getParameterType(t)); } return this; } Builder applyAll(Class c, java.lang.reflect.Type t) { return apply(Schema.class, t).apply(c, t); } /** * Apply the specified annotation to this schema. * * @param a The annotation to apply. * @return This object. */ public Builder apply(Annotation a) { if (a instanceof Content) apply((Content)a); else if (a instanceof Header) apply((Header)a); else if (a instanceof FormData) apply((FormData)a); else if (a instanceof Query) apply((Query)a); else if (a instanceof Path) apply((Path)a); else if (a instanceof Response) apply((Response)a); else if (a instanceof StatusCode) apply((StatusCode)a); else if (a instanceof HasQuery) apply((HasQuery)a); else if (a instanceof HasFormData) apply((HasFormData)a); else if (a instanceof Schema) apply((Schema)a); else throw new BasicRuntimeException("Builder.apply(@{0}) not defined", className(a)); return this; } Builder apply(Content a) { if (! SchemaAnnotation.empty(a.schema())) apply(a.schema()); return this; } Builder apply(Header a) { if (! SchemaAnnotation.empty(a.schema())) apply(a.schema()); name(firstNonEmpty(a.name(), a.value())); parser(a.parser()); serializer(a.serializer()); return this; } Builder apply(FormData a) { if (! SchemaAnnotation.empty(a.schema())) apply(a.schema()); name(firstNonEmpty(a.name(), a.value())); parser(a.parser()); serializer(a.serializer()); return this; } Builder apply(Query a) { if (! SchemaAnnotation.empty(a.schema())) apply(a.schema()); name(firstNonEmpty(a.name(), a.value())); parser(a.parser()); serializer(a.serializer()); return this; } Builder apply(Path a) { if (! SchemaAnnotation.empty(a.schema())) apply(a.schema()); name(firstNonEmpty(a.name(), a.value())); parser(a.parser()); serializer(a.serializer()); // Path remainder always allows empty value. if (startsWith(name, '/')) { allowEmptyValue(); required(false); } else if (required == null) { required(true); } return this; } Builder apply(Response a) { allowEmptyValue(true); apply(a.schema()); parser(a.parser()); required(false); serializer(a.serializer()); return this; } Builder apply(StatusCode a) { codes(a.value()); return this; } Builder apply(Items a) { _default(joinnlOrNull(a._default(), a.df())); _enum(toSet(a._enum(), a.e())); collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf())); exclusiveMaximum(a.exclusiveMaximum() || a.emax()); exclusiveMinimum(a.exclusiveMinimum() || a.emin()); format(firstNonEmpty(a.format(), a.f())); items(a.items()); maximum(toNumber(a.maximum(), a.max())); maxItems(firstNmo(a.maxItems(), a.maxi())); maxLength(firstNmo(a.maxLength(), a.maxl())); minimum(toNumber(a.minimum(), a.min())); minItems(firstNmo(a.minItems(), a.mini())); minLength(firstNmo(a.minLength(), a.minl())); multipleOf(toNumber(a.multipleOf(), a.mo())); pattern(firstNonEmpty(a.pattern(), a.p())); type(firstNonEmpty(a.type(), a.t())); uniqueItems(a.uniqueItems() || a.ui()); return this; } Builder apply(SubItems a) { _default(joinnlOrNull(a._default(), a.df())); _enum(toSet(a._enum(), a.e())); collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf())); exclusiveMaximum(a.exclusiveMaximum() || a.emax()); exclusiveMinimum(a.exclusiveMinimum() || a.emin()); format(firstNonEmpty(a.format(), a.f())); items(HttpPartSchema.toJsonMap(a.items())); maximum(toNumber(a.maximum(), a.max())); maxItems(firstNmo(a.maxItems(), a.maxi())); maxLength(firstNmo(a.maxLength(), a.maxl())); minimum(toNumber(a.minimum(), a.min())); minItems(firstNmo(a.minItems(), a.mini())); minLength(firstNmo(a.minLength(), a.minl())); multipleOf(toNumber(a.multipleOf(), a.mo())); pattern(firstNonEmpty(a.pattern(), a.p())); type(firstNonEmpty(a.type(), a.t())); uniqueItems(a.uniqueItems() || a.ui()); return this; } Builder apply(Schema a) { _default(joinnlOrNull(a._default(), a.df())); _enum(toSet(a._enum(), a.e())); additionalProperties(HttpPartSchema.toJsonMap(a.additionalProperties())); allowEmptyValue(a.allowEmptyValue() || a.aev()); collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf())); exclusiveMaximum(a.exclusiveMaximum() || a.emax()); exclusiveMinimum(a.exclusiveMinimum() || a.emin()); format(firstNonEmpty(a.format(), a.f())); items(a.items()); maximum(toNumber(a.maximum(), a.max())); maxItems(firstNmo(a.maxItems(), a.maxi())); maxLength(firstNmo(a.maxLength(), a.maxl())); maxProperties(firstNmo(a.maxProperties(), a.maxp())); minimum(toNumber(a.minimum(), a.min())); minItems(firstNmo(a.minItems(), a.mini())); minLength(firstNmo(a.minLength(), a.minl())); minProperties(firstNmo(a.minProperties(), a.minp())); multipleOf(toNumber(a.multipleOf(), a.mo())); pattern(firstNonEmpty(a.pattern(), a.p())); properties(HttpPartSchema.toJsonMap(a.properties())); required(a.required() || a.r()); skipIfEmpty(a.skipIfEmpty() || a.sie()); type(firstNonEmpty(a.type(), a.t())); uniqueItems(a.uniqueItems() || a.ui()); return this; } Builder apply(HasQuery a) { name(firstNonEmpty(a.name(), a.value())); return this; } Builder apply(HasFormData a) { name(firstNonEmpty(a.name(), a.value())); return this; } Builder apply(JsonMap m) { if (m != null && ! m.isEmpty()) { _default(m.getString("default")); _enum(HttpPartSchema.toSet(m.getString("enum"))); allowEmptyValue(m.getBoolean("allowEmptyValue")); exclusiveMaximum(m.getBoolean("exclusiveMaximum")); exclusiveMinimum(m.getBoolean("exclusiveMinimum")); required(m.getBoolean("required")); uniqueItems(m.getBoolean("uniqueItems")); collectionFormat(m.getString("collectionFormat")); type(m.getString("type")); format(m.getString("format")); pattern(m.getString("pattern")); maximum(m.get("maximum", Number.class)); minimum(m.get("minimum", Number.class)); multipleOf(m.get("multipleOf", Number.class)); maxItems(m.get("maxItems", Long.class)); maxLength(m.get("maxLength", Long.class)); maxProperties(m.get("maxProperties", Long.class)); minItems(m.get("minItems", Long.class)); minLength(m.get("minLength", Long.class)); minProperties(m.get("minProperties", Long.class)); items(m.getMap("items")); properties(m.getMap("properties")); additionalProperties(m.getMap("additionalProperties")); apply(m.getMap("schema", null)); } return this; } /** * name field. * *

* Applicable to the following Swagger schema objects: *

* * @param value * The new value for this property. * @return This object. */ public Builder name(String value) { if (isNotEmpty(value)) name = value; return this; } /** * Synonym for {@link #name(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder n(String value) { return name(value); } /** * httpStatusCode key. * *

* Applicable to the following Swagger schema objects: *

* * @param value * The new value for this property. *
Ignored if null or an empty array. * @return This object. */ public Builder codes(int[] value) { if (value != null && value.length != 0) for (int v : value) code(v); return this; } /** * httpStatusCode key. * *

* Applicable to the following Swagger schema objects: *

* * @param value * The new value for this property. *
Ignored if value is 0. * @return This object. */ public Builder code(int value) { if (value != 0) { if (codes == null) codes = new TreeSet<>(); codes.add(value); } return this; } /** * required field. * *

* Determines whether the parameter is mandatory. * *

* Applicable to the following Swagger schema objects: *

* * @param value * The new value for this property. *
Ignored if value is null. * @return This object. */ public Builder required(Boolean value) { required = resolve(value, required); return this; } /** * Synonym for {@link #required(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder r(Boolean value) { return required(value); } /** * required field. * *

* Determines whether the parameter is mandatory. * *

* Same as {@link #required(Boolean)} but takes in a boolean value as a string. * * @param value * The new value for this property. *
Ignored if value is null or empty. * @return This object. */ public Builder required(String value) { required = resolve(value, required); return this; } /** * Synonym for {@link #required(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder r(String value) { return required(value); } /** * required field. * *

* Shortcut for calling required(true);. * * @return This object. */ public Builder required() { return required(true); } /** * Synonym for {@link #required()}. * * @return This object. */ public Builder r() { return required(); } /** * type field. * *

* The type of the parameter. * *

* If the type is not specified, it will be auto-detected based on the parameter class type. * *

* Applicable to the following Swagger schema objects: *

* *
    *
  • * "string" *
    Parameter must be a string or a POJO convertible from a string. *
  • * "number" *
    Parameter must be a number primitive or number object. *
    If parameter is Object, creates either a Float or Double depending on the size of the number. *
  • * "integer" *
    Parameter must be a integer/long primitive or integer/long object. *
    If parameter is Object, creates either a Short, Integer, or Long depending on the size of the number. *
  • * "boolean" *
    Parameter must be a boolean primitive or object. *
  • * "array" *
    Parameter must be an array or collection. *
    Elements must be strings or POJOs convertible from strings. *
    If parameter is Object, creates an {@link JsonList}. *
  • * "object" *
    Parameter must be a map or bean. *
    If parameter is Object, creates an {@link JsonMap}. *
    Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts. *
  • * "file" *
    This type is currently not supported. *
* *
See Also:
* * @param value * The new value for this property. *
Ignored if value is null or empty. * @return This object. */ public Builder type(String value) { try { if (isNotEmpty(value)) type = HttpPartDataType.fromString(value); } catch (Exception e) { throw new ContextRuntimeException("Invalid value ''{0}'' passed in as type value. Valid values: {1}", value, HttpPartDataType.values()); } return this; } /** * Synonym for {@link #type(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder t(String value) { return type(value); } /** * Shortcut for type(HttpPartDataType.STRING). * * @return This object. */ public Builder tString() { type = HttpPartDataType.STRING; return this; } /** * Shortcut for type(HttpPartDataType.NUMBER). * * @return This object. */ public Builder tNumber() { type = HttpPartDataType.NUMBER; return this; } /** * Shortcut for type(HttpPartDataType.INTEGER). * * @return This object. */ public Builder tInteger() { type = HttpPartDataType.INTEGER; return this; } /** * Shortcut for type(HttpPartDataType.BOOLEAN). * * @return This object. */ public Builder tBoolean() { type = HttpPartDataType.BOOLEAN; return this; } /** * Shortcut for type(HttpPartDataType.ARRAY). * * @return This object. */ public Builder tArray() { type = HttpPartDataType.ARRAY; return this; } /** * Shortcut for type(HttpPartDataType.OBJECT). * * @return This object. */ public Builder tObject() { type = HttpPartDataType.OBJECT; return this; } /** * Shortcut for type(HttpPartDataType.FILE). * * @return This object. */ public Builder tFile() { type = HttpPartDataType.FILE; return this; } /** * Shortcut for type(HttpPartDataType.NO_TYPE). * * @return This object. */ public Builder tNone() { type = HttpPartDataType.NO_TYPE; return this; } /** * type field. * *

* The type of the parameter. * *

* If the type is not specified, it will be auto-detected based on the parameter class type. * *

* Applicable to the following Swagger schema objects: *

* *
    *
  • {@link HttpPartDataType} *
      *
    • * {@link HttpPartDataType#STRING STRING} *
      Parameter must be a string or a POJO convertible from a string. *
    • * {@link HttpPartDataType#NUMBER NUMBER} *
      Parameter must be a number primitive or number object. *
      If parameter is Object, creates either a Float or Double depending on the size of the number. *
    • * {@link HttpPartDataType#INTEGER INTEGER} *
      Parameter must be a integer/long primitive or integer/long object. *
      If parameter is Object, creates either a Short, Integer, or Long depending on the size of the number. *
    • * {@link HttpPartDataType#BOOLEAN BOOLEAN} *
      Parameter must be a boolean primitive or object. *
    • * {@link HttpPartDataType#ARRAY ARRAY} *
      Parameter must be an array or collection. *
      Elements must be strings or POJOs convertible from strings. *
      If parameter is Object, creates an {@link JsonList}. *
    • * {@link HttpPartDataType#OBJECT OBJECT} *
      Parameter must be a map or bean. *
      If parameter is Object, creates an {@link JsonMap}. *
      Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts. *
    • * {@link HttpPartDataType#FILE FILE} *
      This type is currently not supported. *
    *
* *
See Also:
* * @param value * The new value for this property. * @return This object. */ public Builder type(HttpPartDataType value) { this.type = value; return this; } /** * Synonym for {@link #type(HttpPartDataType)}. * * @param value * The new value for this property. * @return This object. */ public Builder t(HttpPartDataType value) { return type(value); } /** * format field. * *

* The extending format for the previously mentioned parameter type. * *

* Applicable to the following Swagger schema objects: *

* *
    *
  • * "int32" - Signed 32 bits. *
    Only valid with type "integer". *
  • * "int64" - Signed 64 bits. *
    Only valid with type "integer". *
  • * "float" - 32-bit floating point number. *
    Only valid with type "number". *
  • * "double" - 64-bit floating point number. *
    Only valid with type "number". *
  • * "byte" - BASE-64 encoded characters. *
    Only valid with type "string". *
    Parameters of type POJO convertible from string are converted after the string has been decoded. *
  • * "binary" - Hexadecimal encoded octets (e.g. "00FF"). *
    Only valid with type "string". *
    Parameters of type POJO convertible from string are converted after the string has been decoded. *
  • * "binary-spaced" - Hexadecimal encoded octets, spaced (e.g. "00 FF"). *
    Only valid with type "string". *
    Parameters of type POJO convertible from string are converted after the string has been decoded. *
  • * "date" - An RFC3339 full-date. *
    Only valid with type "string". *
  • * "date-time" - An RFC3339 date-time. *
    Only valid with type "string". *
  • * "password" - Used to hint UIs the input needs to be obscured. *
    This format does not affect the serialization or parsing of the parameter. *
  • * "uon" - UON notation (e.g. "(foo=bar,baz=@(qux,123))"). *
    Only valid with type "object". *
    If not specified, then the input is interpreted as plain-text and is converted to a POJO directly. *
* *
See Also:
* * @param value * The new value for this property. *
Ignored if value is null or an empty string. * @return This object. */ public Builder format(String value) { try { if (isNotEmpty(value)) format = HttpPartFormat.fromString(value); } catch (Exception e) { throw new ContextRuntimeException("Invalid value ''{0}'' passed in as format value. Valid values: {1}", value, HttpPartFormat.values()); } return this; } /** * Synonym for {@link #format(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder f(String value) { return format(value); } /** * format field. * *

* The extending format for the previously mentioned parameter type. * *

* Applicable to the following Swagger schema objects: *

* *
    *
      {@link HttpPartFormat} *
        *
      • * {@link HttpPartFormat#INT32 INT32} - Signed 32 bits. *
        Only valid with type "integer". *
      • * {@link HttpPartFormat#INT64 INT64} - Signed 64 bits. *
        Only valid with type "integer". *
      • * {@link HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number. *
        Only valid with type "number". *
      • * {@link HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number. *
        Only valid with type "number". *
      • * {@link HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters. *
        Only valid with type "string". *
        Parameters of type POJO convertible from string are converted after the string has been decoded. *
      • * {@link HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. "00FF"). *
        Only valid with type "string". *
        Parameters of type POJO convertible from string are converted after the string has been decoded. *
      • * {@link HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Hexadecimal encoded octets, spaced (e.g. "00 FF"). *
        Only valid with type "string". *
        Parameters of type POJO convertible from string are converted after the string has been decoded. *
      • * {@link HttpPartFormat#DATE DATE} - An RFC3339 full-date. *
        Only valid with type "string". *
      • * {@link HttpPartFormat#DATE_TIME DATE_TIME} - An RFC3339 date-time. *
        Only valid with type "string". *
      • * {@link HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured. *
        This format does not affect the serialization or parsing of the parameter. *
      • * {@link HttpPartFormat#UON UON} - UON notation (e.g. "(foo=bar,baz=@(qux,123))"). *
        Only valid with type "object". *
        If not specified, then the input is interpreted as plain-text and is converted to a POJO directly. *
      *
    * *
    See Also:
    * * @param value * The new value for this property. * @return This object. */ public Builder format(HttpPartFormat value) { format = value; return this; } /** * Synonym for {@link #format(HttpPartFormat)}. * * @param value * The new value for this property. * @return This object. */ public Builder f(HttpPartFormat value) { return format(value); } /** * Shortcut for format(HttpPartFormat.INT32). * * @return This object. */ public Builder fInt32() { format = HttpPartFormat.INT32; return this; } /** * Shortcut for format(HttpPartFormat.INT64). * * @return This object. */ public Builder fInt64() { format = HttpPartFormat.INT64; return this; } /** * Shortcut for format(HttpPartFormat.FLOAT). * * @return This object. */ public Builder fFloat() { format = HttpPartFormat.FLOAT; return this; } /** * Shortcut for format(HttpPartFormat.DOUBLE). * * @return This object. */ public Builder fDouble() { format = HttpPartFormat.DOUBLE; return this; } /** * Shortcut for format(HttpPartFormat.BYTE). * * @return This object. */ public Builder fByte() { format = HttpPartFormat.BYTE; return this; } /** * Shortcut for format(HttpPartFormat.BINARY). * * @return This object. */ public Builder fBinary() { format = HttpPartFormat.BINARY; return this; } /** * Shortcut for format(HttpPartFormat.BINARY_SPACED). * * @return This object. */ public Builder fBinarySpaced() { format = HttpPartFormat.BINARY_SPACED; return this; } /** * Shortcut for format(HttpPartFormat.DATE). * * @return This object. */ public Builder fDate() { format = HttpPartFormat.DATE; return this; } /** * Shortcut for format(HttpPartFormat.DATE_TIME). * * @return This object. */ public Builder fDateTime() { format = HttpPartFormat.DATE_TIME; return this; } /** * Shortcut for format(HttpPartFormat.PASSWORD). * * @return This object. */ public Builder fPassword() { format = HttpPartFormat.PASSWORD; return this; } /** * Shortcut for format(HttpPartFormat.UON). * * @return This object. */ public Builder fUon() { format = HttpPartFormat.UON; return this; } /** * Shortcut for format(HttpPartFormat.NO_FORMAT). * * @return This object. */ public Builder fNone() { format = HttpPartFormat.NO_FORMAT; return this; } /** * allowEmptyValue field. * *

    * Sets the ability to pass empty-valued parameters. *
    This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value. *
    The default value is false. * *

    * Applicable to the following Swagger schema objects: *

    * * @param value * The new value for this property. *
    Ignored if value is null. * @return This object. */ public Builder allowEmptyValue(Boolean value) { allowEmptyValue = resolve(value, allowEmptyValue); return this; } /** * Synonym for {@link #allowEmptyValue(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder aev(Boolean value) { return allowEmptyValue(value); } /** * allowEmptyValue field. * *

    * Same as {@link #allowEmptyValue(Boolean)} but takes in a string boolean value. * * @param value * The new value for this property. *
    Ignored if value is null or empty. * @return This object. */ public Builder allowEmptyValue(String value) { allowEmptyValue = resolve(value, allowEmptyValue); return this; } /** * Synonym for {@link #allowEmptyValue(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder aev(String value) { return allowEmptyValue(value); } /** * allowEmptyValue field. * *

    * Shortcut for calling allowEmptyValue(true);. * * @return This object. */ public Builder allowEmptyValue() { return allowEmptyValue(true); } /** * Synonym for {@link #allowEmptyValue()}. * * @return This object. */ public Builder aev() { return allowEmptyValue(true); } /** * items field. * *

    * Describes the type of items in the array. *

    * Required if type is "array". *
    Can only be used if type is "array". * *

    * Applicable to the following Swagger schema objects: *

    * * @param value * The new value for this property. *
    Ignored if value is null or empty. * @return This object. */ public Builder items(Builder value) { if (value != null) this.items = value; return this; } /** * Synonym for {@link #items(HttpPartSchema.Builder)}. * * @param value * The new value for this property. * @return This object. */ public Builder i(Builder value) { return items(value); } /** * items field. * *

    * Describes the type of items in the array. *

    * Required if type is "array". *
    Can only be used if type is "array". * *

    * Applicable to the following Swagger schema objects: *

    * * @param value * The new value for this property. *
    Ignored if value is null or empty. * @return This object. */ public Builder items(HttpPartSchema value) { if (value != null) this.items = value; return this; } /** * Synonym for {@link #items(HttpPartSchema)}. * * @param value * The new value for this property. * @return This object. */ public Builder i(HttpPartSchema value) { return items(value); } Builder items(JsonMap value) { if (value != null && ! value.isEmpty()) items = HttpPartSchema.create().apply(value); return this; } Builder items(Items value) { if (! ItemsAnnotation.empty(value)) items = HttpPartSchema.create().apply(value); return this; } Builder items(SubItems value) { if (! SubItemsAnnotation.empty(value)) items = HttpPartSchema.create().apply(value); return this; } /** * collectionFormat field. * *

    * Determines the format of the array if type "array" is used. *
    Can only be used if type is "array". * *

    * Applicable to the following Swagger schema objects: *

    * *

    * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements. * *

      *
    • * "csv" (default) - Comma-separated values (e.g. "foo,bar"). *
    • * "ssv" - Space-separated values (e.g. "foo bar"). *
    • * "tsv" - Tab-separated values (e.g. "foo\tbar"). *
    • * "pipes - Pipe-separated values (e.g. "foo|bar"). *
    • * "multi" - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. "foo=bar&foo=baz"). *
    • * "uon" - UON notation (e.g. "@(foo,bar)"). *
    • *
    * * @param value * The new value for this property. *
    Ignored if value is null or empty. * @return This object. */ public Builder collectionFormat(String value) { try { if (isNotEmpty(value)) this.collectionFormat = HttpPartCollectionFormat.fromString(value); } catch (Exception e) { throw new ContextRuntimeException("Invalid value ''{0}'' passed in as collectionFormat value. Valid values: {1}", value, HttpPartCollectionFormat.values()); } return this; } /** * Synonym for {@link #collectionFormat(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder cf(String value) { return collectionFormat(value); } /** * collectionFormat field. * *

    * Determines the format of the array if type "array" is used. *
    Can only be used if type is "array". * *

    * Applicable to the following Swagger schema objects: *

    * *

    * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements. * *

      *
        {@link HttpPartCollectionFormat} *
          *
        • * {@link HttpPartCollectionFormat#CSV CSV} (default) - Comma-separated values (e.g. "foo,bar"). *
        • * {@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. "foo bar"). *
        • * {@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. "foo\tbar"). *
        • * {@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. "foo|bar"). *
        • * {@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. "foo=bar&foo=baz"). *
        • * {@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. "@(foo,bar)"). *
        *
      * * @param value * The new value for this property. * @return This object. */ public Builder collectionFormat(HttpPartCollectionFormat value) { collectionFormat = value; return this; } /** * Synonym for {@link #collectionFormat(HttpPartCollectionFormat)}. * * @param value * The new value for this property. * @return This object. */ public Builder cf(HttpPartCollectionFormat value) { return collectionFormat(value); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.CSV). * * @return This object. */ public Builder cfCsv() { return collectionFormat(HttpPartCollectionFormat.CSV); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.SSV). * * @return This object. */ public Builder cfSsv() { return collectionFormat(HttpPartCollectionFormat.SSV); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.TSV). * * @return This object. */ public Builder cfTsv() { return collectionFormat(HttpPartCollectionFormat.TSV); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.PIPES). * * @return This object. */ public Builder cfPipes() { return collectionFormat(HttpPartCollectionFormat.PIPES); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.MULTI). * * @return This object. */ public Builder cfMulti() { return collectionFormat(HttpPartCollectionFormat.MULTI); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.UONC). * * @return This object. */ public Builder cfUon() { return collectionFormat(HttpPartCollectionFormat.UONC); } /** * Shortcut for collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT). * * @return This object. */ public Builder cfNone() { return collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT); } /** * default field. * *

      * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request. *
      (Note: "default" has no meaning for required parameters.) * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder _default(String value) { if (value != null) this._default = value; return this; } /** * Synonym for {@link #_default(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder df(String value) { return _default(value); } /** * maximum field. * *

      * Defines the maximum value for a parameter of numeric types. * *

      * Only allowed for the following types: "integer", "number". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder maximum(Number value) { if (value != null) this.maximum = value; return this; } /** * Synonym for {@link #maximum(Number)}. * * @param value * The new value for this property. * @return This object. */ public Builder max(Number value) { return maximum(value); } /** * exclusiveMaximum field. * *

      * Defines whether the maximum is matched exclusively. * *

      * Only allowed for the following types: "integer", "number". *
      If true, must be accompanied with maximum. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder exclusiveMaximum(Boolean value) { exclusiveMaximum = resolve(value, exclusiveMaximum); return this; } /** * Synonym for {@link #exclusiveMaximum(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder emax(Boolean value) { return exclusiveMaximum(value); } /** * exclusiveMaximum field. * *

      * Same as {@link #exclusiveMaximum(Boolean)} but takes in a string boolean value. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder exclusiveMaximum(String value) { exclusiveMaximum = resolve(value, exclusiveMaximum); return this; } /** * Synonym for {@link #exclusiveMaximum(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder emax(String value) { return exclusiveMaximum(value); } /** * exclusiveMaximum field. * *

      * Shortcut for calling exclusiveMaximum(true);. * * @return This object. */ public Builder exclusiveMaximum() { return exclusiveMaximum(true); } /** * Synonym for {@link #exclusiveMaximum()}. * * @return This object. */ public Builder emax() { return exclusiveMaximum(); } /** * minimum field. * *

      * Defines the minimum value for a parameter of numeric types. * *

      * Only allowed for the following types: "integer", "number". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder minimum(Number value) { if (value != null) this.minimum = value; return this; } /** * Synonym for {@link #minimum(Number)}. * * @param value * The new value for this property. * @return This object. */ public Builder min(Number value) { return minimum(value); } /** * exclusiveMinimum field. * *

      * Defines whether the minimum is matched exclusively. * *

      * Only allowed for the following types: "integer", "number". *
      If true, must be accompanied with minimum. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder exclusiveMinimum(Boolean value) { exclusiveMinimum = resolve(value, exclusiveMinimum); return this; } /** * Synonym for {@link #exclusiveMinimum(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder emin(Boolean value) { return exclusiveMinimum(value); } /** * exclusiveMinimum field. * *

      * Same as {@link #exclusiveMinimum(Boolean)} but takes in a string boolean value. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder exclusiveMinimum(String value) { exclusiveMinimum = resolve(value, exclusiveMinimum); return this; } /** * Synonym for {@link #exclusiveMinimum(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder emin(String value) { return exclusiveMinimum(value); } /** * exclusiveMinimum field. * *

      * Shortcut for calling exclusiveMinimum(true);. * * @return This object. */ public Builder exclusiveMinimum() { return exclusiveMinimum(true); } /** * Synonym for {@link #exclusiveMinimum()}. * * @return This object. */ public Builder emin() { return exclusiveMinimum(); } /** * maxLength field. * *

      * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword. *
      The length of a string instance is defined as the number of its characters as defined by RFC 4627. * *

      * Only allowed for the following types: "string". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or -1. * @return This object. */ public Builder maxLength(Long value) { maxLength = resolve(value, maxLength); return this; } /** * Synonym for {@link #maxLength(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxl(Long value) { return maxLength(value); } /** * maxLength field. * *

      * Same as {@link #maxLength(Long)} but takes in a string number. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder maxLength(String value) { maxLength = resolve(value, maxLength); return this; } /** * Synonym for {@link #maxLength(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxl(String value) { return maxLength(value); } /** * minLength field. * *

      * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword. *
      The length of a string instance is defined as the number of its characters as defined by RFC 4627. * *

      * Only allowed for the following types: "string". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or -1. * @return This object. */ public Builder minLength(Long value) { minLength = resolve(value, minLength); return this; } /** * Synonym for {@link #minLength(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder minl(Long value) { return minLength(value); } /** * minLength field. * *

      * Same as {@link #minLength(Long)} but takes in a string number. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder minLength(String value) { minLength = resolve(value, minLength); return this; } /** * Synonym for {@link #minLength(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder minl(String value) { return minLength(value); } /** * pattern field. * *

      * A string input is valid if it matches the specified regular expression pattern. * *

      * Only allowed for the following types: "string". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder pattern(String value) { try { if (isNotEmpty(value)) this.pattern = Pattern.compile(value); } catch (Exception e) { throw new ContextRuntimeException(e, "Invalid value {0} passed in as pattern value. Must be a valid regular expression.", value); } return this; } /** * Synonym for {@link #pattern(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder p(String value) { return pattern(value); } /** * maxItems field. * *

      * An array or collection is valid if its size is less than, or equal to, the value of this keyword. * *

      * Only allowed for the following types: "array". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or -1. * @return This object. */ public Builder maxItems(Long value) { maxItems = resolve(value, maxItems); return this; } /** * Synonym for {@link #maxItems(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxi(Long value) { return maxItems(value); } /** * maxItems field. * *

      * Same as {@link #maxItems(Long)} but takes in a string number. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder maxItems(String value) { maxItems = resolve(value, maxItems); return this; } /** * Synonym for {@link #maxItems(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxi(String value) { return maxItems(value); } /** * minItems field. * *

      * An array or collection is valid if its size is greater than, or equal to, the value of this keyword. * *

      * Only allowed for the following types: "array". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or -1. * @return This object. */ public Builder minItems(Long value) { minItems = resolve(value, minItems); return this; } /** * Synonym for {@link #minItems(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder mini(Long value) { return minItems(value); } /** * minItems field. * *

      * Same as {@link #minItems(Long)} but takes in a string number. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder minItems(String value) { minItems = resolve(value, minItems); return this; } /** * Synonym for {@link #minItems(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder mini(String value) { return minItems(value); } /** * uniqueItems field. * *

      * If true, the input validates successfully if all of its elements are unique. * *

      *
      If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway). *
      Otherwise, the collection or array is checked for duplicate items. * *

      * Only allowed for the following types: "array". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder uniqueItems(Boolean value) { uniqueItems = resolve(value, uniqueItems); return this; } /** * Synonym for {@link #uniqueItems(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder ui(Boolean value) { return uniqueItems(value); } /** * uniqueItems field. * *

      * Same as {@link #uniqueItems(Boolean)} but takes in a string boolean. * * @param value * The new value for this property. *
      Ignored if value is null or empty.. * @return This object. */ public Builder uniqueItems(String value) { uniqueItems = resolve(value, uniqueItems); return this; } /** * Synonym for {@link #uniqueItems(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder ui(String value) { return uniqueItems(value); } /** * uniqueItems field. * *

      * Shortcut for calling uniqueItems(true);. * * @return This object. */ public Builder uniqueItems() { return uniqueItems(true); } /** * Synonym for {@link #uniqueItems()}. * * @return This object. */ public Builder ui() { return uniqueItems(); } /** * skipIfEmpty field. * *

      * Identifies whether an item should be skipped during serialization if it's empty. * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder skipIfEmpty(Boolean value) { skipIfEmpty = resolve(value, skipIfEmpty); return this; } /** * Synonym for {@link #skipIfEmpty(Boolean)}. * * @param value * The new value for this property. * @return This object. */ public Builder sie(Boolean value) { return skipIfEmpty(value); } /** * skipIfEmpty field. * *

      * Same as {@link #skipIfEmpty(Boolean)} but takes in a string boolean. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder skipIfEmpty(String value) { skipIfEmpty = resolve(value, skipIfEmpty); return this; } /** * Synonym for {@link #skipIfEmpty(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder sie(String value) { return skipIfEmpty(value); } /** * Identifies whether an item should be skipped if it's empty. * *

      * Shortcut for calling skipIfEmpty(true);. * * @return This object. */ public Builder skipIfEmpty() { return skipIfEmpty(true); } /** * Synonym for {@link #skipIfEmpty()}. * * @return This object. */ public Builder sie() { return skipIfEmpty(); } /** * enum field. * *

      * If specified, the input validates successfully if it is equal to one of the elements in this array. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or an empty set. * @return This object. */ public Builder _enum(Set value) { if (value != null && ! value.isEmpty()) this._enum = value; return this; } /** * Synonym for {@link #_enum(Set)}. * * @param value * The new value for this property. * @return This object. */ public Builder e(Set value) { return _enum(value); } /** * _enum field. * *

      * Same as {@link #_enum(Set)} but takes in a var-args array. * * @param values * The new values for this property. *
      Ignored if value is empty. * @return This object. */ public Builder _enum(String...values) { return _enum(set(values)); } /** * Synonym for {@link #_enum(String...)}. * * @param values * The new values for this property. * @return This object. */ public Builder e(String...values) { return _enum(values); } /** * multipleOf field. * *

      * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer. * *

      * Only allowed for the following types: "integer", "number". * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder multipleOf(Number value) { if (value != null) this.multipleOf = value; return this; } /** * Synonym for {@link #multipleOf(Number)}. * * @param value * The new value for this property. * @return This object. */ public Builder mo(Number value) { return multipleOf(value); } /** * mapProperties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or -1. * @return This object. */ public Builder maxProperties(Long value) { maxProperties = resolve(value, maxProperties); return this; } /** * Synonym for {@link #maxProperties(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxp(Long value) { return maxProperties(value); } /** * mapProperties field. * *

      * Same as {@link #maxProperties(Long)} but takes in a string number. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder maxProperties(String value) { maxProperties = resolve(value, maxProperties); return this; } /** * Synonym for {@link #maxProperties(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder maxp(String value) { return maxProperties(value); } /** * minProperties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder minProperties(Long value) { minProperties = resolve(value, minProperties); return this; } /** * Synonym for {@link #minProperties(Long)}. * * @param value * The new value for this property. * @return This object. */ public Builder minp(Long value) { return minProperties(value); } /** * minProperties field. * *

      * Same as {@link #minProperties(Long)} but takes in a string boolean. * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder minProperties(String value) { minProperties = resolve(value, minProperties); return this; } /** * Synonym for {@link #minProperties(String)}. * * @param value * The new value for this property. * @return This object. */ public Builder minp(String value) { return minProperties(value); } /** * properties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param key * The property name. * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder property(String key, Builder value) { if ( key != null && value != null) { if (properties == null) properties = map(); properties.put(key, value); } return this; } /** * properties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param key * The property name. * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder property(String key, HttpPartSchema value) { if ( key != null && value != null) { if (properties == null) properties = map(); properties.put(key, value); } return this; } /** * Shortcut for property(key, value). * *

      * Applicable to the following Swagger schema objects: *

      * * @param key * The property name. * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder p(String key, Builder value) { return property(key, value); } /** * Shortcut for property(key, value). * *

      * Applicable to the following Swagger schema objects: *

      * * @param key * The property name. * @param value * The new value for this property. *
      Ignored if value is null. * @return This object. */ public Builder p(String key, HttpPartSchema value) { return property(key, value); } private Builder properties(JsonMap value) { if (value != null) value.forEach((k,v) -> property(k, HttpPartSchema.create().apply((JsonMap)v))); return this; } /** * additionalProperties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder additionalProperties(Builder value) { if (value != null) additionalProperties = value; return this; } /** * additionalProperties field. * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder additionalProperties(HttpPartSchema value) { if (value != null) additionalProperties = value; return this; } /** * Shortcut for additionalProperties(value) * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder ap(Builder value) { return additionalProperties(value); } /** * Shortcut for additionalProperties(value) * *

      * Applicable to the following Swagger schema objects: *

      * * @param value * The new value for this property. *
      Ignored if value is null or empty. * @return This object. */ public Builder ap(HttpPartSchema value) { return additionalProperties(value); } private Builder additionalProperties(JsonMap value) { if (value != null && ! value.isEmpty()) additionalProperties = HttpPartSchema.create().apply(value); return this; } /** * Identifies the part serializer to use for serializing this part. * * @param value * The new value for this property. *
      Ignored if value is null or {@link HttpPartSerializer.Void}. * @return This object. */ public Builder serializer(Class value) { if (isNotVoid(value)) serializer = value; return this; } /** * Identifies the part parser to use for parsing this part. * * @param value * The new value for this property. *
      Ignored if value is null or {@link HttpPartParser.Void}. * @return This object. */ public Builder parser(Class value) { if (isNotVoid(value)) parser = value; return this; } /** * Disables Swagger schema usage validation checking. * * @param value Specify true to prevent {@link ContextRuntimeException} from being thrown if invalid Swagger usage was detected. * @return This object. */ public Builder noValidate(Boolean value) { if (value != null) this.noValidate = value; return this; } /** * Disables Swagger schema usage validation checking. * *

      * Shortcut for calling noValidate(true);. * * @return This object. */ public Builder noValidate() { return noValidate(true); } private Boolean resolve(String newValue, Boolean oldValue) { return isEmpty(newValue) ? oldValue : Boolean.valueOf(newValue); } private Boolean resolve(Boolean newValue, Boolean oldValue) { return newValue == null ? oldValue : newValue; } private Long resolve(String newValue, Long oldValue) { return isEmpty(newValue) ? oldValue : Long.parseLong(newValue); } private Long resolve(Long newValue, Long oldValue) { return (newValue == null || newValue == -1) ? oldValue : newValue; } private Set toSet(String[]...s) { return HttpPartSchema.toSet(s); } private Number toNumber(String...s) { return HttpPartSchema.toNumber(s); } private Long firstNmo(Long...l) { for (Long ll : l) if (ll != null && ll != -1) return ll; return null; } private String joinnlOrNull(String[]...s) { for (String[] ss : s) if (ss.length > 0) return joinnl(ss); return null; } } //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- final String name; final String _default; final Set _enum; final Map properties; final boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty; final HttpPartCollectionFormat collectionFormat; final HttpPartDataType type; final HttpPartFormat format; final Pattern pattern; final HttpPartSchema items, additionalProperties; final Number maximum, minimum, multipleOf; final Long maxLength, minLength, maxItems, minItems, maxProperties, minProperties; final Class parser; final Class serializer; final ClassMeta parsedType; HttpPartSchema(Builder b) { this.name = b.name; this._default = b._default; this._enum = copy(b._enum); this.properties = build(b.properties, b.noValidate); this.allowEmptyValue = resolve(b.allowEmptyValue); this.exclusiveMaximum = resolve(b.exclusiveMaximum); this.exclusiveMinimum = resolve(b.exclusiveMinimum); this.required = resolve(b.required); this.uniqueItems = resolve(b.uniqueItems); this.skipIfEmpty = resolve(b.skipIfEmpty); this.collectionFormat = b.collectionFormat; this.type = b.type; this.format = b.format; this.pattern = b.pattern; this.items = build(b.items, b.noValidate); this.additionalProperties = build(b.additionalProperties, b.noValidate); this.maximum = b.maximum; this.minimum = b.minimum; this.multipleOf = b.multipleOf; this.maxItems = b.maxItems; this.maxLength = b.maxLength; this.maxProperties = b.maxProperties; this.minItems = b.minItems; this.minLength = b.minLength; this.minProperties = b.minProperties; this.parser = b.parser; this.serializer = b.serializer; // Calculate parse type Class parsedType = Object.class; if (type == ARRAY) { if (items != null) parsedType = Array.newInstance(items.parsedType.getInnerClass(), 0).getClass(); } else if (type == BOOLEAN) { parsedType = Boolean.class; } else if (type == INTEGER) { if (format == INT64) parsedType = Long.class; else parsedType = Integer.class; } else if (type == NUMBER) { if (format == DOUBLE) parsedType = Double.class; else parsedType = Float.class; } else if (type == STRING) { if (format == BYTE || format == BINARY || format == BINARY_SPACED) parsedType = byte[].class; else if (format == DATE || format == DATE_TIME) parsedType = Calendar.class; else parsedType = String.class; } this.parsedType = BeanContext.DEFAULT.getClassMeta(parsedType); if (b.noValidate) return; // Validation. List errors = list(); ListBuilder notAllowed = listBuilder(String.class); boolean invalidFormat = false; switch (type) { case STRING: { notAllowed .addIf(properties != null, "properties") .addIf(additionalProperties != null, "additionalProperties") .addIf(exclusiveMaximum, "exclusiveMaximum") .addIf(exclusiveMinimum, "exclusiveMinimum") .addIf(uniqueItems, "uniqueItems") .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat") .addIf(items != null, "items") .addIf(maximum != null, "maximum") .addIf(minimum != null, "minimum") .addIf(multipleOf != null, "multipleOf") .addIf(maxItems != null, "maxItems") .addIf(minItems != null, "minItems") .addIf(minProperties != null, "minProperties"); invalidFormat = ! format.isOneOf(HttpPartFormat.BYTE, HttpPartFormat.BINARY, HttpPartFormat.BINARY_SPACED, HttpPartFormat.DATE, HttpPartFormat.DATE_TIME, HttpPartFormat.PASSWORD, HttpPartFormat.UON, HttpPartFormat.NO_FORMAT); break; } case ARRAY: { notAllowed.addIf(properties != null, "properties") .addIf(additionalProperties != null, "additionalProperties") .addIf(exclusiveMaximum, "exclusiveMaximum") .addIf(exclusiveMinimum, "exclusiveMinimum") .addIf(pattern != null, "pattern") .addIf(maximum != null, "maximum") .addIf(minimum != null, "minimum") .addIf(multipleOf != null, "multipleOf") .addIf(maxLength != null, "maxLength") .addIf(minLength != null, "minLength") .addIf(maxProperties != null, "maxProperties") .addIf(minProperties != null, "minProperties"); invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON); break; } case BOOLEAN: { notAllowed.addIf(! _enum.isEmpty(), "_enum") .addIf(properties != null, "properties") .addIf(additionalProperties != null, "additionalProperties") .addIf(exclusiveMaximum, "exclusiveMaximum") .addIf(exclusiveMinimum, "exclusiveMinimum") .addIf(uniqueItems, "uniqueItems") .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat") .addIf(pattern != null, "pattern") .addIf(items != null, "items") .addIf(maximum != null, "maximum") .addIf(minimum != null, "minimum") .addIf(multipleOf != null, "multipleOf") .addIf(maxItems != null, "maxItems") .addIf(maxLength != null, "maxLength") .addIf(maxProperties != null, "maxProperties") .addIf(minItems != null, "minItems") .addIf(minLength != null, "minLength") .addIf(minProperties != null, "minProperties"); invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON); break; } case FILE: { break; } case INTEGER: { notAllowed.addIf(properties != null, "properties") .addIf(additionalProperties != null, "additionalProperties") .addIf(uniqueItems, "uniqueItems") .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat") .addIf(pattern != null, "pattern") .addIf(items != null, "items") .addIf(maxItems != null, "maxItems") .addIf(maxLength != null, "maxLength") .addIf(maxProperties != null, "maxProperties") .addIf(minItems != null, "minItems") .addIf(minLength != null, "minLength") .addIf(minProperties != null, "minProperties"); invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON, HttpPartFormat.INT32, HttpPartFormat.INT64); break; } case NUMBER: { notAllowed.addIf(properties != null, "properties") .addIf(additionalProperties != null, "additionalProperties") .addIf(uniqueItems, "uniqueItems") .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat") .addIf(pattern != null, "pattern") .addIf(items != null, "items") .addIf(maxItems != null, "maxItems") .addIf(maxLength != null, "maxLength") .addIf(maxProperties != null, "maxProperties") .addIf(minItems != null, "minItems") .addIf(minLength != null, "minLength") .addIf(minProperties != null, "minProperties"); invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON, HttpPartFormat.FLOAT, HttpPartFormat.DOUBLE); break; } case OBJECT: { notAllowed.addIf(exclusiveMaximum, "exclusiveMaximum") .addIf(exclusiveMinimum, "exclusiveMinimum") .addIf(uniqueItems, "uniqueItems") .addIf(pattern != null, "pattern") .addIf(items != null, "items") .addIf(maximum != null, "maximum") .addIf(minimum != null, "minimum") .addIf(multipleOf != null, "multipleOf") .addIf(maxItems != null, "maxItems") .addIf(maxLength != null, "maxLength") .addIf(minItems != null, "minItems") .addIf(minLength != null, "minLength"); invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT); break; } default: break; } List notAllowed2 = notAllowed.build(); if (! notAllowed2.isEmpty()) errors.add("Attributes not allow for type='"+type+"': " + StringUtils.join(notAllowed2, ",")); if (invalidFormat) errors.add("Invalid format for type='"+type+"': '"+format+"'"); if (exclusiveMaximum && maximum == null) errors.add("Cannot specify exclusiveMaximum with maximum."); if (exclusiveMinimum && minimum == null) errors.add("Cannot specify exclusiveMinimum with minimum."); if (required && _default != null) errors.add("Cannot specify a default value on a required value."); if (minLength != null && maxLength != null && maxLength < minLength) errors.add("maxLength cannot be less than minLength."); if (minimum != null && maximum != null && maximum.doubleValue() < minimum.doubleValue()) errors.add("maximum cannot be less than minimum."); if (minItems != null && maxItems != null && maxItems < minItems) errors.add("maxItems cannot be less than minItems."); if (minProperties != null && maxProperties != null && maxProperties < minProperties) errors.add("maxProperties cannot be less than minProperties."); if (minLength != null && minLength < 0) errors.add("minLength cannot be less than zero."); if (maxLength != null && maxLength < 0) errors.add("maxLength cannot be less than zero."); if (minItems != null && minItems < 0) errors.add("minItems cannot be less than zero."); if (maxItems != null && maxItems < 0) errors.add("maxItems cannot be less than zero."); if (minProperties != null && minProperties < 0) errors.add("minProperties cannot be less than zero."); if (maxProperties != null && maxProperties < 0) errors.add("maxProperties cannot be less than zero."); if (type == ARRAY && items != null && items.getType() == OBJECT && (format != UON && format != HttpPartFormat.NO_FORMAT)) errors.add("Cannot define an array of objects unless array format is 'uon'."); if (! errors.isEmpty()) throw new ContextRuntimeException("Schema specification errors: \n\t" + join(errors, "\n\t"), new Object[0]); } /** * Returns the default parsed type for this schema. * * @return The default parsed type for this schema. Never null. */ public ClassMeta getParsedType() { return parsedType; } /** * Returns the name of the object described by this schema, for example the query or form parameter name. * * @return The name, or null if not specified. * @see HttpPartSchema.Builder#name(String) */ public String getName() { return name; } /** * Returns the type field of this schema. * * @return The type field of this schema, or null if not specified. * @see HttpPartSchema.Builder#type(String) */ public HttpPartDataType getType() { return type; } /** * Returns the type field of this schema. * * @param cm * The class meta of the object. *
      Used to auto-detect the type if the type was not specified. * @return The format field of this schema, or null if not specified. * @see HttpPartSchema.Builder#format(String) */ public HttpPartDataType getType(ClassMeta cm) { if (type != HttpPartDataType.NO_TYPE) return type; if (cm.isTemporal() || cm.isDateOrCalendar()) return HttpPartDataType.STRING; if (cm.isNumber()) { if (cm.isDecimal()) return HttpPartDataType.NUMBER; return HttpPartDataType.INTEGER; } if (cm.isBoolean()) return HttpPartDataType.BOOLEAN; if (cm.isMapOrBean()) return HttpPartDataType.OBJECT; if (cm.isCollectionOrArray()) return HttpPartDataType.ARRAY; return HttpPartDataType.STRING; } /** * Returns the default field of this schema. * * @return The default value for this schema, or null if not specified. * @see HttpPartSchema.Builder#_default(String) */ public String getDefault() { return _default; } /** * Returns the collectionFormat field of this schema. * * @return The collectionFormat field of this schema, or null if not specified. * @see HttpPartSchema.Builder#collectionFormat(String) */ public HttpPartCollectionFormat getCollectionFormat() { return collectionFormat; } /** * Returns the format field of this schema. * * @see HttpPartSchema.Builder#format(String) * @return The format field of this schema, or null if not specified. */ public HttpPartFormat getFormat() { return format; } /** * Returns the format field of this schema. * * @param cm * The class meta of the object. *
      Used to auto-detect the format if the format was not specified. * @return The format field of this schema, or null if not specified. * @see HttpPartSchema.Builder#format(String) */ public HttpPartFormat getFormat(ClassMeta cm) { if (format != HttpPartFormat.NO_FORMAT) return format; if (cm.isNumber()) { if (cm.isDecimal()) { if (cm.isDouble()) return HttpPartFormat.DOUBLE; return HttpPartFormat.FLOAT; } if (cm.isLong()) return HttpPartFormat.INT64; return HttpPartFormat.INT32; } return format; } /** * Returns the maximum field of this schema. * * @return The schema for child items of the object represented by this schema, or null if not defined. * @see HttpPartSchema.Builder#items(HttpPartSchema.Builder) */ public HttpPartSchema getItems() { return items; } /** * Returns the maximum field of this schema. * * @return The maximum field of this schema, or null if not specified. * @see HttpPartSchema.Builder#maximum(Number) */ public Number getMaximum() { return maximum; } /** * Returns the minimum field of this schema. * * @return The minimum field of this schema, or null if not specified. * @see HttpPartSchema.Builder#minimum(Number) */ public Number getMinimum() { return minimum; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#multipleOf(Number) */ public Number getMultipleOf() { return multipleOf; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#pattern(String) */ public Pattern getPattern() { return pattern; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#maxLength(Long) */ public Long getMaxLength() { return maxLength; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#minLength(Long) */ public Long getMinLength() { return minLength; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#maxItems(Long) */ public Long getMaxItems() { return maxItems; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#minItems(Long) */ public Long getMinItems() { return minItems; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#maxProperties(Long) */ public Long getMaxProperties() { return maxProperties; } /** * Returns the xxx field of this schema. * * @return The xxx field of this schema, or null if not specified. * @see HttpPartSchema.Builder#minProperties(Long) */ public Long getMinProperties() { return minProperties; } /** * Returns the exclusiveMaximum field of this schema. * * @return The exclusiveMaximum field of this schema. * @see HttpPartSchema.Builder#exclusiveMaximum(Boolean) */ public boolean isExclusiveMaximum() { return exclusiveMaximum; } /** * Returns the exclusiveMinimum field of this schema. * * @return The exclusiveMinimum field of this schema. * @see HttpPartSchema.Builder#exclusiveMinimum(Boolean) */ public boolean isExclusiveMinimum() { return exclusiveMinimum; } /** * Returns the uniqueItems field of this schema. * * @return The uniqueItems field of this schema. * @see HttpPartSchema.Builder#uniqueItems(Boolean) */ public boolean isUniqueItems() { return uniqueItems; } /** * Returns the required field of this schema. * * @return The required field of this schema. * @see HttpPartSchema.Builder#required(Boolean) */ public boolean isRequired() { return required; } /** * Returns the skipIfEmpty field of this schema. * * @return The skipIfEmpty field of this schema. * @see HttpPartSchema.Builder#skipIfEmpty(Boolean) */ public boolean isSkipIfEmpty() { return skipIfEmpty; } /** * Returns the allowEmptyValue field of this schema. * * @return The skipIfEmpty field of this schema. * @see HttpPartSchema.Builder#skipIfEmpty(Boolean) */ public boolean isAllowEmptyValue() { return allowEmptyValue; } /** * Returns the enum field of this schema. * * @return The enum field of this schema, or null if not specified. * @see HttpPartSchema.Builder#_enum(Set) */ public Set getEnum() { return _enum; } /** * Returns the parser field of this schema. * * @return The parser field of this schema, or null if not specified. * @see HttpPartSchema.Builder#parser(Class) */ public Class getParser() { return parser; } /** * Returns the serializer field of this schema. * * @return The serializer field of this schema, or null if not specified. * @see HttpPartSchema.Builder#serializer(Class) */ public Class getSerializer() { return serializer; } /** * Throws a {@link ParseException} if the specified pre-parsed input does not validate against this schema. * * @param in The input. * @return The same object passed in. * @throws SchemaValidationException if the specified pre-parsed input does not validate against this schema. */ public String validateInput(String in) throws SchemaValidationException { if (! isValidRequired(in)) throw new SchemaValidationException("No value specified."); if (in != null) { if (! isValidAllowEmpty(in)) throw new SchemaValidationException("Empty value not allowed."); if (! isValidPattern(in)) throw new SchemaValidationException("Value does not match expected pattern. Must match pattern: {0}", pattern.pattern()); if (! isValidEnum(in)) throw new SchemaValidationException("Value does not match one of the expected values. Must be one of the following: {0}", cdl(_enum)); if (! isValidMaxLength(in)) throw new SchemaValidationException("Maximum length of value exceeded."); if (! isValidMinLength(in)) throw new SchemaValidationException("Minimum length of value not met."); } return in; } /** * Throws a {@link ParseException} if the specified parsed output does not validate against this schema. * * @param The return type. * @param o The parsed output. * @param bc The bean context used to detect POJO types. * @return The same object passed in. * @throws SchemaValidationException if the specified parsed output does not validate against this schema. */ public T validateOutput(T o, BeanContext bc) throws SchemaValidationException { if (o == null) { if (! isValidRequired(o)) throw new SchemaValidationException("Required value not provided."); return o; } ClassMeta cm = bc.getClassMetaForObject(o); switch (getType(cm)) { case ARRAY: { if (cm.isArray()) { if (! isValidMinItems(o)) throw new SchemaValidationException("Minimum number of items not met."); if (! isValidMaxItems(o)) throw new SchemaValidationException("Maximum number of items exceeded."); if (! isValidUniqueItems(o)) throw new SchemaValidationException("Duplicate items not allowed."); HttpPartSchema items = getItems(); if (items != null) for (int i = 0; i < Array.getLength(o); i++) items.validateOutput(Array.get(o, i), bc); } else if (cm.isCollection()) { Collection c = (Collection)o; if (! isValidMinItems(c)) throw new SchemaValidationException("Minimum number of items not met."); if (! isValidMaxItems(c)) throw new SchemaValidationException("Maximum number of items exceeded."); if (! isValidUniqueItems(c)) throw new SchemaValidationException("Duplicate items not allowed."); HttpPartSchema items = getItems(); if (items != null) c.forEach(x -> items.validateOutput(x, bc)); } break; } case INTEGER: { if (cm.isNumber()) { Number n = (Number)o; if (! isValidMinimum(n)) throw new SchemaValidationException("Minimum value not met."); if (! isValidMaximum(n)) throw new SchemaValidationException("Maximum value exceeded."); if (! isValidMultipleOf(n)) throw new SchemaValidationException("Multiple-of not met."); } break; } case NUMBER: { if (cm.isNumber()) { Number n = (Number)o; if (! isValidMinimum(n)) throw new SchemaValidationException("Minimum value not met."); if (! isValidMaximum(n)) throw new SchemaValidationException("Maximum value exceeded."); if (! isValidMultipleOf(n)) throw new SchemaValidationException("Multiple-of not met."); } break; } case OBJECT: { if (cm.isMapOrBean()) { Map m = cm.isMap() ? (Map)o : bc.toBeanMap(o); if (! isValidMinProperties(m)) throw new SchemaValidationException("Minimum number of properties not met."); if (! isValidMaxProperties(m)) throw new SchemaValidationException("Maximum number of properties exceeded."); m.forEach((k,v) -> { String key = k.toString(); HttpPartSchema s2 = getProperty(key); if (s2 != null) s2.validateOutput(v, bc); }); } else if (cm.isBean()) { } break; } case BOOLEAN: case FILE: case STRING: case NO_TYPE: break; } return o; } //----------------------------------------------------------------------------------------------------------------- // Helper methods. //----------------------------------------------------------------------------------------------------------------- private boolean isValidRequired(Object x) { return x != null || ! required; } private boolean isValidMinProperties(Map x) { return minProperties == null || x.size() >= minProperties; } private boolean isValidMaxProperties(Map x) { return maxProperties == null || x.size() <= maxProperties; } private boolean isValidMinimum(Number x) { if (x instanceof Integer || x instanceof AtomicInteger) return minimum == null || x.intValue() > minimum.intValue() || (x.intValue() == minimum.intValue() && (! exclusiveMinimum)); if (x instanceof Short || x instanceof Byte) return minimum == null || x.shortValue() > minimum.shortValue() || (x.intValue() == minimum.shortValue() && (! exclusiveMinimum)); if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger) return minimum == null || x.longValue() > minimum.longValue() || (x.intValue() == minimum.longValue() && (! exclusiveMinimum)); if (x instanceof Float) return minimum == null || x.floatValue() > minimum.floatValue() || (x.floatValue() == minimum.floatValue() && (! exclusiveMinimum)); if (x instanceof Double || x instanceof BigDecimal) return minimum == null || x.doubleValue() > minimum.doubleValue() || (x.doubleValue() == minimum.doubleValue() && (! exclusiveMinimum)); return true; } private boolean isValidMaximum(Number x) { if (x instanceof Integer || x instanceof AtomicInteger) return maximum == null || x.intValue() < maximum.intValue() || (x.intValue() == maximum.intValue() && (! exclusiveMaximum)); if (x instanceof Short || x instanceof Byte) return maximum == null || x.shortValue() < maximum.shortValue() || (x.intValue() == maximum.shortValue() && (! exclusiveMaximum)); if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger) return maximum == null || x.longValue() < maximum.longValue() || (x.intValue() == maximum.longValue() && (! exclusiveMaximum)); if (x instanceof Float) return maximum == null || x.floatValue() < maximum.floatValue() || (x.floatValue() == maximum.floatValue() && (! exclusiveMaximum)); if (x instanceof Double || x instanceof BigDecimal) return maximum == null || x.doubleValue() < maximum.doubleValue() || (x.doubleValue() == maximum.doubleValue() && (! exclusiveMaximum)); return true; } private boolean isValidMultipleOf(Number x) { if (x instanceof Integer || x instanceof AtomicInteger) return multipleOf == null || x.intValue() % multipleOf.intValue() == 0; if (x instanceof Short || x instanceof Byte) return multipleOf == null || x.shortValue() % multipleOf.shortValue() == 0; if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger) return multipleOf == null || x.longValue() % multipleOf.longValue() == 0; if (x instanceof Float) return multipleOf == null || x.floatValue() % multipleOf.floatValue() == 0; if (x instanceof Double || x instanceof BigDecimal) return multipleOf == null || x.doubleValue() % multipleOf.doubleValue() == 0; return true; } private boolean isValidAllowEmpty(String x) { return allowEmptyValue || isNotEmpty(x); } private boolean isValidPattern(String x) { return pattern == null || pattern.matcher(x).matches(); } private boolean isValidEnum(String x) { return _enum.isEmpty() || _enum.contains(x); } private boolean isValidMinLength(String x) { return minLength == null || x.length() >= minLength; } private boolean isValidMaxLength(String x) { return maxLength == null || x.length() <= maxLength; } private boolean isValidMinItems(Object x) { return minItems == null || Array.getLength(x) >= minItems; } private boolean isValidMaxItems(Object x) { return maxItems == null || Array.getLength(x) <= maxItems; } private boolean isValidUniqueItems(Object x) { if (uniqueItems) { Set s = new HashSet<>(); for (int i = 0; i < Array.getLength(x); i++) { Object o = Array.get(x, i); if (! s.add(o)) return false; } } return true; } private boolean isValidMinItems(Collection x) { return minItems == null || x.size() >= minItems; } private boolean isValidMaxItems(Collection x) { return maxItems == null || x.size() <= maxItems; } private boolean isValidUniqueItems(Collection x) { if (uniqueItems && ! (x instanceof Set)) { Set s = new HashSet<>(); for (Object o : x) if (! s.add(o)) return false; } return true; } /** * Returns the schema information for the specified property. * * @param name The property name. * @return The schema information for the specified property, or null if properties are not defined on this schema. */ public HttpPartSchema getProperty(String name) { if (properties != null) { HttpPartSchema schema = properties.get(name); if (schema != null) return schema; } return additionalProperties; } /** * Returns true if this schema has properties associated with it. * * @return true if this schema has properties associated with it. */ public boolean hasProperties() { return properties != null || additionalProperties != null; } private static Set copy(Set in) { return in == null ? emptySet() : unmodifiable(copyOf(in)); } private static Map build(Map in, boolean noValidate) { if (in == null) return null; Map m = map(); in.forEach((k,v) -> m.put(k, build(v, noValidate))); return unmodifiable(m); } private static HttpPartSchema build(Object in, boolean noValidate) { if (in == null) return null; if (in instanceof HttpPartSchema) return (HttpPartSchema)in; return ((Builder)in).noValidate(noValidate).build(); } //----------------------------------------------------------------------------------------------------------------- // Helper methods. //----------------------------------------------------------------------------------------------------------------- private boolean resolve(Boolean b) { return b == null ? false : b; } final static Set toSet(String[]...s) { boolean isNotEmpty = false; for (String[] ss : s) isNotEmpty |= ss.length > 0; if (! isNotEmpty) return null; Set set = set(); for (String[] ss : s) if (ss != null) for (String ss2 : ss) split(ss2, x -> set.add(x)); return set.isEmpty() ? null : set; } final static Set toSet(String s) { if (isEmpty(s)) return null; Set set = set(); try { JsonList.ofJsonOrCdl(s).forEach(x -> set.add(x.toString())); } catch (ParseException e) { throw asRuntimeException(e); } return set; } final static Number toNumber(String...s) { try { for (String ss : s) if (isNotEmpty(ss)) return parseNumber(ss, Number.class); return null; } catch (ParseException e) { throw asRuntimeException(e); } } final static JsonMap toJsonMap(String[] ss) { String s = joinnl(ss); if (s.isEmpty()) return null; if (! isJsonObject(s, true)) s = "{" + s + "}"; try { return JsonMap.ofJson(s); } catch (ParseException e) { throw asRuntimeException(e); } } @Override public String toString() { try { Predicate ne = x -> isNotEmpty(stringify(x)); Predicate nf = ObjectUtils::isTrue; Predicate nm1 = ObjectUtils::isNotMinusOne; Predicate nn = ObjectUtils::isNotNull; JsonMap m = new JsonMap() .appendIf(ne, "name", name) .appendIf(ne, "type", type) .appendIf(ne, "format", format) .appendIf(ne, "default", _default) .appendIf(ne, "enum", _enum) .appendIf(ne, "properties", properties) .appendIf(nf, "allowEmptyValue", allowEmptyValue) .appendIf(nf, "exclusiveMaximum", exclusiveMaximum) .appendIf(nf, "exclusiveMinimum", exclusiveMinimum) .appendIf(nf, "required", required) .appendIf(nf, "uniqueItems", uniqueItems) .appendIf(nf, "skipIfEmpty", skipIfEmpty) .appendIf(x -> x != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat", collectionFormat) .appendIf(ne, "pattern", pattern) .appendIf(nn, "items", items) .appendIf(nn, "additionalProperties", additionalProperties) .appendIf(nm1, "maximum", maximum) .appendIf(nm1, "minimum", minimum) .appendIf(nm1, "multipleOf", multipleOf) .appendIf(nm1, "maxLength", maxLength) .appendIf(nm1, "minLength", minLength) .appendIf(nm1, "maxItems", maxItems) .appendIf(nm1, "minItems", minItems) .appendIf(nm1, "maxProperties", maxProperties) .appendIf(nm1, "minProperties", minProperties) .append("parsedType", parsedType) ; return m.toString(); } catch (Exception e) { return ""; } } }