AnnotationsUtils.java

package io.swagger.v3.core.util;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.Annotated;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.annotations.StringToClassMapItem;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.links.LinkParameter;
import io.swagger.v3.oas.annotations.media.DependentRequired;
import io.swagger.v3.oas.annotations.media.DependentSchema;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.SchemaProperty;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.Encoding;
import io.swagger.v3.oas.models.media.JsonSchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.servers.ServerVariable;
import io.swagger.v3.oas.models.servers.ServerVariables;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public abstract class AnnotationsUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationsUtils.class);
    public static final String COMPONENTS_REF = Components.COMPONENTS_SCHEMAS_REF;

    public static boolean hasSchemaAnnotation(io.swagger.v3.oas.annotations.media.Schema schema) {
        if (schema == null) {
            return false;
        }
        if (StringUtils.isBlank(schema.type())
                && StringUtils.isBlank(schema.format())
                && StringUtils.isBlank(schema.title())
                && StringUtils.isBlank(schema.description())
                && StringUtils.isBlank(schema.ref())
                && StringUtils.isBlank(schema.name())
                && schema.multipleOf() == 0
                && StringUtils.isBlank(schema.maximum())
                && StringUtils.isBlank(schema.minimum())
                && !schema.exclusiveMinimum()
                && !schema.exclusiveMaximum()
                && schema.maxLength() == Integer.MAX_VALUE
                && schema.minLength() == 0
                && schema.minProperties() == 0
                && schema.maxProperties() == 0
                && schema.requiredProperties().length == 0
                && !schema.required()
                && schema.requiredMode().equals(io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO)
                && !schema.nullable()
                && !schema.readOnly()
                && !schema.writeOnly()
                && schema.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.AUTO)
                && !schema.deprecated()
                && schema.allowableValues().length == 0
                && StringUtils.isBlank(schema.defaultValue())
                && schema.implementation().equals(Void.class)
                && StringUtils.isBlank(schema.example())
                && StringUtils.isBlank(schema.pattern())
                && schema.not().equals(Void.class)
                && schema.allOf().length == 0
                && schema.oneOf().length == 0
                && schema.anyOf().length == 0
                && schema.subTypes().length == 0
                && !getExternalDocumentation(schema.externalDocs()).isPresent()
                && StringUtils.isBlank(schema.discriminatorProperty())
                && schema.discriminatorMapping().length == 0
                && schema.extensions().length == 0
                && !schema.hidden()
                && !schema.enumAsRef()
                && schema.dependentSchemas().length == 0
                && schema.patternProperties().length == 0
                && schema.unevaluatedProperties().equals(Void.class)
                && schema.types().length == 0
                && schema.exclusiveMinimumValue() == 0
                && schema.exclusiveMaximumValue() == 0
                && StringUtils.isBlank(schema.$id())
                && StringUtils.isBlank(schema.$schema())
                && StringUtils.isBlank(schema.$anchor())
                && StringUtils.isBlank(schema.contentEncoding())
                && StringUtils.isBlank(schema.contentMediaType())
                && schema.contentSchema().equals(Void.class)
                && schema.propertyNames().equals(Void.class)
                && schema._if().equals(Void.class)
                && schema._else().equals(Void.class)
                && schema.then().equals(Void.class)
                && StringUtils.isBlank(schema.$comment())
                && schema.dependentRequiredMap().length == 0
                && schema.patternProperties().length == 0
                && schema.properties().length == 0
                && StringUtils.isBlank(schema._const())
                && schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION)
                && schema.additionalPropertiesSchema().equals(Void.class)
                && schema.examples().length == 0
        ) {
            return false;
        }
        return true;
    }

    public static boolean equals(Annotation thisAnnotation, Annotation thatAnnotation) {
        if (thisAnnotation == null && thatAnnotation == null) {
            return true;
        } else if (thisAnnotation == null || thatAnnotation == null) {
            return false;
        }
        if (!thisAnnotation.annotationType().equals(thatAnnotation.annotationType())) {
            return false;
        }
        if (thisAnnotation instanceof io.swagger.v3.oas.annotations.media.Schema) {
            return equals((io.swagger.v3.oas.annotations.media.Schema) thisAnnotation, (io.swagger.v3.oas.annotations.media.Schema) thatAnnotation);
        } else if (thisAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema) {
            return equals((io.swagger.v3.oas.annotations.media.ArraySchema) thisAnnotation, (io.swagger.v3.oas.annotations.media.ArraySchema) thatAnnotation);
        }
        return true;
    }

    public static boolean equals(io.swagger.v3.oas.annotations.media.ArraySchema thisArraySchema, io.swagger.v3.oas.annotations.media.ArraySchema thatArraySchema) {
        if (thisArraySchema == null && thatArraySchema == null) {
            return true;
        } else if (thisArraySchema == null || thatArraySchema == null) {
            return false;
        }

        if (thisArraySchema.maxItems() != thatArraySchema.maxItems()) {
            return false;
        }
        if (thisArraySchema.minItems() != thatArraySchema.minItems()) {
            return false;
        }
        if (thisArraySchema.uniqueItems() != thatArraySchema.uniqueItems()) {
            return false;
        }

        if (!Arrays.equals(thisArraySchema.extensions(), thatArraySchema.extensions())) {
            return false;
        }

        if (!equals(thisArraySchema.schema(), thatArraySchema.schema())) {
            return false;
        }

        if (!equals(thisArraySchema.contains(), thatArraySchema.contains())) {
            return false;
        }
        if (thisArraySchema.maxContains() != thatArraySchema.maxContains()) {
            return false;
        }
        if (thisArraySchema.minContains() != thatArraySchema.minContains()) {
            return false;
        }
        if (!Arrays.equals(thisArraySchema.prefixItems(), thatArraySchema.prefixItems())) {
            return false;
        }
        return true;
    }

    public static boolean equals(io.swagger.v3.oas.annotations.media.Schema thisSchema, io.swagger.v3.oas.annotations.media.Schema thatSchema) {
        if (thisSchema == null && thatSchema == null) {
            return true;
        } else if (thisSchema == null || thatSchema == null) {
            return false;
        }

        if (!StringUtils.equals(thisSchema.type(), thatSchema.type())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.format(), thatSchema.format())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.title(), thatSchema.title())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.description(), thatSchema.description())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.ref(), thatSchema.ref())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.name(), thatSchema.name())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.defaultValue(), thatSchema.defaultValue())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.maximum(), thatSchema.maximum())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.minimum(), thatSchema.minimum())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.example(), thatSchema.example())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.pattern(), thatSchema.pattern())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.discriminatorProperty(), thatSchema.discriminatorProperty())) {
            return false;
        }

        if (thisSchema.multipleOf() != thatSchema.multipleOf()) {
            return false;
        }
        if (thisSchema.minLength() != thatSchema.minLength()) {
            return false;
        }
        if (thisSchema.minProperties() != thatSchema.minProperties()) {
            return false;
        }
        if (thisSchema.maxProperties() != thatSchema.maxProperties()) {
            return false;
        }
        if (thisSchema.maxLength() != thatSchema.maxLength()) {
            return false;
        }

        if (!Arrays.equals(thisSchema.allOf(), thatSchema.allOf())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.oneOf(), thatSchema.oneOf())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.anyOf(), thatSchema.anyOf())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.subTypes(), thatSchema.subTypes())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.discriminatorMapping(), thatSchema.discriminatorMapping())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.extensions(), thatSchema.extensions())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.allowableValues(), thatSchema.allowableValues())) {
            return false;
        }
        if (!Arrays.equals(thisSchema.requiredProperties(), thatSchema.requiredProperties())) {
            return false;
        }

        if (thisSchema.exclusiveMinimum() != thatSchema.exclusiveMinimum()) {
            return false;
        }
        if (thisSchema.exclusiveMaximum() != thatSchema.exclusiveMaximum()) {
            return false;
        }
        if (thisSchema.required() != thatSchema.required()) {
            return false;
        }
        if (!thisSchema.requiredMode().equals(thatSchema.requiredMode())) {
            return false;
        }
        if (thisSchema.nullable() != thatSchema.nullable()) {
            return false;
        }
        if (thisSchema.readOnly() != thatSchema.readOnly()) {
            return false;
        }
        if (thisSchema.writeOnly() != thatSchema.writeOnly()) {
            return false;
        }
        if (!thisSchema.accessMode().equals(thatSchema.accessMode())) {
            return false;
        }
        if (thisSchema.deprecated() != thatSchema.deprecated()) {
            return false;
        }
        if (thisSchema.hidden() != thatSchema.hidden()) {
            return false;
        }
        if (thisSchema.enumAsRef() != thatSchema.enumAsRef()) {
            return false;
        }
        if (!thisSchema.implementation().equals(thatSchema.implementation())) {
            return false;
        }
        if (!thisSchema.not().equals(thatSchema.not())) {
            return false;
        }

        if (!StringUtils.equals(thisSchema.externalDocs().description(), thatSchema.externalDocs().description())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.externalDocs().url(), thatSchema.externalDocs().url())) {
            return false;
        }
        if (thisSchema.externalDocs().extensions().length != thatSchema.externalDocs().extensions().length) {
            return false;
        }
        if (!Arrays.equals(thisSchema.extensions(), thatSchema.extensions())) {
            return false;
        }
        if (!thisSchema.additionalProperties().equals(thatSchema.additionalProperties())) {
            return false;
        }

        if (!Arrays.equals(thisSchema.types(), thatSchema.types())) {
            return false;
        }
        if (thisSchema.exclusiveMaximumValue() != thatSchema.exclusiveMaximumValue()) {
            return false;
        }
        if (thisSchema.exclusiveMinimumValue() != thatSchema.exclusiveMinimumValue()) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.$id(), thatSchema.$id())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.$schema(), thatSchema.$schema())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.$anchor(), thatSchema.$anchor())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.contentEncoding(), thatSchema.contentEncoding())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.contentMediaType(), thatSchema.contentMediaType())) {
            return false;
        }
        if (!StringUtils.equals(thisSchema.contentMediaType(), thatSchema.contentMediaType())) {
            return false;
        }
        if (!thisSchema.contentSchema().equals(thatSchema.contentSchema())) {
            return false;
        }
        if (!thisSchema.propertyNames().equals(thatSchema.propertyNames())) {
            return false;
        }
        if (!thisSchema._if().equals(thatSchema._if())) {
            return false;
        }
        if (!thisSchema._else().equals(thatSchema._else())) {
            return false;
        }
        if (!thisSchema.then().equals(thatSchema.then())) {
            return false;
        }
        if (!thisSchema.$comment().equals(thatSchema.$comment())) {
            return false;
        }
        if (!thisSchema._const().equals(thatSchema._const())) {
            return false;
        }

        return true;
    }


    public static boolean hasArrayAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema array) {
        if (array == null) {
            return false;
        }
        if (!array.uniqueItems()
                && array.maxItems() == Integer.MIN_VALUE
                && array.minItems() == Integer.MAX_VALUE
                && !hasSchemaAnnotation(array.schema())
                && !hasSchemaAnnotation(array.arraySchema())
                && !hasSchemaAnnotation(array.contains())
                && array.maxContains() == 0
                && array.minContains() == 0
                && !hasSchemaAnnotation(array.unevaluatedItems())
                && array.prefixItems().length == 0
        ) {
            return false;
        }
        return true;
    }

    public static Optional<Example> getExample(ExampleObject example) {
        return getExample(example, false);
    }

    public static Optional<Example> getExample(ExampleObject example, boolean ignoreName) {
        if (example == null) {
            return Optional.empty();
        }
        Example exampleObject = new Example();
        if (!ignoreName && StringUtils.isNotBlank(example.name())) {

            if (StringUtils.isNotBlank(example.name())) {
                exampleObject.setDescription(example.name());
            }
            resolveExample(exampleObject, example);

            return Optional.of(exampleObject);
        } else if (ignoreName) {
            if (resolveExample(exampleObject, example)) {
                return Optional.of(exampleObject);
            }
        }
        return Optional.empty();
    }

    private static boolean resolveExample(Example exampleObject, ExampleObject example) {

        boolean isEmpty = true;
        if (StringUtils.isNotBlank(example.summary())) {
            isEmpty = false;
            exampleObject.setSummary(example.summary());
        }

        if (StringUtils.isNotBlank(example.description())) {
            isEmpty = false;
            exampleObject.setDescription(example.description());
        }

        if (StringUtils.isNotBlank(example.externalValue())) {
            isEmpty = false;
            exampleObject.setExternalValue(example.externalValue());
        }
        if (StringUtils.isNotBlank(example.value())) {
            isEmpty = false;
            try {
                ObjectMapper mapper = ObjectMapperFactory.buildStrictGenericObjectMapper();
                exampleObject.setValue(mapper.readTree(example.value()));
            } catch (IOException e) {
                exampleObject.setValue(example.value());
            }
        }
        if (StringUtils.isNotBlank(example.ref())) {
            isEmpty = false;
            exampleObject.set$ref(example.ref());
        }
        if (example.extensions().length > 0) {
            isEmpty = false;
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(example.extensions());
            if (extensions != null) {
                extensions.forEach(exampleObject::addExtension);
            }
        }
        return !isEmpty;
    }

    public static Optional<ArraySchema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, JsonView jsonViewAnnotation) {
        return getArraySchema(arraySchema, null, jsonViewAnnotation);
    }

    public static Optional<ArraySchema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, Components components, JsonView jsonViewAnnotation) {
        return getArraySchema(arraySchema, components, jsonViewAnnotation, false);
    }

    public static Optional<ArraySchema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        Optional result = getArraySchema(arraySchema, components, jsonViewAnnotation, openapi31, null);
        if (result.isPresent()) {
            return Optional.of((ArraySchema) result.get());
        }
        return Optional.empty();
    }

    public static Optional<Schema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, Components components, JsonView jsonViewAnnotation, boolean openapi31, Schema existingSchema) {
        return getArraySchema(arraySchema, components, jsonViewAnnotation, openapi31, existingSchema, false);
    }

    public static Optional<Schema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, Components components, JsonView jsonViewAnnotation, boolean openapi31, Schema existingSchema, boolean processSchemaImplementation) {
        return getArraySchema(arraySchema, components, jsonViewAnnotation, openapi31, existingSchema, processSchemaImplementation, null);
    }

    public static Optional<Schema> getArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema arraySchema, Components components, JsonView jsonViewAnnotation, boolean openapi31, Schema existingSchema, boolean processSchemaImplementation, ModelConverterContext context) {
        if (arraySchema == null || !hasArrayAnnotation(arraySchema)) {
            if (existingSchema == null) {
                return Optional.empty();
            } else {
                return Optional.of(existingSchema);
            }
        }
        Schema arraySchemaObject = null;
        if (!openapi31) {
            if (existingSchema != null && existingSchema instanceof ArraySchema) {
                return Optional.of((ArraySchema) existingSchema);
            }
            arraySchemaObject = new ArraySchema();
        } else {
            if (existingSchema == null) {
                arraySchemaObject = new JsonSchema().typesItem("array");
            } else {
                arraySchemaObject = existingSchema;
            }
        }

        if (arraySchema.uniqueItems()) {
            arraySchemaObject.setUniqueItems(arraySchema.uniqueItems());
        }
        if (arraySchema.maxItems() > 0) {
            arraySchemaObject.setMaxItems(arraySchema.maxItems());
        }

        if (arraySchema.minItems() < Integer.MAX_VALUE) {
            arraySchemaObject.setMinItems(arraySchema.minItems());
        }

        getSchemaFromAnnotation(arraySchema.contains(), components, jsonViewAnnotation, openapi31, null, context).ifPresent(arraySchemaObject::setContains);
        getSchemaFromAnnotation(arraySchema.unevaluatedItems(), components, jsonViewAnnotation, openapi31, null, context).ifPresent(arraySchemaObject::setUnevaluatedItems);

        if (arraySchema.maxContains() > 0) {
            arraySchemaObject.setMaxContains(arraySchema.maxContains());
        }
        if (arraySchema.minContains() > 0) {
            arraySchemaObject.setMinContains(arraySchema.minContains());
        }
        if (arraySchema.prefixItems().length > 0) {
            for (io.swagger.v3.oas.annotations.media.Schema prefixItem : arraySchema.prefixItems()) {
                getSchemaFromAnnotation(prefixItem, components, jsonViewAnnotation, openapi31, null, context).ifPresent(arraySchemaObject::addPrefixItem);
            }
        }

        if (arraySchema.extensions().length > 0) {
            boolean usePrefix = !openapi31;
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, usePrefix, arraySchema.extensions());
            if (extensions != null) {
                extensions.forEach(arraySchemaObject::addExtension);
            }
        }

        if (arraySchema.schema() != null) {
            if (arraySchema.schema().implementation().equals(Void.class)) {
                getSchemaFromAnnotation(arraySchema.schema(), components, jsonViewAnnotation, openapi31, arraySchemaObject.getItems(), context)
                        .ifPresent(arraySchemaObject::setItems);
            } else if (processSchemaImplementation) {
                getSchema(arraySchema.schema(), arraySchema, false, arraySchema.schema().implementation(), components, jsonViewAnnotation, openapi31, context)
                        .ifPresent(arraySchemaObject::setItems);
            } else if (arraySchemaObject.getItems() == null && context != null) {
                // Cycle guard active: the implementation type is currently being
                // resolved up the call stack, so we cannot recurse into it.
                // The type has already been registered as a component, so emit a
                // $ref instead of leaving the array's items unset (gh-5187).
                String implName = findRegisteredSchemaName(arraySchema.schema().implementation(), context);
                if (implName != null) {
                    Schema itemsRef = openapi31 ? new JsonSchema() : new Schema();
                    itemsRef.set$ref(COMPONENTS_REF + implName);
                    arraySchemaObject.setItems(itemsRef);
                }
            }
        }

        if (arraySchema.arraySchema() != null) {
            applyArraySchemaAnnotation(arraySchema.arraySchema(), arraySchemaObject, openapi31);
        }

        return Optional.of(arraySchemaObject);
    }

    private static void applyArraySchemaAnnotation(io.swagger.v3.oas.annotations.media.Schema arraySchemaAnnotation, Schema arraySchemaObject, boolean openapi31) {
        if (StringUtils.isNotBlank(arraySchemaAnnotation.description())) {
            arraySchemaObject.setDescription(arraySchemaAnnotation.description());
        }
        if (StringUtils.isNotBlank(arraySchemaAnnotation.title())) {
            arraySchemaObject.setTitle(arraySchemaAnnotation.title());
        }
        if (arraySchemaAnnotation.deprecated()) {
            arraySchemaObject.deprecated(true);
        }
        if (arraySchemaAnnotation.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY)) {
            arraySchemaObject.setReadOnly(true);
            arraySchemaObject.setWriteOnly(null);
        } else if (arraySchemaAnnotation.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.WRITE_ONLY)) {
            arraySchemaObject.setReadOnly(null);
            arraySchemaObject.setWriteOnly(true);
        } else if (arraySchemaAnnotation.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE)) {
            arraySchemaObject.setReadOnly(null);
            arraySchemaObject.setWriteOnly(null);
        }
        if (openapi31 && arraySchemaAnnotation.examples().length > 0) {
            arraySchemaObject.setExamples(parseExamplesArray(arraySchemaAnnotation, arraySchemaObject));
        }
    }

    public static Optional<Schema> getSchemaFromAnnotation(io.swagger.v3.oas.annotations.media.Schema schema, JsonView jsonViewAnnotation) {
        return getSchemaFromAnnotation(schema, jsonViewAnnotation, false);
    }

    public static Optional<Schema> getSchemaFromAnnotation(io.swagger.v3.oas.annotations.media.Schema schema, JsonView jsonViewAnnotation, boolean openapi31) {
        return getSchemaFromAnnotation(schema, null, jsonViewAnnotation, openapi31);
    }

    public static Optional<Schema> getSchemaFromAnnotation(io.swagger.v3.oas.annotations.media.Schema schema, Components components, JsonView jsonViewAnnotation) {
        return getSchemaFromAnnotation(schema, components, jsonViewAnnotation, false);
    }

    public static Optional<Schema> getSchemaFromAnnotation(io.swagger.v3.oas.annotations.media.Schema schema, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        return getSchemaFromAnnotation(schema, components, jsonViewAnnotation, openapi31, null);
    }

    public static Optional<Schema> getSchemaFromAnnotation(
            io.swagger.v3.oas.annotations.media.Schema schema,
            Components components,
            JsonView jsonViewAnnotation,
            boolean openapi31,
            Schema existingSchema) {
        return getSchemaFromAnnotation(schema, components, jsonViewAnnotation, openapi31, existingSchema, null);
    }

    public static Optional<Schema> getSchemaFromAnnotation(
            io.swagger.v3.oas.annotations.media.Schema schema,
            Components components,
            JsonView jsonViewAnnotation,
            boolean openapi31,
            Schema existingSchema,
            ModelConverterContext context) {
        return getSchemaFromAnnotation(schema, components, jsonViewAnnotation, openapi31, existingSchema, Schema.SchemaResolution.DEFAULT, context);
    }

    public static Optional<Schema> getSchemaFromAnnotation(
            io.swagger.v3.oas.annotations.media.Schema schema,
            Components components,
            JsonView jsonViewAnnotation,
            boolean openapi31,
            Schema existingSchema,
            Schema.SchemaResolution schemaResolution,
            ModelConverterContext context) {
        if (schema == null || !hasSchemaAnnotation(schema)) {
            if (existingSchema == null || (!openapi31 && Schema.SchemaResolution.DEFAULT.equals(schemaResolution))) {
                return Optional.empty();
            } else if (existingSchema != null && (openapi31 || Schema.SchemaResolution.INLINE.equals(schemaResolution))) {
                return Optional.of(existingSchema);
            }
        }
        Schema schemaObject = null;
        if (!openapi31) {
            if (existingSchema != null) {
                if (!Schema.SchemaResolution.DEFAULT.equals(schemaResolution)) {
                    schemaObject = existingSchema;
                } else {
                    return Optional.of(existingSchema);
                }
            }
            if (Schema.SchemaResolution.DEFAULT.equals(schemaResolution)) {
                if (schema != null && (schema.oneOf().length > 0 ||
                        schema.allOf().length > 0 ||
                        schema.anyOf().length > 0)) {
                    schemaObject = new ComposedSchema();
                } else {
                    schemaObject = new Schema();
                }
            } else if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) {
                if (existingSchema == null) {
                    schemaObject = new Schema();
                } else {
                    schemaObject = existingSchema;
                }
            }
        } else {
            if (existingSchema == null) {
                schemaObject = new JsonSchema();
            } else {
                schemaObject = existingSchema;
            }
        }
        if (schema == null) {
            return Optional.of(schemaObject);
        }
        if (StringUtils.isNotBlank(schema.description())) {
            schemaObject.setDescription(schema.description());
        }
        if (StringUtils.isNotBlank(schema.ref())) {
            schemaObject.set$ref(schema.ref());
        }
        if (StringUtils.isNotBlank(schema.type())) {
            schemaObject.setType(schema.type());
        }

        if (schema.types().length > 0) {
            schemaObject.setTypes(new LinkedHashSet<>(Arrays.asList(schema.types())));
        }
        if (StringUtils.isNotBlank(schema.$id())) {
            schemaObject.set$id(schema.$id());
        }
        if (StringUtils.isNotBlank(schema.$schema())) {
            schemaObject.set$schema(schema.$schema());
        }
        if (StringUtils.isNotBlank(schema.$anchor())) {
            schemaObject.set$anchor(schema.$anchor());
        }
        if (StringUtils.isNotBlank(schema.$vocabulary())) {
            schemaObject.set$vocabulary(schema.$vocabulary());
        }
        if (StringUtils.isNotBlank(schema.$dynamicAnchor())) {
            schemaObject.set$dynamicAnchor(schema.$dynamicAnchor());
        }
        if (StringUtils.isNotBlank(schema.$dynamicRef())) {
            schemaObject.set$dynamicRef(schema.$dynamicRef());
        }
        if (StringUtils.isNotBlank(schema.contentEncoding())) {
            schemaObject.setContentEncoding(schema.contentEncoding());
        }
        if (StringUtils.isNotBlank(schema.contentMediaType())) {
            schemaObject.setContentMediaType(schema.contentMediaType());
        }
        if (schema.exclusiveMaximumValue() != Integer.MAX_VALUE && schema.exclusiveMaximumValue() > 0) {
            schemaObject.setExclusiveMaximumValue(BigDecimal.valueOf(schema.exclusiveMaximumValue()));
        }
        if (schema.exclusiveMinimumValue() > 0) {
            schemaObject.setExclusiveMinimumValue(BigDecimal.valueOf(schema.exclusiveMinimumValue()));
        }
        if (!schema.contentSchema().equals(Void.class)) {
            schemaObject.setContentSchema(resolveSchemaFromType(schema.contentSchema(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (!schema.propertyNames().equals(Void.class)) {
            schemaObject.setPropertyNames(resolveSchemaFromType(schema.propertyNames(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (!schema._if().equals(Void.class)) {
            schemaObject.setIf(resolveSchemaFromType(schema._if(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (!schema._else().equals(Void.class)) {
            schemaObject.setElse(resolveSchemaFromType(schema._else(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (!schema.then().equals(Void.class)) {
            schemaObject.setThen(resolveSchemaFromType(schema.then(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (StringUtils.isNotBlank(schema._const())) {
            try {
                Object _const;
                if (openapi31) {
                    _const = Json31.mapper().readTree(schema._const());
                } else {
                    _const = Json.mapper().readTree(schema._const());
                }
                schemaObject.setConst(_const);
            } catch (IOException e) {
                schemaObject.setConst(schema._const());
            }
        }
        if (StringUtils.isNotBlank(schema.$comment())) {
            schemaObject.set$comment(schema.$comment());
        }
        if (schema.dependentRequiredMap().length > 0) {
            final Map<String, List<String>> dependentRequired = new LinkedHashMap<>();
            for (DependentRequired dependentRequiredAnnotation : schema.dependentRequiredMap()) {
                dependentRequired.put(dependentRequiredAnnotation.name(), Arrays.asList(dependentRequiredAnnotation.value()));
            }
            schemaObject.setDependentRequired(dependentRequired);
        }
        if (schema.dependentSchemas().length > 0) {
            final Map<String, Schema> dependentSchema = new LinkedHashMap<>();
            for (StringToClassMapItem mapItem : schema.dependentSchemas()) {
                dependentSchema.put(mapItem.key(), resolveSchemaFromType(mapItem.value(), components, jsonViewAnnotation, openapi31, null, null, context));
            }
            schemaObject.setDependentSchemas(dependentSchema);
        }
        if (schema.patternProperties().length > 0) {
            final Map<String, Schema> patternProperties = new LinkedHashMap<>();
            for (StringToClassMapItem mapItem : schema.patternProperties()) {
                patternProperties.put(mapItem.key(), resolveSchemaFromType(mapItem.value(), components, jsonViewAnnotation, openapi31, null, null, context));
            }
            schemaObject.setPatternProperties(patternProperties);
        }
        if (schema.properties().length > 0) {
            final Map<String, Schema> properties = new LinkedHashMap<>();
            for (StringToClassMapItem mapItem : schema.properties()) {
                properties.put(mapItem.key(), resolveSchemaFromType(mapItem.value(), components, jsonViewAnnotation, openapi31, null, null, context));
            }
            schemaObject.setProperties(properties);
        }
        if (!schema.unevaluatedProperties().equals(Void.class)) {
            schemaObject.setUnevaluatedProperties(resolveSchemaFromType(schema.unevaluatedProperties(), components, jsonViewAnnotation, openapi31, null, null, context));
        }
        if (openapi31 && schema.examples().length > 0) {
            schemaObject.setExamples(parseExamplesArray(schema, schemaObject));
        }

        if (schema.defaultValue() != null && StringUtils.isNotBlank(schema.defaultValue())) {
            setDefaultSchema(schema, openapi31, schemaObject);
        }
        if (StringUtils.isNotBlank(schema.example()) &&
                (!openapi31 || schema.examples().length == 0)) {
            setExampleSchema(schema, openapi31, schemaObject);
        }
        if (StringUtils.isNotBlank(schema.format())) {
            schemaObject.setFormat(schema.format());
        }
        if (StringUtils.isNotBlank(schema.pattern())) {
            schemaObject.setPattern(schema.pattern());
        }
        if (schema.readOnly()) {
            schemaObject.setReadOnly(schema.readOnly());
        }
        if (schema.deprecated()) {
            schemaObject.setDeprecated(schema.deprecated());
        }
        if (schema.exclusiveMaximum()) {
            schemaObject.setExclusiveMaximum(schema.exclusiveMaximum());
        }
        if (schema.exclusiveMinimum()) {
            schemaObject.setExclusiveMinimum(schema.exclusiveMinimum());
        }
        if (schema.maxProperties() > 0) {
            schemaObject.setMaxProperties(schema.maxProperties());
        }
        if (schema.maxLength() != Integer.MAX_VALUE && schema.maxLength() > 0) {
            schemaObject.setMaxLength(schema.maxLength());
        }
        if (schema.minProperties() > 0) {
            schemaObject.setMinProperties(schema.minProperties());
        }
        if (schema.minLength() > 0) {
            schemaObject.setMinLength(schema.minLength());
        }
        if (schema.multipleOf() != 0) {
            schemaObject.setMultipleOf(BigDecimal.valueOf(schema.multipleOf()));
        }
        if (NumberUtils.isCreatable(schema.maximum())) {
            String filteredMaximum = schema.maximum().replace(Constants.COMMA, StringUtils.EMPTY);
            schemaObject.setMaximum(new BigDecimal(filteredMaximum));
        }
        if (NumberUtils.isCreatable(schema.minimum())) {
            String filteredMinimum = schema.minimum().replace(Constants.COMMA, StringUtils.EMPTY);
            schemaObject.setMinimum(new BigDecimal(filteredMinimum));
        }
        if (schema.nullable()) {
            if (openapi31) {
                schemaObject.addType("null");
            } else {
                schemaObject.setNullable(true);
            }
        }
        if (StringUtils.isNotBlank(schema.title())) {
            schemaObject.setTitle(schema.title());
        }
        if (schema.writeOnly()) {
            schemaObject.setWriteOnly(schema.writeOnly());
        }
        // process after readOnly and writeOnly
        if (schema.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY)) {
            schemaObject.setReadOnly(true);
            schemaObject.setWriteOnly(null);
        } else if (schema.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.WRITE_ONLY)) {
            schemaObject.setReadOnly(null);
            schemaObject.setWriteOnly(true);
        } else if (schema.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE)) {
            schemaObject.setReadOnly(null);
            schemaObject.setWriteOnly(null);
        }
        if (schema.requiredProperties().length > 0) {
            schemaObject.setRequired(Arrays.asList(schema.requiredProperties()));
        }
        if (schema.allowableValues().length > 0) {
            schemaObject.setEnum(Arrays.asList(schema.allowableValues()));
        }

        if (schema.extensions().length > 0) {
            boolean usePrefix = !openapi31;
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, usePrefix, schema.extensions());
            if (extensions != null) {
                extensions.forEach(schemaObject::addExtension);
            }
        }

        getExternalDocumentation(schema.externalDocs(), openapi31).ifPresent(schemaObject::setExternalDocs);

        if (!schema.not().equals(Void.class)) {
            Class<?> schemaImplementation = schema.not();
            Schema notSchemaObject = resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, null, null, context);
            schemaObject.setNot(notSchemaObject);
        }
        if (schema.oneOf().length > 0) {
            Class<?>[] schemaImplementations = schema.oneOf();
            for (Class<?> schemaImplementation : schemaImplementations) {
                Schema oneOfSchemaObject = resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, null, null, context);
                schemaObject.addOneOfItem(oneOfSchemaObject);
            }
        }
        if (schema.anyOf().length > 0) {
            Class<?>[] schemaImplementations = schema.anyOf();
            for (Class<?> schemaImplementation : schemaImplementations) {
                Schema anyOfSchemaObject = resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, null, null, context);
                schemaObject.addAnyOfItem(anyOfSchemaObject);
            }
        }
        if (schema.allOf().length > 0) {
            Class<?>[] schemaImplementations = schema.allOf();
            for (Class<?> schemaImplementation : schemaImplementations) {
                Schema allOfSchemaObject = resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, null, null, context);
                schemaObject.addAllOfItem(allOfSchemaObject);
            }
        }
        if (schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.TRUE)) {
            schemaObject.additionalProperties(true);
        } else if (schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.FALSE)) {
            schemaObject.additionalProperties(false);
        } else {
            if (!schema.additionalPropertiesSchema().equals(Void.class)) {
                schemaObject.additionalProperties(resolveSchemaFromType(schema.additionalPropertiesSchema(), components, jsonViewAnnotation, openapi31, null, null, context));
            }
        }

        return Optional.of(schemaObject);
    }

    private static void setExampleSchema(io.swagger.v3.oas.annotations.media.Schema schema, boolean openapi31, Schema schemaObject) {
        String exampleValue = schema.example().trim();
        final ObjectMapper mapper = openapi31 ? Json31.mapper() : Json.mapper();

        try {
            JsonNode node = mapper.readTree(exampleValue);
            // Only parse "null" as null value when nullable=true
            if (node.isNull() && schema.nullable()) {
                schemaObject.setExample(null);
            } else if (shouldUseNodeAsExample(node, schemaObject)) {
                schemaObject.setExample(node);
            } else {
                schemaObject.setExample(exampleValue);
            }
        } catch (IOException ignored) {
            schemaObject.setExample(exampleValue);
        }
    }

    private static boolean shouldUseNodeAsExample(JsonNode node, Schema schemaObject) {
        if (node.isObject() || node.isArray()) {
            return true;
        }
        if (schemaObject != null) {
            if (SchemaTypeUtils.isNumberSchema(schemaObject)) {
                return true;
            }
            if (SchemaTypeUtils.isBooleanSchema(schemaObject)) {
                return true;
            }
            if (SchemaTypeUtils.isStringSchema(schemaObject)) {
                return false;
            }
        }
        return node.isNumber() || node.isBoolean();
    }

    private static void setDefaultSchema(io.swagger.v3.oas.annotations.media.Schema schema, boolean openapi31, Schema schemaObject) {
        String defaultValue = schema.defaultValue().trim();
        final ObjectMapper mapper = openapi31 ? Json31.mapper() : Json.mapper();

        try {
            JsonNode node = mapper.readTree(defaultValue);
            // Only parse "null" as null value when nullable=true
            if (node.isNull() && schema.nullable()) {
                schemaObject.setDefault(null);
            } else if (node.isObject() || node.isArray()) {
                schemaObject.setDefault(node);
            } else {
                schemaObject.setDefault(defaultValue);
            }
        } catch (IOException ignored) {
            schemaObject.setDefault(defaultValue);
        }
    }

    public static List<Object> parseExamplesArray(io.swagger.v3.oas.annotations.media.Schema schema) {
        return parseExamplesArray(schema, null);
    }

    public static List<Object> parseExamplesArray(io.swagger.v3.oas.annotations.media.Schema schema, Schema schemaObject) {
        String[] examplesArray = schema.examples();
        List<Object> parsedExamples = new ArrayList<>();
        final ObjectMapper mapper = Json31.mapper();

        for (String exampleStr : examplesArray) {
            if (StringUtils.isBlank(exampleStr)) {
                parsedExamples.add(exampleStr);
                continue;
            }

            String trimmed = exampleStr.trim();
            try {
                JsonNode node = mapper.readTree(trimmed);
                // Only parse "null" as null value when nullable=true
                if (node.isNull() && schema.nullable()) {
                    parsedExamples.add(null);
                } else if (schemaObject == null && "string".equals(schema.type())) {
                    parsedExamples.add(trimmed);
                } else if (shouldUseNodeAsExample(node, schemaObject)) {
                    parsedExamples.add(node);
                } else {
                    parsedExamples.add(trimmed);
                }
            } catch (IOException ignored) {
                parsedExamples.add(trimmed);
            }
        }

        return parsedExamples;
    }

    public static Schema resolveSchemaFromType(Class<?> schemaImplementation, Components components, JsonView jsonViewAnnotation) {
        return resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, false);
    }

    public static Schema resolveSchemaFromType(Class<?> schemaImplementation, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        return resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, null, null, null);
    }

    public static Schema resolveSchemaFromType(Class<?> schemaImplementation,
                                               Components components,
                                               JsonView jsonViewAnnotation,
                                               boolean openapi31,
                                               io.swagger.v3.oas.annotations.media.Schema schemaAnnotation,
                                               io.swagger.v3.oas.annotations.media.ArraySchema arrayAnnotation, ModelConverterContext context) {
        Schema schemaObject;
        PrimitiveType primitiveType = PrimitiveType.fromType(schemaImplementation);
        if (primitiveType != null) {
            schemaObject = openapi31 ? primitiveType.createProperty31() : primitiveType.createProperty();
        } else {
            schemaObject = new Schema();
            ResolvedSchema resolvedSchema = null;
            if (context == null) {
                resolvedSchema = ModelConverters.getInstance(openapi31).readAllAsResolvedSchema(new AnnotatedType().type(schemaImplementation).components(components).jsonViewAnnotation(jsonViewAnnotation));
            } else {
                Schema resSchema = context.resolve(new AnnotatedType().type(schemaImplementation).components(components).jsonViewAnnotation(jsonViewAnnotation));
                if (resSchema != null) {
                    resolvedSchema = new ResolvedSchema();
                    resolvedSchema.schema = resSchema;
                    resolvedSchema.referencedSchemas = context.getDefinedModels();
                }
            }
            Map<String, Schema> schemaMap;
            if (resolvedSchema != null) {
                schemaMap = resolvedSchema.referencedSchemas;
                if (schemaMap != null) {
                    schemaMap.forEach((key, referencedSchema) -> {
                        if (components != null) {
                            if (context != null) {
                                context.defineModel(key, referencedSchema);
                            }
                            components.addSchemas(key, referencedSchema);
                        }
                    });
                }
                if (resolvedSchema.schema != null) {
                    if (StringUtils.isNotBlank(resolvedSchema.schema.getName())) {
                        schemaObject.set$ref(COMPONENTS_REF + resolvedSchema.schema.getName());
                    } else {
                        schemaObject = resolvedSchema.schema;
                    }
                }
            }
        }
        // Schema existingSchemaObject = clone(schemaObject, openapi31);
        Schema existingSchemaObject = schemaObject;
        if (openapi31) {
            Optional<Schema> reResolvedSchema = resolveSiblings(existingSchemaObject, components, jsonViewAnnotation, openapi31, schemaAnnotation, arrayAnnotation);
            if (reResolvedSchema.isPresent()) {
                existingSchemaObject = reResolvedSchema.get();
            }
        }
        boolean doesSchemaHaveNullOrEmptyTypes = existingSchemaObject.getTypes() == null || existingSchemaObject.getTypes().isEmpty();
        if (StringUtils.isBlank(existingSchemaObject.get$ref())
                && StringUtils.isBlank(existingSchemaObject.getType())
                && doesSchemaHaveNullOrEmptyTypes) {
            // default to string
            existingSchemaObject.setType("string");
        }
        return existingSchemaObject;
    }

    private static Optional<Schema> resolveSiblings(Schema existingSchemaObject,
                                                    Components components,
                                                    JsonView jsonViewAnnotation,
                                                    boolean openapi31,
                                                    io.swagger.v3.oas.annotations.media.Schema schemaAnnotation,
                                                    io.swagger.v3.oas.annotations.media.ArraySchema arrayAnnotation) {
        Optional<Schema> reResolvedSchema;
        reResolvedSchema = getSchemaFromAnnotation(schemaAnnotation, components, jsonViewAnnotation, openapi31, existingSchemaObject);
        // Return the reResolvedSchema if the schemaAnnotation has incurred differences
        if (reResolvedSchema.isPresent() && !reResolvedSchema.get().equals(existingSchemaObject)) {
            return reResolvedSchema;
        }
        // If there were no changes based upon the schemaAnnotation, check the arrayAnnotation's schema
        if (arrayAnnotation != null && arrayAnnotation.schema() != null) {
            reResolvedSchema = getSchemaFromAnnotation(arrayAnnotation.schema(), components, jsonViewAnnotation, openapi31, existingSchemaObject);
            if (reResolvedSchema.isPresent()) {
                return reResolvedSchema;
            }
        }
        // If neither of the schema annotations applied any changes, check the arrayAnnotation
        return getArraySchema(arrayAnnotation, components, null, openapi31, existingSchemaObject);
    }

    public static Optional<Set<Tag>> getTags(io.swagger.v3.oas.annotations.tags.Tag[] tags, boolean skipOnlyName) {
        if (tags == null) {
            return Optional.empty();
        }
        Set<Tag> tagsList = new LinkedHashSet<>();
        for (io.swagger.v3.oas.annotations.tags.Tag tag : tags) {
            if (StringUtils.isBlank(tag.name())) {
                continue;
            }
            if (skipOnlyName &&
                    StringUtils.isBlank(tag.description()) &&
                    StringUtils.isBlank(tag.externalDocs().description()) &&
                    StringUtils.isBlank(tag.externalDocs().url())) {
                continue;
            }
            Tag tagObject = new Tag();
            if (StringUtils.isNotBlank(tag.description())) {
                tagObject.setDescription(tag.description());
            }
            tagObject.setName(tag.name());
            getExternalDocumentation(tag.externalDocs()).ifPresent(tagObject::setExternalDocs);
            if (tag.extensions().length > 0) {
                Map<String, Object> extensions = AnnotationsUtils.getExtensions(tag.extensions());
                if (extensions != null) {
                    extensions.forEach(tagObject::addExtension);
                }
            }
            tagsList.add(tagObject);
        }
        if (tagsList.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(tagsList);
    }

    public static Optional<List<Server>> getServers(io.swagger.v3.oas.annotations.servers.Server[] servers) {
        if (servers == null) {
            return Optional.empty();
        }
        List<Server> serverObjects = new ArrayList<>();
        for (io.swagger.v3.oas.annotations.servers.Server server : servers) {
            getServer(server).ifPresent(serverObjects::add);
        }
        if (serverObjects.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(serverObjects);
    }

    public static Optional<Server> getServer(io.swagger.v3.oas.annotations.servers.Server server) {
        if (server == null) {
            return Optional.empty();
        }

        Server serverObject = new Server();
        boolean isEmpty = true;
        if (StringUtils.isNotBlank(server.url())) {
            serverObject.setUrl(server.url());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(server.description())) {
            serverObject.setDescription(server.description());
            isEmpty = false;
        }
        if (server.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(server.extensions());
            if (extensions != null) {
                extensions.forEach(serverObject::addExtension);
            }
            isEmpty = false;
        }
        if (isEmpty) {
            return Optional.empty();
        }
        io.swagger.v3.oas.annotations.servers.ServerVariable[] serverVariables = server.variables();
        if (serverVariables.length > 0) {
            ServerVariables serverVariablesObject = new ServerVariables();
            for (io.swagger.v3.oas.annotations.servers.ServerVariable serverVariable : serverVariables) {
                ServerVariable serverVariableObject = new ServerVariable();
                if (StringUtils.isNotBlank(serverVariable.description())) {
                    serverVariableObject.setDescription(serverVariable.description());
                }
                if (StringUtils.isNotBlank(serverVariable.defaultValue())) {
                    serverVariableObject.setDefault(serverVariable.defaultValue());
                }
                if (serverVariable.allowableValues() != null && serverVariable.allowableValues().length > 0) {
                    if (StringUtils.isNotBlank(serverVariable.allowableValues()[0])) {
                        serverVariableObject.setEnum(Arrays.asList(serverVariable.allowableValues()));
                    }
                }
                if (serverVariable.extensions() != null && serverVariable.extensions().length > 0) {
                    Map<String, Object> extensions = AnnotationsUtils.getExtensions(serverVariable.extensions());
                    if (extensions != null) {
                        extensions.forEach(serverVariableObject::addExtension);
                    }
                }
                serverVariablesObject.addServerVariable(serverVariable.name(), serverVariableObject);
            }
            serverObject.setVariables(serverVariablesObject);
        }

        return Optional.of(serverObject);
    }

    public static Optional<ExternalDocumentation> getExternalDocumentation(io.swagger.v3.oas.annotations.ExternalDocumentation externalDocumentation) {
        return getExternalDocumentation(externalDocumentation, false);
    }

    public static Optional<ExternalDocumentation> getExternalDocumentation(io.swagger.v3.oas.annotations.ExternalDocumentation externalDocumentation, boolean openapi31) {
        if (externalDocumentation == null) {
            return Optional.empty();
        }
        boolean isEmpty = true;
        ExternalDocumentation external = new ExternalDocumentation();
        if (StringUtils.isNotBlank(externalDocumentation.description())) {
            isEmpty = false;
            external.setDescription(externalDocumentation.description());
        }
        if (StringUtils.isNotBlank(externalDocumentation.url())) {
            isEmpty = false;
            external.setUrl(externalDocumentation.url());
        }
        if (externalDocumentation.extensions() != null && externalDocumentation.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, externalDocumentation.extensions());
            if (extensions != null) {
                if (openapi31) {
                    extensions.forEach(external::addExtension31);
                } else {
                    extensions.forEach(external::addExtension);
                }
                isEmpty = false;
            }
        }

        if (isEmpty) {
            return Optional.empty();
        }
        return Optional.of(external);
    }

    public static Optional<Info> getInfo(io.swagger.v3.oas.annotations.info.Info info) {
        return getInfo(info, false);
    }

    public static Optional<Info> getInfo(io.swagger.v3.oas.annotations.info.Info info, boolean openapi31) {
        if (info == null) {
            return Optional.empty();
        }
        boolean isEmpty = true;
        Info infoObject = new Info();
        if (StringUtils.isNotBlank(info.description())) {
            infoObject.setDescription(info.description());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(info.termsOfService())) {
            infoObject.setTermsOfService(info.termsOfService());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(info.title())) {
            infoObject.setTitle(info.title());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(info.version())) {
            infoObject.setVersion(info.version());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(info.summary())) {
            infoObject.setSummary(info.summary());
            isEmpty = false;
        }
        if (info.extensions() != null && info.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, info.extensions());
            if (extensions != null) {
                if (openapi31) {
                    extensions.forEach(infoObject::addExtension31);
                } else {
                    extensions.forEach(infoObject::addExtension);
                }
                isEmpty = false;
            }
        }
        if (isEmpty) {
            return Optional.empty();
        }
        getContact(info.contact()).ifPresent(infoObject::setContact);
        getLicense(info.license()).ifPresent(infoObject::setLicense);

        return Optional.of(infoObject);
    }

    public static Optional<Contact> getContact(io.swagger.v3.oas.annotations.info.Contact contact) {
        return getContact(contact, false);
    }

    public static Optional<Contact> getContact(io.swagger.v3.oas.annotations.info.Contact contact, boolean openapi31) {
        if (contact == null) {
            return Optional.empty();
        }
        boolean isEmpty = true;
        Contact contactObject = new Contact();
        if (StringUtils.isNotBlank(contact.email())) {
            contactObject.setEmail(contact.email());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(contact.name())) {
            contactObject.setName(contact.name());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(contact.url())) {
            contactObject.setUrl(contact.url());
            isEmpty = false;
        }
        if (contact.extensions() != null && contact.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, contact.extensions());
            if (extensions != null) {
                if (openapi31) {
                    extensions.forEach(contactObject::addExtension31);
                } else {
                    extensions.forEach(contactObject::addExtension);
                }
                isEmpty = false;
            }
        }

        if (isEmpty) {
            return Optional.empty();
        }
        return Optional.of(contactObject);
    }

    public static Optional<License> getLicense(io.swagger.v3.oas.annotations.info.License license) {
        return getLicense(license, false);
    }

    public static Optional<License> getLicense(io.swagger.v3.oas.annotations.info.License license, boolean openapi31) {
        if (license == null) {
            return Optional.empty();
        }
        License licenseObject = new License();
        boolean isEmpty = true;
        if (StringUtils.isNotBlank(license.name())) {
            licenseObject.setName(license.name());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(license.url())) {
            licenseObject.setUrl(license.url());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(license.identifier())) {
            licenseObject.setIdentifier(license.identifier());
            isEmpty = false;
        }
        if (license.extensions() != null && license.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, license.extensions());
            if (extensions != null) {
                if (openapi31) {
                    extensions.forEach(licenseObject::addExtension31);
                } else {
                    extensions.forEach(licenseObject::addExtension);
                }
                isEmpty = false;
            }
        }
        if (isEmpty) {
            return Optional.empty();
        }
        return Optional.of(licenseObject);
    }

    public static Map<String, Link> getLinks(io.swagger.v3.oas.annotations.links.Link[] links) {
        return getLinks(links, false);
    }

    public static Map<String, Link> getLinks(io.swagger.v3.oas.annotations.links.Link[] links, boolean openapi31) {
        Map<String, Link> linkMap = new HashMap<>();
        if (links == null) {
            return linkMap;
        }
        for (io.swagger.v3.oas.annotations.links.Link link : links) {
            getLink(link, openapi31).ifPresent(linkResult -> linkMap.put(link.name(), linkResult));
        }
        return linkMap;
    }

    public static Optional<Link> getLink(io.swagger.v3.oas.annotations.links.Link link) {
        return getLink(link, false);
    }

    public static Optional<Link> getLink(io.swagger.v3.oas.annotations.links.Link link, boolean openapi31) {
        if (link == null) {
            return Optional.empty();
        }
        boolean isEmpty = true;
        Link linkObject = new Link();
        if (StringUtils.isNotBlank(link.description())) {
            linkObject.setDescription(link.description());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(link.operationId())) {
            linkObject.setOperationId(link.operationId());
            isEmpty = false;
            if (StringUtils.isNotBlank(link.operationRef())) {
                LOGGER.debug("OperationId and OperatonRef are mutually exclusive, there must be only one setted");
            }
        } else {
            if (StringUtils.isNotBlank(link.operationRef())) {
                linkObject.setOperationRef(link.operationRef());
                isEmpty = false;
            }
        }
        if (StringUtils.isNotBlank(link.ref())) {
            linkObject.set$ref(link.ref());
            isEmpty = false;
        }
        if (link.extensions() != null && link.extensions().length > 0) {
            Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, link.extensions());
            if (extensions != null) {
                if (openapi31) {
                    extensions.forEach(linkObject::addExtension31);
                } else {
                    extensions.forEach(linkObject::addExtension);
                }
                isEmpty = false;
            }
        }
        if (isEmpty) {
            return Optional.empty();
        }
        Map<String, String> linkParameters = getLinkParameters(link.parameters());
        if (linkParameters.size() > 0) {
            linkObject.setParameters(linkParameters);
        }

        if (StringUtils.isNotBlank(link.requestBody())) {
            JsonNode processedValue = null;
            try {
                if (openapi31) {
                    processedValue = Json31.mapper().readTree(link.requestBody());
                } else {
                    processedValue = Json.mapper().readTree(link.requestBody());
                }
            } catch (Exception e) {
                // not a json string
            }
            if (processedValue == null) {
                linkObject.requestBody(link.requestBody());
            } else {
                linkObject.requestBody(processedValue);
            }
        }
        return Optional.of(linkObject);
    }

    public static Map<String, String> getLinkParameters(LinkParameter[] linkParameter) {
        Map<String, String> linkParametersMap = new HashMap<>();
        if (linkParameter == null) {
            return linkParametersMap;
        }
        for (LinkParameter parameter : linkParameter) {
            if (StringUtils.isNotBlank(parameter.name())) {
                linkParametersMap.put(parameter.name(), parameter.expression());
            }
        }

        return linkParametersMap;
    }

    public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation) {
        return getHeaders(annotationHeaders, null, jsonViewAnnotation);
    }

    public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation) {
        return getHeaders(annotationHeaders, components, jsonViewAnnotation, false);
    }

    public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation, boolean openapi31) {
        return getHeaders(annotationHeaders, null, jsonViewAnnotation, openapi31);
    }

    public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        if (annotationHeaders == null) {
            return Optional.empty();
        }

        Map<String, Header> headers = new HashMap<>();
        for (io.swagger.v3.oas.annotations.headers.Header header : annotationHeaders) {
            getHeader(header, components, jsonViewAnnotation, openapi31).ifPresent(headerResult -> headers.put(header.name(), headerResult));
        }

        if (headers.size() == 0) {
            return Optional.empty();
        }
        return Optional.of(headers);
    }

    public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation) {
        return getHeader(header, null, jsonViewAnnotation);
    }

    public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, Components components, JsonView jsonViewAnnotation) {
        return getHeader(header, components, jsonViewAnnotation, false);
    }

    public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation, boolean openapi31) {
        return getHeader(header, null, jsonViewAnnotation, openapi31);
    }

    public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, Components components, JsonView jsonViewAnnotation, boolean openapi31) {

        if (header == null || header.hidden()) {
            return Optional.empty();
        }

        Header headerObject = new Header();
        boolean isEmpty = !StringUtils.isNotBlank(header.name());
        if (StringUtils.isNotBlank(header.description())) {
            headerObject.setDescription(header.description());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(header.ref())) {
            headerObject.set$ref(header.ref());
            isEmpty = false;
        }
        if (StringUtils.isNotBlank(header.example())) {
            try {
                headerObject.setExample(Json.mapper().readTree(header.example()));
            } catch (IOException e) {
                headerObject.setExample(header.example());
            }
        }
        if (header.deprecated()) {
            headerObject.setDeprecated(header.deprecated());
        }
        if (header.required()) {
            headerObject.setRequired(header.required());
            isEmpty = false;
        }
        Map<String, Example> exampleMap = new LinkedHashMap<>();
        if (header.examples().length == 1 && StringUtils.isBlank(header.examples()[0].name())) {
            Optional<Example> exampleOptional = AnnotationsUtils.getExample(header.examples()[0], true);
            exampleOptional.ifPresent(headerObject::setExample);
        } else {
            for (ExampleObject exampleObject : header.examples()) {
                AnnotationsUtils.getExample(exampleObject).ifPresent(example -> exampleMap.put(exampleObject.name(), example));
            }
        }
        if (!exampleMap.isEmpty()) {
            headerObject.setExamples(exampleMap);
        }
        headerObject.setStyle(Header.StyleEnum.SIMPLE);

        if (header.schema() != null) {
            if (header.schema().implementation().equals(Void.class)) {
                AnnotationsUtils.getSchemaFromAnnotation(header.schema(), jsonViewAnnotation, openapi31).ifPresent(
                        headerObject::setSchema);
            } else {
                AnnotatedType annotatedType = new AnnotatedType()
                        .type(getSchemaType(header.schema()))
                        .resolveAsRef(true)
                        .skipOverride(true)
                        .jsonViewAnnotation(jsonViewAnnotation);

                final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).resolveAsResolvedSchema(annotatedType);

                if (resolvedSchema.schema != null) {
                    headerObject.setSchema(resolvedSchema.schema);
                }
                if (resolvedSchema.referencedSchemas != null && components != null) {
                    resolvedSchema.referencedSchemas.forEach(components::addSchemas);
                }

            }
        }
        if (hasArrayAnnotation(header.array())) {
            AnnotationsUtils.getArraySchema(header.array(), components, jsonViewAnnotation, openapi31, null, true).ifPresent(
                    headerObject::setSchema);
        }

        setHeaderExplode(headerObject, header);
        if (isEmpty) {
            return Optional.empty();
        }

        return Optional.of(headerObject);
    }

    public static void setHeaderExplode(Header header, io.swagger.v3.oas.annotations.headers.Header h) {
        if (isHeaderExplodable(h, header)) {
            if (Explode.TRUE.equals(h.explode())) {
                header.setExplode(Boolean.TRUE);
            } else if (Explode.FALSE.equals(h.explode())) {
                header.setExplode(Boolean.FALSE);
            }
        }
    }

    private static boolean isHeaderExplodable(io.swagger.v3.oas.annotations.headers.Header h, Header header) {
        io.swagger.v3.oas.annotations.media.Schema schema = hasArrayAnnotation(h.array()) ? h.array().schema() : h.schema();
        boolean explode = true;
        if (schema != null) {
            Class implementation = schema.implementation();
            if (implementation == Void.class) {
                if (!isExplodableOAS30(schema)) {
                    explode = false;
                }
            }
        }
        return explode;
    }

    public static boolean isExplodableOAS30(io.swagger.v3.oas.annotations.media.Schema schema) {
        return schema.type().equals("object") || schema.type().equals("array");
    }

    public static boolean isExplodableOAS31(io.swagger.v3.oas.annotations.media.Schema schema) {
        return Arrays.asList(schema.types()).contains("object") || Arrays.asList(schema.types()).contains("array");
    }

    public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas.annotations.media.Encoding encoding, JsonView jsonViewAnnotation) {
        addEncodingToMediaType(mediaType, encoding, jsonViewAnnotation, false);
    }

    public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas.annotations.media.Encoding encoding, JsonView jsonViewAnnotation, boolean openapi31) {
        if (encoding == null) {
            return;
        }
        if (StringUtils.isNotBlank(encoding.name())) {

            Encoding encodingObject = new Encoding();

            if (StringUtils.isNotBlank(encoding.contentType())) {
                encodingObject.setContentType(encoding.contentType());
            }
            if (StringUtils.isNotBlank(encoding.style())) {
                encodingObject.setStyle(Encoding.StyleEnum.valueOf(encoding.style()));
            }
            if (encoding.explode()) {
                encodingObject.setExplode(encoding.explode());
            }
            if (encoding.allowReserved()) {
                encodingObject.setAllowReserved(encoding.allowReserved());
            }

            if (encoding.headers() != null) {
                getHeaders(encoding.headers(), null, jsonViewAnnotation, openapi31).ifPresent(encodingObject::headers);
            }
            if (encoding.extensions() != null && encoding.extensions().length > 0) {
                Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, encoding.extensions());
                if (extensions != null) {
                    if (openapi31) {
                        extensions.forEach(encodingObject::addExtension31);
                    } else {
                        extensions.forEach(encodingObject::addExtension);
                    }
                }
            }

            mediaType.addEncoding(encoding.name(), encodingObject);
        }

    }

    public static Type getSchemaType(io.swagger.v3.oas.annotations.media.Schema schema) {
        return getSchemaType(schema, false);
    }

    public static Type getSchemaType(io.swagger.v3.oas.annotations.media.Schema schema, boolean nullIfNotFound) {
        if (schema == null) {
            if (nullIfNotFound) {
                return null;
            }
            return String.class;
        }
        String schemaType = schema.type();
        String schemaFormat = schema.format();
        Class schemaImplementation = schema.implementation();

        if (!schemaImplementation.equals(Void.class)) {
            return schemaImplementation;
        } else if (StringUtils.isBlank(schemaType)) {
            if (nullIfNotFound) {
                return null;
            }
            return String.class;
        }
        switch (schemaType) {
            case "number":
                if ("float".equals(schemaFormat)) {
                    return Float.class;
                } else if ("double".equals(schemaFormat)) {
                    return Double.class;
                } else {
                    return BigDecimal.class;
                }
            case "integer":
                if ("int32".equals(schemaFormat)) {
                    return Integer.class;
                } else {
                    return Long.class;
                }
            case "boolean":
                return Boolean.class;
            case "string":
                return String.class;
            default:
                if (nullIfNotFound) {
                    return null;
                }
                return String.class;
        }
    }

    public static Optional<Content> getContent(io.swagger.v3.oas.annotations.media.Content[] annotationContents, String[] classTypes, String[] methodTypes, Schema schema, Components components, JsonView jsonViewAnnotation) {
        return getContent(annotationContents, classTypes, methodTypes, schema, components, jsonViewAnnotation, false);
    }

    public static Schema clone(Schema schema, boolean openapi31) {
        if (schema == null)
            return schema;
        try {
            String cloneName = schema.getName();
            if (openapi31) {
                schema = Json31.mapper().readValue(Json31.pretty(schema), Schema.class);
            } else {
                schema = Json.mapper().readValue(Json.pretty(schema), Schema.class);
            }
            schema.setName(cloneName);
        } catch (IOException e) {
            LOGGER.error("Could not clone schema", e);
        }
        return schema;
    }

    public static Optional<Content> getContent(io.swagger.v3.oas.annotations.media.Content[] annotationContents, String[] classTypes, String[] methodTypes, Schema schema, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        if (annotationContents == null || annotationContents.length == 0) {
            return Optional.empty();
        }

        //Encapsulating Content model
        Content content = new Content();

        for (io.swagger.v3.oas.annotations.media.Content annotationContent : annotationContents) {
            MediaType mediaType = new MediaType();
            if (components != null) {
                Optional annSchema = getSchema(annotationContent, components, jsonViewAnnotation, openapi31);
                if (annSchema.isPresent()) {
                    // mediaType.setSchema(clone((Schema)annSchema.get(), openapi31));
                    mediaType.setSchema((Schema) annSchema.get());
                }
                if (annotationContent.schemaProperties().length > 0) {
                    if (mediaType.getSchema() == null) {
                        mediaType.schema(openapi31 ? new JsonSchema().typesItem("object") : new Schema<Object>().type("object"));
                    }
                    Schema oSchema = mediaType.getSchema();
                    for (SchemaProperty sp : annotationContent.schemaProperties()) {
                        Class<?> schemaImplementation = sp.schema().implementation();
                        boolean isArray = false;
                        if (schemaImplementation == Void.class) {
                            schemaImplementation = sp.array().schema().implementation();
                            if (schemaImplementation != Void.class) {
                                isArray = true;
                            }
                        }
                        getSchema(sp.schema(), sp.array(), isArray, schemaImplementation, components, jsonViewAnnotation, openapi31)
                                .ifPresent(s -> {
                                    if ("array".equals(oSchema.getType())) {
                                        oSchema.getItems().addProperty(sp.name(), s);
                                    } else {
                                        oSchema.addProperty(sp.name(), s);
                                    }
                                });

                    }
                }
                Optional<Schema> arraySchemaResult = getArraySchema(annotationContent.additionalPropertiesArraySchema(), components, jsonViewAnnotation, openapi31, null, true);
                if (arraySchemaResult.isPresent()) {
                    if ("array".equals(mediaType.getSchema().getType())) {
                        mediaType.getSchema().getItems().additionalProperties(arraySchemaResult.get());
                    } else {
                        mediaType.getSchema().additionalProperties(arraySchemaResult.get());
                    }
                } else {
                    if (
                            hasSchemaAnnotation(annotationContent.additionalPropertiesSchema()) &&
                                    mediaType.getSchema() != null &&
                                    !Boolean.TRUE.equals(mediaType.getSchema().getAdditionalProperties()) &&
                                    !Boolean.FALSE.equals(mediaType.getSchema().getAdditionalProperties())) {
                        getSchemaFromAnnotation(annotationContent.additionalPropertiesSchema(), components, jsonViewAnnotation, openapi31)
                                .ifPresent(s -> {
                                            if ("array".equals(mediaType.getSchema().getType())) {
                                                mediaType.getSchema().getItems().additionalProperties(s);
                                            } else {
                                                mediaType.getSchema().additionalProperties(s);
                                            }
                                        }
                                );
                    }
                }
            } else {
                mediaType.setSchema(schema);
            }

            ExampleObject[] examples = annotationContent.examples();
            if (examples.length == 1 && StringUtils.isBlank(examples[0].name())) {
                getExample(examples[0], true).ifPresent(exampleObject -> mediaType.example(exampleObject.getValue()));
            } else {
                for (ExampleObject example : examples) {
                    getExample(example).ifPresent(exampleObject -> mediaType.addExamples(example.name(), exampleObject));
                }
            }
            if (annotationContent.extensions() != null && annotationContent.extensions().length > 0) {
                Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, annotationContent.extensions());
                if (extensions != null) {
                    if (openapi31) {
                        extensions.forEach(mediaType::addExtension31);
                    } else {
                        extensions.forEach(mediaType::addExtension);
                    }
                }
            }

            io.swagger.v3.oas.annotations.media.Encoding[] encodings = annotationContent.encoding();
            for (io.swagger.v3.oas.annotations.media.Encoding encoding : encodings) {
                addEncodingToMediaType(mediaType, encoding, jsonViewAnnotation, openapi31);
            }
            if (StringUtils.isNotBlank(annotationContent.mediaType())) {
                content.addMediaType(annotationContent.mediaType(), mediaType);
            } else {
                applyTypes(classTypes, methodTypes, content, mediaType);
            }

            DependentSchema[] dependentSchemas = annotationContent.dependentSchemas();
            if (dependentSchemas.length > 0) {
                final Map<String, Schema> dependentSchemaMap = new LinkedHashMap<>();
                for (DependentSchema dependentSchema : dependentSchemas) {
                    if ("array".equals(mediaType.getSchema().getType())) {
                        getArraySchema(dependentSchema.array(), components, jsonViewAnnotation, openapi31).ifPresent(arraySchema -> dependentSchemaMap.put(dependentSchema.name(), arraySchema));
                    } else {
                        getSchemaFromAnnotation(dependentSchema.schema(), components, jsonViewAnnotation, openapi31).ifPresent(schema1 -> dependentSchemaMap.put(dependentSchema.name(), schema1));
                    }
                }
                mediaType.getSchema().setDependentSchemas(dependentSchemaMap);
            }
            if (mediaType.getSchema() != null) {
                getSchemaFromAnnotation(annotationContent.contentSchema(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setContentSchema);
                getSchemaFromAnnotation(annotationContent.propertyNames(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setPropertyNames);
                getSchemaFromAnnotation(annotationContent._if(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setIf);
                getSchemaFromAnnotation(annotationContent._then(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setThen);
                getSchemaFromAnnotation(annotationContent._else(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setElse);
                getSchemaFromAnnotation(annotationContent.not(), components, jsonViewAnnotation, openapi31).ifPresent(mediaType.getSchema()::setNot);
            }
            if (annotationContent.oneOf().length > 0) {
                for (io.swagger.v3.oas.annotations.media.Schema oneOfSchema : annotationContent.oneOf()) {
                    getSchemaFromAnnotation(oneOfSchema, components, jsonViewAnnotation).ifPresent(mediaType.getSchema()::addOneOfItem);
                }
            }
            if (annotationContent.anyOf().length > 0) {
                for (io.swagger.v3.oas.annotations.media.Schema anyOfSchema : annotationContent.anyOf()) {
                    getSchemaFromAnnotation(anyOfSchema, components, jsonViewAnnotation).ifPresent(mediaType.getSchema()::addAnyOfItem);
                }
            }
            if (annotationContent.allOf().length > 0) {
                for (io.swagger.v3.oas.annotations.media.Schema anyOfSchema : annotationContent.allOf()) {
                    getSchemaFromAnnotation(anyOfSchema, components, jsonViewAnnotation).ifPresent(mediaType.getSchema()::addAllOfItem);
                }
            }
        }

        if (content.size() == 0) {
            return Optional.empty();
        }
        return Optional.of(content);
    }

    public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations.media.Content annotationContent, Components components, JsonView jsonViewAnnotation) {
        return getSchema(annotationContent, components, jsonViewAnnotation, false);
    }

    public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations.media.Content annotationContent, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
        Class<?> schemaImplementation = annotationContent.schema().implementation();
        boolean isArray = false;
        if (schemaImplementation == Void.class) {
            schemaImplementation = annotationContent.array().schema().implementation();
            if (schemaImplementation != Void.class) {
                isArray = true;
            }
        }
        return getSchema(annotationContent.schema(), annotationContent.array(), isArray, schemaImplementation, components, jsonViewAnnotation, openapi31);
    }

    public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations.media.Schema schemaAnnotation,
                                                       io.swagger.v3.oas.annotations.media.ArraySchema arrayAnnotation,
                                                       boolean isArray,
                                                       Class<?> schemaImplementation,
                                                       Components components,
                                                       JsonView jsonViewAnnotation) {
        return getSchema(schemaAnnotation, arrayAnnotation, isArray, schemaImplementation, components, jsonViewAnnotation, false);

    }
    public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations.media.Schema schemaAnnotation,
                                                       io.swagger.v3.oas.annotations.media.ArraySchema arrayAnnotation,
                                                       boolean isArray,
                                                       Class<?> schemaImplementation,
                                                       Components components,
                                                       JsonView jsonViewAnnotation,
                                                       boolean openapi31) {
        return getSchema(schemaAnnotation, arrayAnnotation, isArray, schemaImplementation, components, jsonViewAnnotation, openapi31, null);
    }

    public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations.media.Schema schemaAnnotation,
                                                       io.swagger.v3.oas.annotations.media.ArraySchema arrayAnnotation,
                                                       boolean isArray,
                                                       Class<?> schemaImplementation,
                                                       Components components,
                                                       JsonView jsonViewAnnotation,
                                                       boolean openapi31,
                                                       ModelConverterContext context) {
        if (schemaImplementation != Void.class) {
            Schema schemaObject = resolveSchemaFromType(schemaImplementation, components, jsonViewAnnotation, openapi31, schemaAnnotation, arrayAnnotation, context);
            if (StringUtils.isNotBlank(schemaAnnotation.format())) {
                schemaObject.setFormat(schemaAnnotation.format());
            }
            if (schemaAnnotation.allowableValues().length != 0) {
                schemaObject.setEnum(Arrays.asList(schemaAnnotation.allowableValues()));
            }
            if (isArray) {
                Optional<Schema> arraySchema = AnnotationsUtils.getArraySchema(arrayAnnotation, components, jsonViewAnnotation, openapi31, null, false, context);
                if (arraySchema.isPresent()) {
                    arraySchema.get().setItems(schemaObject);
                    return arraySchema;
                } else {
                    return Optional.empty();
                }
            } else {
                return Optional.of(schemaObject);
            }

        } else {
            Optional<Schema> schemaFromAnnotation = AnnotationsUtils.getSchemaFromAnnotation(schemaAnnotation, components, jsonViewAnnotation, openapi31, null, context);
            if (schemaFromAnnotation.isPresent()) {
                if (StringUtils.isBlank(schemaFromAnnotation.get().get$ref()) && StringUtils.isBlank(schemaFromAnnotation.get().getType()) && !(schemaFromAnnotation.get() instanceof ComposedSchema)) {
                    // default to string
                    schemaFromAnnotation.get().setType("string");
                }
                return Optional.of(schemaFromAnnotation.get());
            } else {
                Optional<Schema> arraySchemaFromAnnotation = AnnotationsUtils.getArraySchema(arrayAnnotation, components, jsonViewAnnotation, openapi31, null, false, context);
                if (arraySchemaFromAnnotation.isPresent()) {
                    if (arraySchemaFromAnnotation.get().getItems() != null && StringUtils.isBlank(arraySchemaFromAnnotation.get().getItems().get$ref()) && StringUtils.isBlank(arraySchemaFromAnnotation.get().getItems().getType())) {
                        // default to string
                        arraySchemaFromAnnotation.get().getItems().setType("string");
                    }
                    return Optional.of(arraySchemaFromAnnotation.get());
                }
            }
        }
        return Optional.empty();
    }


    public static void applyTypes(String[] classTypes, String[] methodTypes, Content content, MediaType mediaType) {
        if (methodTypes != null && methodTypes.length > 0) {
            for (String value : methodTypes) {
                content.addMediaType(value, mediaType);
            }
        } else if (classTypes != null && classTypes.length > 0) {
            for (String value : classTypes) {
                content.addMediaType(value, mediaType);
            }
        } else {
            content.addMediaType(ParameterProcessor.MEDIA_TYPE, mediaType);
        }

    }

    public static io.swagger.v3.oas.annotations.media.Schema getSchemaAnnotation(Annotated a) {
        if (a == null) {
            return null;
        }
        io.swagger.v3.oas.annotations.media.ArraySchema arraySchema = a.getAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema.class);
        if (arraySchema != null) {
            return arraySchema.schema();
        } else {
            return a.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        }
    }

    public static io.swagger.v3.oas.annotations.media.Schema getSchemaDeclaredAnnotation(Annotated a) {
        if (a == null) {
            return null;
        }
        io.swagger.v3.oas.annotations.media.ArraySchema arraySchema = a.getRawType().getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema.class);
        if (arraySchema != null) {
            return arraySchema.schema();
        } else {
            return a.getRawType().getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        }
    }

    public static io.swagger.v3.oas.annotations.media.Schema getSchemaAnnotation(Class<?> cls) {
        if (cls == null) {
            return null;
        }
        io.swagger.v3.oas.annotations.media.Schema mp = null;
        io.swagger.v3.oas.annotations.media.ArraySchema as = cls.getAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema.class);
        if (as != null) {
            mp = as.schema();
        } else {
            mp = cls.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        }
        return mp;
    }

    public static io.swagger.v3.oas.annotations.media.Schema getSchemaDeclaredAnnotation(Class<?> cls) {
        if (cls == null) {
            return null;
        }
        io.swagger.v3.oas.annotations.media.Schema mp = null;
        io.swagger.v3.oas.annotations.media.ArraySchema as = cls.getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema.class);
        if (as != null) {
            mp = as.schema();
        } else {
            mp = cls.getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        }
        return mp;
    }

    public static Map<String, Object> getExtensions(Extension... extensions) {
        return getExtensions(false, true, extensions);
    }

    public static Map<String, Object> getExtensions(boolean openapi31, Extension... extensions) {
        return getExtensions(openapi31, true, extensions);
    }

    public static Map<String, Object> getExtensions(boolean openapi31, boolean usePrefix, Extension... extensions) {
        final Map<String, Object> map = new HashMap<>();
        for (Extension extension : extensions) {
            final String name = extension.name();
            String decoratedName = usePrefix
                    ? StringUtils.prependIfMissing(name, "x-")
                    : name;
            final String key = name.isEmpty() ? name : decoratedName;

            for (ExtensionProperty property : extension.properties()) {
                final String propertyName = property.name();
                final String propertyValue = property.value();
                JsonNode processedValue;
                final boolean propertyAsJson = property.parseValue();
                if (StringUtils.isNotBlank(propertyName) && StringUtils.isNotBlank(propertyValue)) {
                    if (key.isEmpty()) {
                        if (propertyAsJson) {
                            try {
                                if (openapi31) {
                                    processedValue = Json31.mapper().readTree(propertyValue);
                                } else {
                                    processedValue = Json.mapper().readTree(propertyValue);
                                }
                                decoratedName = usePrefix ? StringUtils.prependIfMissing(propertyName, "x-") : propertyName;
                                map.put(decoratedName, processedValue);
                            } catch (Exception e) {
                                decoratedName = usePrefix ? StringUtils.prependIfMissing(propertyName, "x-") : propertyName;
                                map.put(decoratedName, propertyValue);
                            }
                        } else {
                            decoratedName = usePrefix ? StringUtils.prependIfMissing(propertyName, "x-") : propertyName;
                            map.put(decoratedName, propertyValue);
                        }
                    } else {
                        Object value = map.get(key);
                        if (!(value instanceof Map)) {
                            value = new HashMap<String, Object>();
                            map.put(key, value);
                        }
                        @SuppressWarnings("unchecked") final Map<String, Object> mapValue = (Map<String, Object>) value;
                        if (propertyAsJson) {
                            try {
                                if (openapi31) {
                                    processedValue = Json31.mapper().readTree(propertyValue);
                                } else {
                                    processedValue = Json.mapper().readTree(propertyValue);
                                }
                                mapValue.put(propertyName, processedValue);
                            } catch (Exception e) {
                                mapValue.put(propertyName, propertyValue);
                            }
                        } else {
                            mapValue.put(propertyName, propertyValue);
                        }
                    }
                }
            }
        }

        return map;
    }

    public static io.swagger.v3.oas.annotations.media.Schema getSchemaAnnotation(Annotation... annotations) {
        if (annotations == null) {
            return null;
        }
        for (Annotation annotation : annotations) {
            if (annotation instanceof io.swagger.v3.oas.annotations.media.Schema) {
                return (io.swagger.v3.oas.annotations.media.Schema) annotation;
            }
        }
        return null;
    }

    /**
     * Looks up the registered schema name for an implementation class in the
     * given {@link ModelConverterContext}.
     * <p>
     * Prefers an explicit {@code @Schema(name = "...")} declaration on the
     * class, then the type-name resolved by the underlying {@code ObjectMapper}
     * (when available), and finally the simple class name. Returns {@code null}
     * if no matching schema is registered.
     * <p>
     * Used to recover a usable {@code $ref} when array-schema processing is
     * suppressed by a cycle guard (gh-5187).
     */
    static String findRegisteredSchemaName(Class<?> impl, ModelConverterContext context) {
        if (impl == null || impl.equals(Void.class) || context == null) {
            return null;
        }
        Map<String, Schema> defined = context.getDefinedModels();
        if (defined == null || defined.isEmpty()) {
            return null;
        }
        io.swagger.v3.oas.annotations.media.Schema annotated = impl.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        if (annotated != null && StringUtils.isNotBlank(annotated.name()) && defined.containsKey(annotated.name())) {
            return annotated.name();
        }
        String simpleName = impl.getSimpleName();
        if (StringUtils.isNotBlank(simpleName) && defined.containsKey(simpleName)) {
            return simpleName;
        }
        return null;
    }

    public static io.swagger.v3.oas.annotations.media.ArraySchema getArraySchemaAnnotation(Annotation... annotations) {
        if (annotations == null) {
            return null;
        }
        for (Annotation annotation : annotations) {
            if (annotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema) {
                return (io.swagger.v3.oas.annotations.media.ArraySchema) annotation;
            }
        }
        return null;
    }

    public static <T> T getAnnotation(Class<T> cls, Annotation... annotations) {
        if (annotations == null) {
            return null;
        }
        for (Annotation annotation : annotations) {
            if (cls.isAssignableFrom(annotation.getClass())) {
                return (T) annotation;
            }
        }
        return null;
    }

    public static Annotation[] removeAnnotations(Annotation[] annotations, Class... classes) {
        if (annotations == null) {
            return null;
        }
        List<Annotation> result = new ArrayList<>();
        for (Annotation annotation : annotations) {
            boolean found = false;
            for (Class cls : classes) {
                if (cls.isAssignableFrom(annotation.getClass())) {
                    found = true;
                }
            }
            if (!found) {
                result.add(annotation);
            }
        }
        return result.toArray(new Annotation[result.size()]);
    }


    public static void updateAnnotation(Class<?> clazz, io.swagger.v3.oas.annotations.media.Schema newAnnotation) {
        try {
            Field field = Class.class.getDeclaredField("annotations");
            field.setAccessible(true);
            Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(clazz);
            annotations.put(io.swagger.v3.oas.annotations.media.Schema.class, newAnnotation);
        } catch (NoSuchFieldException e) {
            //
        } catch (IllegalAccessException e) {
            //
        }

    }

    public static Annotation mergeSchemaAnnotations(
            Annotation[] ctxAnnotations, JavaType type) {
        return mergeSchemaAnnotations(ctxAnnotations, type, false);
    }

    /*
     * returns null if no annotations, otherwise either ArraySchema or Schema
     */
    public static Annotation mergeSchemaAnnotations(
            Annotation[] ctxAnnotations, JavaType type, boolean contextWins) {
        // get type array and schema
        io.swagger.v3.oas.annotations.media.Schema tS = type.getRawClass().getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
        if (!hasSchemaAnnotation(tS)) {
            tS = null;
        }
        io.swagger.v3.oas.annotations.media.ArraySchema tA = type.getRawClass().getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.ArraySchema.class);
        if (!hasArrayAnnotation(tA)) {
            tA = null;
        }

        io.swagger.v3.oas.annotations.media.Schema tAs = tA == null ? null : tA.schema();
        if (!hasSchemaAnnotation(tAs)) {
            tAs = null;
        }
        // get ctx array and schema
        io.swagger.v3.oas.annotations.media.Schema cS = getSchemaAnnotation(ctxAnnotations);
        if (!hasSchemaAnnotation(cS)) {
            cS = null;
        }
        io.swagger.v3.oas.annotations.media.ArraySchema cA = getArraySchemaAnnotation(ctxAnnotations);
        if (!hasArrayAnnotation(cA)) {
            cA = null;
        }
        io.swagger.v3.oas.annotations.media.Schema cAs = cA == null ? null : cA.schema();
        if (!hasSchemaAnnotation(cAs)) {
            cAs = null;
        }

        if (tS == null && tA == null && cS == null && cA == null) {
            return null;
        } else if (tS == null && tA == null && cS != null) {
            return cS;
        } else if (tS == null && tA == null && cS == null && cA != null) {
            return cA;
        } else if (tS == null && tA != null && cS == null && cA == null) {
            return tA;
        } else if (tS == null && tA != null && cS != null && cA == null) {
            if (tAs != null) {
                return tA;
            }
            return mergeArrayWithSchemaAnnotation(tA, cS);
        } else if (tS != null && tA == null && cS == null && cA != null) {
            if (cAs != null) {
                return cA;
            }
            return mergeArrayWithSchemaAnnotation(cA, tS);
        } else if (tA != null && cA != null) {
            if (contextWins) {
                return mergeArraySchemaAnnotations(tA, cA);
            }
            return mergeArraySchemaAnnotations(cA, tA);
        } else if (tS != null && cS == null && cA == null) {
            return tS;
        } else if (tS != null && cS != null) {
            if (contextWins) {
                return mergeSchemaAnnotations(cS, tS);
            }
            return mergeSchemaAnnotations(tS, cS);
        } else {
            return tS;
        }


    }

    public static io.swagger.v3.oas.annotations.media.Schema mergeSchemaAnnotations(
            io.swagger.v3.oas.annotations.media.Schema master,
            io.swagger.v3.oas.annotations.media.Schema patch) {
        if (master == null) {
            return patch;
        } else if (patch == null) {
            return master;
        } else if (!hasSchemaAnnotation(patch)) {
            return master;
        }
        Annotation schema = new io.swagger.v3.oas.annotations.media.Schema() {
            @Override
            public Class<?> implementation() {
                if (!master.implementation().equals(Void.class) || patch.implementation().equals(Void.class)) {
                    return master.implementation();
                }
                return patch.implementation();
            }

            @Override
            public Class<?> not() {
                if (!master.not().equals(Void.class) || patch.not().equals(Void.class)) {
                    return master.not();
                }
                return patch.not();
            }

            @Override
            public Class<?>[] oneOf() {
                if (master.oneOf().length > 0 || patch.oneOf().length == 0) {
                    return master.oneOf();
                }
                return patch.oneOf();
            }

            @Override
            public Class<?>[] anyOf() {
                if (master.anyOf().length > 0 || patch.anyOf().length == 0) {
                    return master.anyOf();
                }
                return patch.anyOf();
            }

            @Override
            public Class<?>[] allOf() {
                if (master.allOf().length > 0 || patch.allOf().length == 0) {
                    return master.allOf();
                }
                return patch.allOf();
            }

            @Override
            public String name() {
                if (StringUtils.isNotBlank(master.name()) || StringUtils.isBlank(patch.name())) {
                    return master.name();
                }
                return patch.name();
            }

            @Override
            public String title() {
                if (StringUtils.isNotBlank(master.title()) || StringUtils.isBlank(patch.title())) {
                    return master.title();
                }
                return patch.title();
            }

            @Override
            public double multipleOf() {
                if (master.multipleOf() != 0 || patch.multipleOf() == 0) {
                    return master.multipleOf();
                }
                return patch.multipleOf();

            }

            @Override
            public String maximum() {
                if (StringUtils.isNotBlank(master.maximum()) || StringUtils.isBlank(patch.maximum())) {
                    return master.maximum();
                }
                return patch.maximum();
            }

            @Override
            public boolean exclusiveMaximum() {
                if (master.exclusiveMaximum() || !patch.exclusiveMaximum()) {
                    return master.exclusiveMaximum();
                }
                return patch.exclusiveMaximum();
            }

            @Override
            public String minimum() {
                if (StringUtils.isNotBlank(master.minimum()) || StringUtils.isBlank(patch.minimum())) {
                    return master.minimum();
                }
                return patch.minimum();
            }

            @Override
            public boolean exclusiveMinimum() {
                if (master.exclusiveMinimum() || !patch.exclusiveMinimum()) {
                    return master.exclusiveMinimum();
                }
                return patch.exclusiveMinimum();
            }

            @Override
            public int maxLength() {
                if ((master.maxLength() != Integer.MAX_VALUE && master.maxLength() > 0) ||
                        (patch.maxLength() == Integer.MAX_VALUE || patch.maxLength() == 0)) {
                    return master.maxLength();
                }
                return patch.maxLength();
            }

            @Override
            public int minLength() {
                if (master.minLength() != 0 || patch.minLength() == 0) {
                    return master.minLength();
                }
                return patch.minLength();
            }

            @Override
            public String pattern() {
                if (StringUtils.isNotBlank(master.pattern()) || StringUtils.isBlank(patch.pattern())) {
                    return master.pattern();
                }
                return patch.pattern();
            }

            @Override
            public int maxProperties() {
                if (master.maxProperties() != 0 || patch.maxProperties() == 0) {
                    return master.maxProperties();
                }
                return patch.maxProperties();
            }

            @Override
            public int minProperties() {
                if (master.minProperties() != 0 || patch.minProperties() == 0) {
                    return master.minProperties();
                }
                return patch.minProperties();
            }

            @Override
            public String[] requiredProperties() {
                if (master.requiredProperties().length > 0 || patch.requiredProperties().length == 0) {
                    return master.requiredProperties();
                }
                return patch.requiredProperties();
            }

            @Override
            public boolean required() {
                if (master.required() || !patch.required()) {
                    return master.required();
                }
                return patch.required();
            }

            @Override
            public RequiredMode requiredMode() {
                if (!master.requiredMode().equals(RequiredMode.AUTO) || patch.requiredMode().equals(RequiredMode.AUTO)) {
                    return master.requiredMode();
                }
                return patch.requiredMode();
            }

            @Override
            public String description() {
                if (StringUtils.isNotBlank(master.description()) || StringUtils.isBlank(patch.description())) {
                    return master.description();
                }
                return patch.description();
            }

            @Override
            public String format() {
                if (StringUtils.isNotBlank(master.format()) || StringUtils.isBlank(patch.format())) {
                    return master.format();
                }
                return patch.format();
            }

            @Override
            public String ref() {
                if (StringUtils.isNotBlank(master.ref()) || StringUtils.isBlank(patch.ref())) {
                    return master.ref();
                }
                return patch.ref();
            }

            @Override
            public boolean nullable() {
                if (master.nullable() || !patch.nullable()) {
                    return master.nullable();
                }
                return patch.nullable();
            }

            @Override
            public boolean readOnly() {
                if (master.readOnly() || !patch.readOnly()) {
                    return master.readOnly();
                }
                return patch.readOnly();
            }

            @Override
            public boolean writeOnly() {
                if (master.writeOnly() || !patch.writeOnly()) {
                    return master.writeOnly();
                }
                return patch.writeOnly();
            }

            @Override
            public AccessMode accessMode() {
                if (!master.accessMode().equals(AccessMode.AUTO) || patch.accessMode().equals(AccessMode.AUTO)) {
                    return master.accessMode();
                }
                return patch.accessMode();
            }

            @Override
            public String example() {
                if (StringUtils.isNotBlank(master.example()) || StringUtils.isBlank(patch.example())) {
                    return master.example();
                }
                return patch.example();
            }

            @Override
            public io.swagger.v3.oas.annotations.ExternalDocumentation externalDocs() {
                if (getExternalDocumentation(master.externalDocs()).isPresent() || !getExternalDocumentation(patch.externalDocs()).isPresent()) {
                    return master.externalDocs();
                }
                return patch.externalDocs();
            }

            @Override
            public boolean deprecated() {
                if (master.deprecated() || !patch.deprecated()) {
                    return master.deprecated();
                }
                return patch.deprecated();
            }

            @Override
            public String type() {
                if (StringUtils.isNotBlank(master.type()) || StringUtils.isBlank(patch.type())) {
                    return master.type();
                }
                return patch.type();
            }

            @Override
            public String[] allowableValues() {
                if (master.allowableValues().length > 0 || patch.allowableValues().length == 0) {
                    return master.allowableValues();
                }
                return patch.allowableValues();
            }

            @Override
            public String defaultValue() {
                if (StringUtils.isNotBlank(master.defaultValue()) || StringUtils.isBlank(patch.defaultValue())) {
                    return master.defaultValue();
                }
                return patch.defaultValue();
            }

            @Override
            public String discriminatorProperty() {
                if (StringUtils.isNotBlank(master.discriminatorProperty()) || StringUtils.isBlank(patch.discriminatorProperty())) {
                    return master.discriminatorProperty();
                }
                return patch.discriminatorProperty();
            }

            @Override
            public DiscriminatorMapping[] discriminatorMapping() {
                if (master.discriminatorMapping().length > 0 || patch.discriminatorMapping().length == 0) {
                    return master.discriminatorMapping();
                }
                return patch.discriminatorMapping();
            }

            @Override
            public boolean hidden() {
                if (master.hidden() || !patch.hidden()) {
                    return master.hidden();
                }
                return patch.hidden();
            }

            @Override
            public boolean enumAsRef() {
                if (master.enumAsRef() || !patch.enumAsRef()) {
                    return master.enumAsRef();
                }
                return patch.enumAsRef();
            }

            @Override
            public Class<?>[] subTypes() {
                if (master.subTypes().length > 0 || patch.subTypes().length == 0) {
                    return master.subTypes();
                }
                return patch.subTypes();
            }

            @Override
            public Extension[] extensions() {
                if (master.extensions().length > 0 || patch.extensions().length == 0) {
                    return master.extensions();
                }
                return patch.extensions();
            }

            @Override
            public Class<?>[] prefixItems() {
                if (master.prefixItems().length > 0 || patch.prefixItems().length == 0) {
                    return master.prefixItems();
                }
                return patch.prefixItems();
            }

            @Override
            public String[] types() {
                if (master.types().length > 0 || patch.types().length == 0) {
                    return master.types();
                }
                return patch.types();
            }

            @Override
            public int exclusiveMaximumValue() {
                if (master.exclusiveMaximumValue() != 0 || patch.exclusiveMaximumValue() == 0) {
                    return master.exclusiveMaximumValue();
                }
                return patch.exclusiveMaximumValue();
            }

            @Override
            public int exclusiveMinimumValue() {
                if (master.exclusiveMinimumValue() != 0 || patch.exclusiveMinimumValue() == 0) {
                    return master.exclusiveMaximumValue();
                }
                return patch.exclusiveMinimumValue();
            }

            @Override
            public Class<?> contains() {
                if (!master.contains().equals(Void.class) || patch.contains().equals(Void.class)) {
                    return master.contains();
                }
                return patch.contains();
            }

            @Override
            public String $id() {
                if (StringUtils.isNotBlank(master.$id()) || StringUtils.isBlank(patch.$id())) {
                    return master.$id();
                }
                return patch.$id();
            }

            @Override
            public String $schema() {
                if (StringUtils.isNotBlank(master.$schema()) || StringUtils.isBlank(patch.$schema())) {
                    return master.$schema();
                }
                return patch.$schema();
            }

            @Override
            public String $anchor() {
                if (StringUtils.isNotBlank(master.$anchor()) || StringUtils.isBlank(patch.$anchor())) {
                    return master.$anchor();
                }
                return patch.$anchor();
            }

            @Override
            public String $vocabulary() {
                if (StringUtils.isNotBlank(master.$vocabulary()) || StringUtils.isBlank(patch.$vocabulary())) {
                    return master.$vocabulary();
                }
                return patch.$vocabulary();
            }

            @Override
            public String $dynamicAnchor() {
                if (StringUtils.isNotBlank(master.$dynamicAnchor()) || StringUtils.isBlank(patch.$dynamicAnchor())) {
                    return master.$dynamicAnchor();
                }
                return patch.$dynamicAnchor();
            }

            @Override
            public String $dynamicRef() {
                if (StringUtils.isNotBlank(master.$dynamicRef()) || StringUtils.isBlank(patch.$dynamicRef())) {
                    return master.$dynamicRef();
                }
                return patch.$dynamicRef();
            }

            @Override
            public String contentEncoding() {
                if (StringUtils.isNotBlank(master.contentEncoding()) || StringUtils.isBlank(patch.contentEncoding())) {
                    return master.contentEncoding();
                }
                return patch.contentEncoding();
            }

            @Override
            public String contentMediaType() {
                if (StringUtils.isNotBlank(master.contentMediaType()) || StringUtils.isBlank(patch.contentMediaType())) {
                    return master.contentMediaType();
                }
                return patch.contentMediaType();
            }

            @Override
            public Class<?> contentSchema() {
                if (!master.contentSchema().equals(Void.class) || patch.contentSchema().equals(Void.class)) {
                    return master.contentSchema();
                }
                return patch.contentSchema();
            }

            @Override
            public Class<?> propertyNames() {
                if (!master.propertyNames().equals(Void.class) || patch.propertyNames().equals(Void.class)) {
                    return master.propertyNames();
                }
                return patch.propertyNames();
            }

            @Override
            public int maxContains() {
                if (master.maxContains() != 0 || patch.maxContains() == 0) {
                    return master.maxContains();
                }
                return patch.maxContains();
            }

            @Override
            public int minContains() {
                if (master.minContains() != 0 || patch.minContains() == 0) {
                    return master.minContains();
                }
                return patch.minContains();
            }

            @Override
            public Class<?> additionalItems() {
                if (!master.additionalItems().equals(Void.class) || patch.additionalItems().equals(Void.class)) {
                    return master.additionalItems();
                }
                return patch.additionalItems();
            }

            @Override
            public Class<?> unevaluatedItems() {
                if (!master.unevaluatedItems().equals(Void.class) || patch.unevaluatedItems().equals(Void.class)) {
                    return master.unevaluatedItems();
                }
                return patch.unevaluatedItems();
            }

            @Override
            public Class<?> _if() {
                if (!master._if().equals(Void.class) || patch._if().equals(Void.class)) {
                    return master._if();
                }
                return patch._if();
            }

            @Override
            public Class<?> _else() {
                if (!master._else().equals(Void.class) || patch._else().equals(Void.class)) {
                    return master._else();
                }
                return patch._else();
            }

            @Override
            public Class<?> then() {
                if (!master.then().equals(Void.class) || patch.then().equals(Void.class)) {
                    return master.then();
                }
                return patch.then();
            }

            @Override
            public String $comment() {
                if (StringUtils.isNotBlank(master.$comment()) || StringUtils.isBlank(patch.$comment())) {
                    return master.$comment();
                }
                return patch.$comment();
            }

            @Override
            public String[] examples() {
                if (master.examples().length > 0 || patch.examples().length == 0) {
                    return master.examples();
                }
                return patch.examples();
            }

            @Override
            public Class[] exampleClasses() {
                if (master.exampleClasses().length > 0 || patch.exampleClasses().length == 0) {
                    return master.exampleClasses();
                }
                return patch.exampleClasses();
            }

            @Override
            public String _const() {
                if (!master._const().equals(Void.class) || patch._const().equals(Void.class)) {
                    return master._const();
                }
                return patch._const();
            }

            @Override
            public Class<? extends Annotation> annotationType() {
                return io.swagger.v3.oas.annotations.media.Schema.class;
            }

            @Override
            public AdditionalPropertiesValue additionalProperties() {
                if (!master.additionalProperties().equals(AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION) || patch.additionalProperties().equals(AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION)) {
                    return master.additionalProperties();
                }
                return patch.additionalProperties();
            }

            @Override
            public DependentRequired[] dependentRequiredMap() {
                if (master.dependentRequiredMap().length > 0 || patch.dependentRequiredMap().length == 0) {
                    return master.dependentRequiredMap();
                }
                return patch.dependentRequiredMap();
            }

            @Override
            public StringToClassMapItem[] dependentSchemas() {
                if (master.dependentSchemas().length > 0 || patch.dependentSchemas().length == 0) {
                    return master.dependentSchemas();
                }
                return patch.dependentSchemas();
            }

            @Override
            public StringToClassMapItem[] patternProperties() {
                if (master.patternProperties().length > 0 || patch.patternProperties().length == 0) {
                    return master.patternProperties();
                }
                return patch.patternProperties();
            }

            @Override
            public StringToClassMapItem[] properties() {
                if (master.properties().length > 0 || patch.properties().length == 0) {
                    return master.properties();
                }
                return patch.properties();
            }

            @Override
            public Class<?> unevaluatedProperties() {
                if (!master.unevaluatedProperties().equals(Void.class) || patch.unevaluatedProperties().equals(Void.class)) {
                    return master.unevaluatedProperties();
                }
                return patch.unevaluatedProperties();
            }

            @Override
            public Class<?> additionalPropertiesSchema() {
                if (!master.additionalPropertiesSchema().equals(Void.class) || patch.additionalPropertiesSchema().equals(Void.class)) {
                    return master.additionalPropertiesSchema();
                }
                return patch.additionalPropertiesSchema();
            }

            /* We always want the patch to take precedence in schema resolution behavior */
            @Override
            public SchemaResolution schemaResolution() {
                if (!patch.schemaResolution().equals(SchemaResolution.DEFAULT) || master.schemaResolution().equals(SchemaResolution.DEFAULT)) {
                    return patch.schemaResolution();
                }
                return master.schemaResolution();
            }

        };

        return (io.swagger.v3.oas.annotations.media.Schema) schema;
    }

    public static io.swagger.v3.oas.annotations.media.ArraySchema mergeArraySchemaAnnotations(
            io.swagger.v3.oas.annotations.media.ArraySchema master,
            io.swagger.v3.oas.annotations.media.ArraySchema patch) {
        if (master == null) {
            return patch;
        } else if (patch == null) {
            return master;
        } else if (!hasArrayAnnotation(patch)) {
            return master;
        }
        Annotation newArraySchema = new io.swagger.v3.oas.annotations.media.ArraySchema() {

            @Override
            public Class<? extends Annotation> annotationType() {
                return io.swagger.v3.oas.annotations.media.ArraySchema.class;
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema schema() {
                io.swagger.v3.oas.annotations.media.Schema patchSchema = patch.schema();
                if (!hasSchemaAnnotation(patchSchema)) {
                    patchSchema = null;
                }

                if (patchSchema == null) {
                    return master.schema();
                } else {
                    return mergeSchemaAnnotations(master.schema(), patch.schema());
                }
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema arraySchema() {
                io.swagger.v3.oas.annotations.media.Schema patchSchema = patch.arraySchema();
                if (!hasSchemaAnnotation(patchSchema)) {
                    patchSchema = null;
                }

                if (patchSchema == null) {
                    return master.arraySchema();
                } else {
                    return mergeSchemaAnnotations(master.arraySchema(), patch.arraySchema());
                }
            }

            @Override
            public int maxItems() {
                if (master.maxItems() != 0 || patch.maxItems() == 0) {
                    return master.maxItems();
                }
                return patch.maxItems();
            }

            @Override
            public int minItems() {
                if (master.minItems() != 0 || patch.minItems() == 0) {
                    return master.minItems();
                }
                return patch.minItems();
            }

            @Override
            public boolean uniqueItems() {
                if (master.uniqueItems() || !patch.uniqueItems()) {
                    return master.uniqueItems();
                }
                return patch.uniqueItems();
            }

            @Override
            public Extension[] extensions() {
                if (master.extensions().length > 0 || patch.extensions().length == 0) {
                    return master.extensions();
                }
                return patch.extensions();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema[] prefixItems() {
                if (master.prefixItems().length > 0 || patch.prefixItems().length == 0) {
                    return master.prefixItems();
                }
                return patch.prefixItems();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema contains() {
                if (!master.contains().equals(Void.class) || patch.contains().equals(Void.class)) {
                    return master.contains();
                }
                return patch.contains();
            }

            @Override
            public int maxContains() {
                if (master.maxContains() > 0) {
                    return master.maxContains();
                }
                return patch.maxContains();
            }

            @Override
            public int minContains() {
                if (master.minContains() > 0) {
                    return master.minContains();
                }
                return patch.minContains();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema unevaluatedItems() {
                if (!master.unevaluatedItems().equals(Void.class) || patch.unevaluatedItems().equals(Void.class)) {
                    return master.unevaluatedItems();
                }
                return patch.unevaluatedItems();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema items() {
                if (!master.items().equals(Void.class) || patch.items().equals(Void.class)) {
                    return master.items();
                }
                return patch.items();
            }


        };

        return (io.swagger.v3.oas.annotations.media.ArraySchema) newArraySchema;
    }

    public static io.swagger.v3.oas.annotations.media.ArraySchema mergeArrayWithSchemaAnnotation(
            io.swagger.v3.oas.annotations.media.ArraySchema arraySchema,
            io.swagger.v3.oas.annotations.media.Schema schema) {
        if (arraySchema == null || schema == null) {
            return arraySchema;
        }
        if (!hasSchemaAnnotation(schema)) {
            return arraySchema;
        }
        Annotation newArraySchema = new io.swagger.v3.oas.annotations.media.ArraySchema() {

            @Override
            public Class<? extends Annotation> annotationType() {
                return io.swagger.v3.oas.annotations.media.ArraySchema.class;
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema items() {
                return arraySchema.items();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema schema() {
                return schema;
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema arraySchema() {
                return arraySchema.arraySchema();
            }

            @Override
            public int maxItems() {
                return arraySchema.maxItems();
            }

            @Override
            public int minItems() {
                return arraySchema.minItems();
            }

            @Override
            public boolean uniqueItems() {
                return arraySchema.uniqueItems();
            }

            @Override
            public Extension[] extensions() {
                return arraySchema.extensions();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema contains() {
                return arraySchema.contains();
            }

            @Override
            public int maxContains() {
                return arraySchema.maxContains();
            }

            @Override
            public int minContains() {
                return arraySchema.minContains();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema unevaluatedItems() {
                return arraySchema.unevaluatedItems();
            }

            @Override
            public io.swagger.v3.oas.annotations.media.Schema[] prefixItems() {
                return arraySchema.prefixItems();
            }
        };

        return (io.swagger.v3.oas.annotations.media.ArraySchema) newArraySchema;
    }

    public static Schema.SchemaResolution resolveSchemaResolution(Schema.SchemaResolution globalSchemaResolution, io.swagger.v3.oas.annotations.media.Schema schemaAnnotation) {
        if (schemaAnnotation != null && !io.swagger.v3.oas.annotations.media.Schema.SchemaResolution.AUTO.equals(schemaAnnotation.schemaResolution())) {
            return Schema.SchemaResolution.valueOf(schemaAnnotation.schemaResolution().toString());
        }
        return globalSchemaResolution;
    }

    public static boolean computeEnumAsRef(io.swagger.v3.oas.annotations.media.Schema ctxSchema, io.swagger.v3.oas.annotations.media.ArraySchema ctxArraySchema) {
        if (ctxSchema != null && ctxSchema.enumAsRef()) {
            return ctxSchema.enumAsRef();
        } else if (ctxArraySchema != null && ctxArraySchema.schema() != null && ctxArraySchema.schema().enumAsRef()) {
            return ctxArraySchema.schema().enumAsRef();
        }
        return false;
    }

    public static boolean areSiblingsAllowed(Schema.SchemaResolution resolvedSchemaResolution, boolean openapi31) {
        return Schema.SchemaResolution.ALL_OF.equals(resolvedSchemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(resolvedSchemaResolution) || openapi31;
    }

    public static AnnotatedType addTypeWhenSiblingsAllowed(AnnotatedType aType, io.swagger.v3.oas.annotations.media.Schema ctxSchema, boolean areSiblingsAllowed) {
        if (areSiblingsAllowed && ctxSchema != null) {
            if (!Void.class.equals(ctxSchema.implementation())) {
                aType.setType(ctxSchema.implementation());
            } else if (StringUtils.isNotBlank(ctxSchema.type())) {
                aType.setType(ctxSchema.type().getClass());
            }
        }
        return aType;
    }
}