content: package io.muserver.openapi;
  
  import io.muserver.UploadedFile;
  
  import java.io.File;
  import java.io.InputStream;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.lang.reflect.WildcardType;
  import java.time.Instant;
  import java.time.LocalDate;
  import java.time.temporal.Temporal;
  import java.util.*;
  import java.util.regex.Pattern;
  
  import static io.muserver.openapi.OpenApiUtils.immutable;
  import static java.util.Arrays.asList;
  
  /**
   * <p>The Schema Object allows the definition of input and output data types. These types can be objects, but also
   * primitives and arrays. This object is an extended subset of the <a href="http://json-schema.org/">JSON Schema
   * Specification Wright Draft 00</a>.</p>
   * <p>For more information about the properties, see <a href="https://tools.ietf.org/html/draft-wright-json-schema-00">JSON
   * Schema Core</a> and <a href="https://tools.ietf.org/html/draft-wright-json-schema-validation-00" >JSON Schema Validation</a>.
   * Unless stated otherwise, the property definitions follow the JSON Schema.</p>
   */
  public class SchemaObjectBuilder {
      private String title;
      private Double multipleOf;
      private Double maximum;
      private Boolean exclusiveMaximum;
      private Double minimum;
      private Boolean exclusiveMinimum;
      private Integer maxLength;
      private Integer minLength;
      private Pattern pattern;
      private Integer maxItems;
      private Integer minItems;
      private Boolean uniqueItems;
      private Integer maxProperties;
      private Integer minProperties;
      private List<String> required;
      private List<Object> enumValue;
      private String type;
      private List<SchemaObject> allOf;
      private List<SchemaObject> oneOf;
      private List<SchemaObject> anyOf;
      private List<SchemaObject> not;
      private SchemaObject items;
      private Map<String, SchemaObject> properties;
      private Object additionalProperties;
      private String description;
      private String format;
      private Object defaultValue;
      private Boolean nullable;
      private DiscriminatorObject discriminator;
      private Boolean readOnly;
      private Boolean writeOnly;
      private XmlObject xml;
      private ExternalDocumentationObject externalDocs;
      private Object example;
      private Boolean deprecated;
  
      /**
       * @return the value set by {@link #withTitle}
       */
      public String title() {
          return title;
      }
  
      /**
       * @return the value set by {@link #withMultipleOf}
       */
      public Double multipleOf() {
          return multipleOf;
      }
  
      /**
       * @return the value set by {@link #withMaximum}
       */
      public Double maximum() {
          return maximum;
      }
  
      /**
       * @return the value set by {@link #withExclusiveMaximum}
       */
      public Boolean exclusiveMaximum() {
          return exclusiveMaximum;
      }
  
      /**
       * @return the value set by {@link #withMinimum}
       */
      public Double minimum() {
          return minimum;
      }
  
      /**
       * @return the value set by {@link #withExclusiveMinimum}
       */
      public Boolean exclusiveMinimum() {
          return exclusiveMinimum;
      }
  
      /**
       * @return the value set by {@link #withMaxLength}
       */
      public Integer maxLength() {
          return maxLength;
      }
  
      /**
       * @return the value set by {@link #withMinLength}
       */
      public Integer minLength() {
          return minLength;
      }
  
      /**
       * @return the value set by {@link #withPattern}
       */
      public Pattern pattern() {
          return pattern;
      }
  
      /**
       * @return the value set by {@link #withMaxItems}
       */
      public Integer maxItems() {
          return maxItems;
      }
  
      /**
       * @return the value set by {@link #withMinItems}
       */
      public Integer minItems() {
          return minItems;
      }
  
      /**
       * @return the value set by {@link #withUniqueItems}
       */
      public Boolean uniqueItems() {
          return uniqueItems;
      }
  
      /**
       * @return the value set by {@link #withMaxProperties}
       */
      public Integer maxProperties() {
          return maxProperties;
      }
  
      /**
       * @return the value set by {@link #withMinProperties}
       */
      public Integer minProperties() {
          return minProperties;
      }
  
      /**
       * @return the value set by {@link #withRequired}
       */
      public List<String> required() {
          return required;
      }
  
      /**
       * @return the value set by {@link #withEnumValue}
       */
      public List<Object> enumValue() {
          return enumValue;
      }
  
      /**
       * @return the value set by {@link #withType}
       */
      public String type() {
          return type;
      }
  
      /**
       * @return the value set by {@link #withAllOf}
       */
      public List<SchemaObject> allOf() {
          return allOf;
      }
  
      /**
       * @return the value set by {@link #withOneOf}
       */
      public List<SchemaObject> oneOf() {
          return oneOf;
      }
  
      /**
       * @return the value set by {@link #withAnyOf}
       */
      public List<SchemaObject> anyOf() {
          return anyOf;
      }
  
      /**
       * @return the value set by {@link #withNot}
       */
      public List<SchemaObject> not() {
          return not;
      }
  
      /**
       * @return the value set by {@link #withItems}
       */
      public SchemaObject items() {
          return items;
      }
  
      /**
       * @return the value set by {@link #withProperties}
       */
      public Map<String, SchemaObject> properties() {
          return properties;
      }
  
      /**
       * @return the value set by {@link #withAdditionalProperties}
       */
      public Object additionalProperties() {
          return additionalProperties;
      }
  
      /**
       * @return the value set by {@link #withDescription}
       */
      public String description() {
          return description;
      }
  
      /**
       * @return the value set by {@link #withFormat}
       */
      public String format() {
          return format;
      }
  
      /**
       * @return the value set by {@link #withDefaultValue}
       */
      public Object defaultValue() {
          return defaultValue;
      }
  
      /**
       * @return the value set by {@link #withNullable}
       */
      public Boolean nullable() {
          return nullable;
      }
  
      /**
       * @return the value set by {@link #withDiscriminator}
       */
      public DiscriminatorObject discriminator() {
          return discriminator;
      }
  
      /**
       * @return the value set by {@link #withReadOnly}
       */
      public Boolean readOnly() {
          return readOnly;
      }
  
      /**
       * @return the value set by {@link #withWriteOnly}
       */
      public Boolean writeOnly() {
          return writeOnly;
      }
  
      /**
       * @return the value set by {@link #withXml}
       */
      public XmlObject xml() {
          return xml;
      }
  
      /**
       * @return the value set by {@link #withExternalDocs}
       */
      public ExternalDocumentationObject externalDocs() {
          return externalDocs;
      }
  
      /**
       * @return the value set by {@link #withExample}
       */
      public Object example() {
          return example;
      }
  
      /**
       * @return the value set by {@link #withDeprecated}
       */
      public Boolean deprecated() {
          return deprecated;
      }
  
      /**
       * @param title the name of this object type
       * @return this builder
       */
      public SchemaObjectBuilder withTitle(String title) {
          this.title = title;
          return this;
      }
  
      /**
       * Restricts numeric values to be a multiple of the given value
       * @param multipleOf the multiple
       * @return this builder
       */
      public SchemaObjectBuilder withMultipleOf(Double multipleOf) {
          this.multipleOf = multipleOf;
          return this;
      }
  
      /**
       * @param maximum The maximum allowed value for numeric values
       * @return this builder
       * @see #withExclusiveMaximum(Boolean)
       */
      public SchemaObjectBuilder withMaximum(Double maximum) {
          this.maximum = maximum;
          return this;
      }
  
      /**
       * @param exclusiveMaximum <code>true</code> if the value specified with {@link #withMaximum(Double)} is exclusive;
       *                         otherwise the default <code>false</code> means it is an inclusive number.
       * @return this builder
       * @see #withMaximum(Double)
       */
      public SchemaObjectBuilder withExclusiveMaximum(Boolean exclusiveMaximum) {
          this.exclusiveMaximum = exclusiveMaximum;
          return this;
      }
  
      /**
       * @param minimum The minimum allowed value for numeric values
       * @return this builder
       * @see #withExclusiveMinimum(Boolean)
       */
      public SchemaObjectBuilder withMinimum(Double minimum) {
          this.minimum = minimum;
          return this;
      }
  
      /**
       * @param exclusiveMinimum <code>true</code> if the value specified with {@link #withMinimum(Double)} is exclusive;
       *                         otherwise the default <code>false</code> means it is an inclusive number.
       * @return this builder
       * @see #withMinimum(Double)
       */
      public SchemaObjectBuilder withExclusiveMinimum(Boolean exclusiveMinimum) {
          this.exclusiveMinimum = exclusiveMinimum;
          return this;
      }
  
      /**
       * @param maxLength the maximum allowed length of string values
       * @return this builder
       */
      public SchemaObjectBuilder withMaxLength(Integer maxLength) {
          this.maxLength = maxLength;
          return this;
      }
  
      /**
       * @param minLength the minimum allowed length of string values
       * @return this builder
       */
      public SchemaObjectBuilder withMinLength(Integer minLength) {
          this.minLength = minLength;
          return this;
      }
  
      /**
       * @param pattern a regular expression that string values must match against
       * @return this builder
       */
      public SchemaObjectBuilder withPattern(Pattern pattern) {
          this.pattern = pattern;
          return this;
      }
  
      /**
       * @param maxItems the maximum number of items allowed in an array value
       * @return this builder
       */
      public SchemaObjectBuilder withMaxItems(Integer maxItems) {
          this.maxItems = maxItems;
          return this;
      }
  
      /**
       * @param minItems the minimum number of items allowed in an array value
       * @return this builder
       */
      public SchemaObjectBuilder withMinItems(Integer minItems) {
          this.minItems = minItems;
          return this;
      }
  
      /**
       * @param uniqueItems if true, then all items in an array value must be unique
       * @return this builder
       */
      public SchemaObjectBuilder withUniqueItems(Boolean uniqueItems) {
          this.uniqueItems = uniqueItems;
          return this;
      }
  
      /**
       * @param maxProperties the maximum number of properties allowed for an &quot;object&quot; type.
       * @return this builder
       */
      public SchemaObjectBuilder withMaxProperties(Integer maxProperties) {
          this.maxProperties = maxProperties;
          return this;
      }
  
      /**
       * @param minProperties the minimum number of properties allowed for an &quot;object&quot; type.
       * @return this builder
       */
      public SchemaObjectBuilder withMinProperties(Integer minProperties) {
          this.minProperties = minProperties;
          return this;
      }
  
      /**
       * @param required the list of properties that are required to have a value for an &quot;object&quot; type.
       * @return this builder
       */
      public SchemaObjectBuilder withRequired(List<String> required) {
          this.required = required;
          return this;
      }
  
      /**
       * @param enumValue the allowed values for an &quot;enum&quot; type
       * @return this builder
       */
      public SchemaObjectBuilder withEnumValue(List<Object> enumValue) {
          this.enumValue = enumValue;
          return this;
      }
  
      /**
       * @param type the type of this schema object. One of <code>string</code>, <code>number</code>, <code>integer</code>, <code>boolean</code>, <code>array</code> or <code>object</code>
       * @return this builder
       */
      public SchemaObjectBuilder withType(String type) {
          this.type = type;
          return this;
      }
  
      /**
       * @param allOf the schemas that the value must match
       * @return this builder
       */
      public SchemaObjectBuilder withAllOf(List<SchemaObject> allOf) {
          this.allOf = allOf;
          return this;
      }
  
      /**
       * Forces a value to be one of several different schemas
       * @param oneOf the schemas the validate against
       * @return this builder
       * @see #withAnyOf(List)
       */
      public SchemaObjectBuilder withOneOf(List<SchemaObject> oneOf) {
          this.oneOf = oneOf;
          return this;
      }
  
      /**
       * Forces a value to be any of a number of different schemas
       * @param anyOf the schemas the validate against
       * @return this builder
       * @see #withOneOf(List)
       */
      public SchemaObjectBuilder withAnyOf(List<SchemaObject> anyOf) {
          this.anyOf = anyOf;
          return this;
      }
  
      /**
       * @param not schemas the value must not validate against
       * @return this builder
       */
      public SchemaObjectBuilder withNot(List<SchemaObject> not) {
          this.not = not;
          return this;
      }
  
      /**
       * @param items the schema that items in an array object must validate against
       * @return this builder
       */
      public SchemaObjectBuilder withItems(SchemaObject items) {
          this.items = items;
          return this;
      }
  
      /**
       * @param properties the schema objects of each property for an <code>object</code> type
       * @return this builder
       */
      public SchemaObjectBuilder withProperties(Map<String, SchemaObject> properties) {
          this.properties = properties;
          return this;
      }
  
      /**
       * Defines how properties not covered by {@link #withProperties(Map)} are handled when the
       * type is <code>object</code>
       * @param additionalProperties If <code>false</code> then extra properties are not allowed.
       *                             If it is a schema object then any extra properties must validate
       *                             against this schema.
       * @return this builder
       */
      public SchemaObjectBuilder withAdditionalProperties(Object additionalProperties) {
          this.additionalProperties = additionalProperties;
          return this;
      }
  
      /**
       * @param description a description of this type
       * @return this builder
       * @see #withTitle(String)
       */
      public SchemaObjectBuilder withDescription(String description) {
          this.description = description;
          return this;
      }
  
      /**
       * This is used to further specify the format of <code>string</code> types.
       * <table>
       *     <caption>Example type/format combos</caption>
       *     <thead>
       *         <tr>
       *             <th>Type</th>
       *             <th>Format</th>
       *             <th>Description</th>
       *         </tr>
       *     </thead>
       *     <tbody>
       *         <tr>
       *             <td>number</td>
       *             <td></td>
       *             <td>Any numbers.</td>
       *         </tr>
       *         <tr>
       *             <td>number</td>
       *             <td>float</td>
       *             <td>Floating-point numbers.</td>
       *         </tr>
       *         <tr>
       *             <td>number</td>
       *             <td>double</td>
       *             <td>Floating-point numbers with double precision.</td>
       *         </tr>
       *         <tr>
       *             <td>integer</td>
       *             <td></td>
       *             <td>Integer numbers.</td>
       *         </tr>
       *         <tr>
       *             <td>integer</td>
       *             <td>in32</td>
       *             <td>Signed 32-bit integers (commonly used integer type).</td>
       *         </tr>
       *         <tr>
       *             <td>integer</td>
       *             <td>int64</td>
       *             <td>Signed 64-bit integers (long type).</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>date</td>
       *             <td>full-date notation as defined by RFC 3339, section 5.6, for example, <code>2021-02-12</code></td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>date-time</td>
       *             <td>the date-time notation as defined by RFC 3339, section 5.6, for example, <code>2021-02-12T15:33:28Z</code></td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>password</td>
       *             <td>a hint to UIs to mask the input</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>byte</td>
       *             <td>base64-encoded characters, for example, <code>U3dhZ2dlciByb2Nrcw==</code></td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>binary</td>
       *             <td>binary data, used to describe files (not text)</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>email</td>
       *             <td>email addresses</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>uuid</td>
       *             <td>UUIDs such as <code>93d35de9-0083-4765-8b60-822258e8ffad</code></td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>uri</td>
       *             <td>URIs, for example <code>https://muserver.io/</code></td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>hostname</td>
       *             <td>A server hostname</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>ipv4</td>
       *             <td>An IP4 address</td>
       *         </tr>
       *         <tr>
       *             <td>string</td>
       *             <td>ipv6</td>
       *             <td>An IP6 address</td>
       *         </tr>
       *     </tbody>
       * </table>
       * <p>Custom formats may be specified too.</p>
       * @param format the format of the type specified by {@link #withType(String)}
       * @return this builder
       */
      public SchemaObjectBuilder withFormat(String format) {
          this.format = format;
          return this;
      }
  
      /**
       * @param defaultValue The default value to use when none is specified
       * @return this builder
       */
      public SchemaObjectBuilder withDefaultValue(Object defaultValue) {
          this.defaultValue = defaultValue;
          return this;
      }
  
      /**
       * @param nullable A <code>true</code> value adds <code>&quot;null&quot;</code> to the allowed type specified by the
       *                 <code>type</code> keyword, only if <code>type</code> is explicitly defined within the same Schema
       *                 Object. Other Schema Object constraints retain their defined behavior, and therefore may disallow
       *                 the use of <code>null</code> as a value. A <code>false</code> value leaves the specified or default
       *                 <code>type</code> unmodified. The default value is <code>false</code>.
       * @return The current builder
       */
      public SchemaObjectBuilder withNullable(Boolean nullable) {
          this.nullable = nullable;
          return this;
      }
  
      /**
       * @param discriminator Adds support for polymorphism. The discriminator is an object name that is used to differentiate between other schemas which may satisfy the payload description.
       * @return The current builder
       */
      public SchemaObjectBuilder withDiscriminator(DiscriminatorObject discriminator) {
          this.discriminator = discriminator;
          return this;
      }
  
      /**
       * @param readOnly Relevant only for Schema <code>"properties"</code> definitions. Declares the property as "read only".
       *                 This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request.
       *                 If the property is marked as <code>readOnly</code> being <code>true</code> and is in the <code>required</code>
       *                 list, the <code>required</code> will take effect on the response only. A property MUST NOT be marked
       *                 as both <code>readOnly</code> and <code>writeOnly</code> being <code>true</code>. Default value is <code>false</code>.
       * @return The current builder
       */
      public SchemaObjectBuilder withReadOnly(Boolean readOnly) {
          this.readOnly = readOnly;
          return this;
      }
  
      /**
       * @param writeOnly Relevant only for Schema <code>"properties"</code> definitions. Declares the property as "write only".
       *                  Therefore, it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. If
       *                  the property is marked as <code>writeOnly</code> being <code>true</code> and is in the
       *                  <code>required</code> list, the <code>required</code> will take effect on the request only. A property
       *                  MUST NOT be marked as both <code>readOnly</code> and <code>writeOnly</code> being <code>true</code>.
       *                  Default value is <code>false</code>.
       * @return The current builder
       */
      public SchemaObjectBuilder withWriteOnly(Boolean writeOnly) {
          this.writeOnly = writeOnly;
          return this;
      }
  
      /**
       * @param xml This MAY be used only on properties schemas. It has no effect on root schemas. Adds additional metadata
       *            to describe the XML representation of this property.
       * @return The current builder
       */
      public SchemaObjectBuilder withXml(XmlObject xml) {
          this.xml = xml;
          return this;
      }
  
      /**
       * @param externalDocs Additional external documentation for this schema.
       * @return The current builder
       */
      public SchemaObjectBuilder withExternalDocs(ExternalDocumentationObject externalDocs) {
          this.externalDocs = externalDocs;
          return this;
      }
  
      /**
       * @param example A free-form property to include an example of an instance for this schema. To represent examples
       *                that cannot be naturally represented in JSON or YAML, a string value can be used to contain the
       *                example with escaping where necessary.
       * @return The current builder
       */
      public SchemaObjectBuilder withExample(Object example) {
          this.example = example;
          return this;
      }
  
      /**
       * @param deprecated Specifies that a schema is deprecated and SHOULD be transitioned out of usage. Default value is <code>false</code>.
       * @return The current builder
       */
      public SchemaObjectBuilder withDeprecated(Boolean deprecated) {
          this.deprecated = deprecated;
          return this;
      }
  
      /**
       * @return A new object
       */
      public SchemaObject build() {
          return new SchemaObject(title, multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, maxLength,
              minLength, pattern, maxItems, minItems, uniqueItems, maxProperties, minProperties, immutable(required),
              immutable(enumValue), type, immutable(allOf), immutable(oneOf), immutable(anyOf), immutable(not),
              items, immutable(properties), additionalProperties, description, format, defaultValue, nullable,
              discriminator, readOnly, writeOnly, xml, externalDocs, example, deprecated);
      }
  
      /**
       * Creates a builder for a {@link SchemaObject}
       *
       * @return A new builder
       */
      public static SchemaObjectBuilder schemaObject() {
          return new SchemaObjectBuilder();
      }
  
      /**
       * Creates a builder for a {@link SchemaObject} with the type and format based on the given class
       * @param from Type type to build from, e.g. if the type is <code>String.class</code> then the <code>type</code> will
       *             be set as <code>string</code>.
       * @return A new builder
       */
      public static SchemaObjectBuilder schemaObjectFrom(Class<?> from) {
          return schemaObjectFrom(from, null, false);
      }
  
      /**
       * Creates a builder for a {@link SchemaObject} with the type and format based on the given class and generic type.
       * @param from Type type to build from, e.g. if the type is <code>List.class</code> then the <code>type</code> will
       *             be set as <code>array</code>.
       * @param parameterizedType The generic type of the class, e.g. a String if the type is <code>List&lt;String&gt;</code>
       * @param required True if it's a required value
       * @return A new builder
       */
      public static SchemaObjectBuilder schemaObjectFrom(Class<?> from, Type parameterizedType, boolean required) {
          Objects.requireNonNull(from, "from");
          if (from.equals(void.class) || from.equals(Void.class)) {
              return schemaObject();
          }
          parameterizedType = getUpperBound(parameterizedType);
          String jsonType = jsonType(from);
          SchemaObjectBuilder schemaObjectBuilder = schemaObject()
              .withType(jsonType)
              .withFormat(jsonFormat(from))
              .withExample(example(from))
              .withNullable((!from.isPrimitive() && !required) ? true : null)
              .withItems(itemsFor(from, parameterizedType, "array".equals(jsonType)));
          if (from.equals(UUID.class)) {
              schemaObjectBuilder
                  .withPattern(Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]"));
          } else if (from.isEnum()) {
              Object[] enumConstants = from.getEnumConstants();
              schemaObjectBuilder.withEnumValue(asList(enumConstants));
          }
          return schemaObjectBuilder;
      }
  
      private static Object example(Class<?> clazz) {
          if (clazz.equals(UUID.class)) {
              return UUID.randomUUID();
          } else if (Temporal.class.isAssignableFrom(clazz)) {
              try {
                  return clazz.getDeclaredMethod("now").invoke(null);
              } catch (Exception ignored) { }
          }
          return null;
      }
  
      private static Type getUpperBound(Type parameterizedType) {
          if (parameterizedType instanceof WildcardType && ((WildcardType)parameterizedType).getUpperBounds().length > 0) {
              parameterizedType = ((WildcardType)parameterizedType).getUpperBounds()[0];
          }
          return parameterizedType;
      }
  
      private static SchemaObject itemsFor(Class<?> from, Type parameterizedType, boolean isJsonArray) {
          Class<?> componentType = from.getComponentType();
          if (componentType == null) {
              if (isJsonArray) {
                  SchemaObjectBuilder schemaObjectBuilder = schemaObject().withType("object");
                  if (parameterizedType instanceof ParameterizedType) {
                      Type[] actualTypeArguments = ((ParameterizedType) parameterizedType).getActualTypeArguments();
                      if (actualTypeArguments.length == 1) {
                          Type argType = getUpperBound(actualTypeArguments[0]);
                          if (argType instanceof Class<?>) {
                              Class<?> argClass = (Class<?>) argType;
                              schemaObjectBuilder = schemaObjectFrom(argClass, null, true);
                          }
                      }
                  }
                  return schemaObjectBuilder.build();
              } else {
                  return null;
              }
          }
          return schemaObjectFrom(componentType).build();
      }
  
      private static String jsonType(Class<?> type) {
          if (CharSequence.class.isAssignableFrom(type) || type.equals(byte.class) || type.equals(Byte.class)
              || type.isAssignableFrom(Date.class) || Temporal.class.isAssignableFrom(type) || isBinaryClass(type)
              || type.isAssignableFrom(UUID.class) || type.isEnum()) {
              return "string";
          } else if (type.equals(boolean.class) || type.equals(Boolean.class)) {
              return "boolean";
          } else if (type.equals(int.class) || type.equals(Integer.class) || type.equals(long.class) || type.equals(Long.class)) {
              return "integer";
          } else if (Number.class.isAssignableFrom(type) || type.equals(float.class) || type.equals(double.class)) {
              return "number";
          } else if (Collection.class.isAssignableFrom(type) || type.isArray()) {
              return "array";
          }
          return "object";
      }
  
      private static String jsonFormat(Class<?> type) {
          if (type.equals(int.class) || type.equals(Integer.class)) {
              return "int32";
          } else if (type.equals(long.class) || type.equals(Long.class)) {
              return "int64";
          } else if (type.equals(float.class) || type.equals(Float.class)) {
              return "float";
          } else if (type.equals(double.class) || type.equals(Double.class)) {
              return "double";
          } else if (type.equals(byte.class) || type.equals(Byte.class)) {
              return "byte";
          } else if (type.equals(Date.class) || type.equals(Instant.class)) {
              return "date-time";
          } else if (type.equals(LocalDate.class)) {
              return "date";
          } else if (isBinaryClass(type)) {
              return "binary";
          } else if (type.equals(UUID.class)) {
              return "uuid";
          }
          return null;
      }
  
      private static boolean isBinaryClass(Class<?> type) {
          return UploadedFile.class.isAssignableFrom(type) || File.class.isAssignableFrom(type)
              || InputStream.class.isAssignableFrom(type) || (type.isArray() && type.getComponentType().equals(byte.class));
      }
  
      @Override
      public String toString() {
          return "SchemaObjectBuilder{" +
              "title='" + title + '\'' +
              ", multipleOf=" + multipleOf +
              ", maximum=" + maximum +
              ", exclusiveMaximum=" + exclusiveMaximum +
              ", minimum=" + minimum +
              ", exclusiveMinimum=" + exclusiveMinimum +
              ", maxLength=" + maxLength +
              ", minLength=" + minLength +
              ", pattern=" + pattern +
              ", maxItems=" + maxItems +
              ", minItems=" + minItems +
              ", uniqueItems=" + uniqueItems +
              ", maxProperties=" + maxProperties +
              ", minProperties=" + minProperties +
              ", required=" + required +
              ", enumValue=" + enumValue +
              ", type='" + type + '\'' +
              ", allOf=" + allOf +
              ", oneOf=" + oneOf +
              ", anyOf=" + anyOf +
              ", not=" + not +
              ", items=" + items +
              ", properties=" + properties +
              ", additionalProperties=" + additionalProperties +
              ", description='" + description + '\'' +
              ", format='" + format + '\'' +
              ", defaultValue=" + defaultValue +
              ", nullable=" + nullable +
              ", discriminator=" + discriminator +
              ", readOnly=" + readOnly +
              ", writeOnly=" + writeOnly +
              ", xml=" + xml +
              ", externalDocs=" + externalDocs +
              ", example=" + example +
              ", deprecated=" + deprecated +
              '}';
      }
  
  }