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

io.swagger.v3.parser.util.OpenAPIDeserializer Maven / Gradle / Ivy

package io.swagger.v3.parser.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.callbacks.Callback;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ByteArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.DateSchema;
import io.swagger.v3.oas.models.media.DateTimeSchema;
import io.swagger.v3.oas.models.media.Discriminator;
import io.swagger.v3.oas.models.media.Encoding;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.XML;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.tags.Tag;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.parameters.CookieParameter;
import io.swagger.v3.oas.models.parameters.HeaderParameter;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum;
import io.swagger.v3.oas.models.parameters.PathParameter;
import io.swagger.v3.oas.models.parameters.QueryParameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityRequirement;
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.parser.core.models.SwaggerParseResult;
import io.swagger.v3.core.util.Json;

import org.apache.commons.lang3.StringUtils;

import static io.swagger.v3.core.util.RefUtils.extractSimpleName;

import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Calendar.*;


public class OpenAPIDeserializer {

    protected static Set ROOT_KEYS = new LinkedHashSet<>(Arrays.asList("openapi", "info", "servers", "paths", "components", "security", "tags",  "externalDocs"));
    protected static Set INFO_KEYS = new LinkedHashSet<>(Arrays.asList("title", "description", "termsOfService", "contact", "license", "version"));
    protected static Set CONTACT_KEYS = new LinkedHashSet<>(Arrays.asList("name", "url", "email"));
    protected static Set LICENSE_KEYS = new LinkedHashSet<>(Arrays.asList("name", "url"));
    protected static Set TAG_KEYS = new LinkedHashSet<>(Arrays.asList("description", "name", "externalDocs"));
    protected static Set RESPONSE_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "description", "headers", "content", "links"));
    protected static Set SERVER_KEYS = new LinkedHashSet<>(Arrays.asList( "url", "description", "variables"));
    protected static Set SERVER_VARIABLE_KEYS = new LinkedHashSet<>(Arrays.asList( "enum", "default", "description"));
    protected static Set PATHITEM_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "summary", "description", "get", "put", "post", "delete", "head", "patch", "options", "trace", "servers", "parameters"));
    protected static Set OPERATION_KEYS = new LinkedHashSet<>(Arrays.asList("tags", "summary", "description", "externalDocs", "operationId", "parameters", "requestBody", "responses", "callbacks", "deprecated" ,  "security", "servers"));
    protected static Set PARAMETER_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "name", "in", "description", "required", "deprecated", "allowEmptyValue", "style", "explode", "allowReserved", "schema", "example", "examples", "content"));
    protected static Set REQUEST_BODY_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "description", "content", "required"));
    protected static Set SECURITY_SCHEME_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "type", "name", "in", "description", "flows", "scheme", "bearerFormat" , "openIdConnectUrl"));
    protected static Set EXTERNAL_DOCS_KEYS = new LinkedHashSet<>(Arrays.asList("description", "url"));
    protected static Set COMPONENTS_KEYS = new LinkedHashSet<>(Arrays.asList("schemas", "responses", "parameters", "examples", "requestBodies", "headers", "securitySchemes",  "links", "callbacks"));
    protected static Set SCHEMA_KEYS = new LinkedHashSet<>(Arrays.asList("$ref","title", "multipleOf", "maximum", "format", "exclusiveMaximum", "minimum", "exclusiveMinimum", "maxLength", "minLength", "pattern", "maxItems", "minItems", "uniqueItems", "maxProperties", "minProperties", "required", "enum", "type", "allOf", "oneOf", "anyOf", "not", "items", "properties", "additionalProperties", "description", "format", "default", "nullable", "discriminator", "readOnly", "writeOnly","xml", "externalDocs", "example","deprecated"));
    protected static Set EXAMPLE_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "summary", "description", "value", "externalValue"));
    protected static Set HEADER_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "name", "in", "description", "required", "deprecated", "allowEmptyValue", "style", "explode", "allowReserved", "schema", "example", "examples", "content"));
    protected static Set LINK_KEYS = new LinkedHashSet<>(Arrays.asList("$ref", "operationRef", "operationId", "parameters", "requestBody", "description", "server"));
    protected static Set MEDIATYPE_KEYS = new LinkedHashSet<>(Arrays.asList("schema", "example", "examples", "encoding"));
    protected static Set XML_KEYS = new LinkedHashSet<>(Arrays.asList("name", "namespace", "prefix", "attribute", "wrapped"));
    protected static Set OAUTHFLOW_KEYS = new LinkedHashSet<>(Arrays.asList("authorizationUrl", "tokenUrl", "refreshUrl", "scopes"));
    protected static Set OAUTHFLOWS_KEYS = new LinkedHashSet<>(Arrays.asList("implicit", "password", "clientCredentials", "authorizationCode"));
    protected static Set ENCODING_KEYS = new LinkedHashSet<>(Arrays.asList("contentType", "headers", "style", "explode", "allowReserved"));


    private static final String QUERY_PARAMETER = "query";
    private static final String COOKIE_PARAMETER = "cookie";
    private static final String PATH_PARAMETER = "path";
    private static final String HEADER_PARAMETER = "header";
    private static final Pattern RFC3339_DATE_TIME_PATTERN = Pattern.compile( "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?((Z)|([+-]\\d{2}:\\d{2}))$");
    private static final Pattern RFC3339_DATE_PATTERN = Pattern.compile( "^(\\d{4})-(\\d{2})-(\\d{2})$");
    private static final String REFERENCE_SEPARATOR = "#/";
    private Components components;
    private final Set operationIDs = new HashSet<>();

    public SwaggerParseResult deserialize(JsonNode rootNode) {
    	return deserialize(rootNode, null);
    }
    
    public SwaggerParseResult deserialize(JsonNode rootNode, String path) {
        SwaggerParseResult result = new SwaggerParseResult();
        try {
            
            ParseResult rootParse = new ParseResult();
            OpenAPI api = parseRoot(rootNode, rootParse, path);
            result.setOpenAPI(api);
            result.setMessages(rootParse.getMessages());

        } catch (Exception e) {
            result.setMessages(Arrays.asList(e.getMessage()));

        }
        return result;
    }

    public OpenAPI parseRoot(JsonNode node, ParseResult result, String path) {
        String location = "";
        OpenAPI openAPI = new OpenAPI();
        if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
            ObjectNode rootNode = (ObjectNode) node;

            // required
            String value = getString("openapi", rootNode, true, location, result);

            // we don't even try if the version isn't there
            if(value == null || !value.startsWith("3.0")) {
                return null;
            }
            openAPI.setOpenapi(value);

            ObjectNode obj = getObject("info", rootNode, true, location, result);
            if (obj != null) {
                Info info = getInfo(obj, "info", result);
                openAPI.setInfo(info);
            }

            obj = getObject("components", rootNode, false, location, result);
            if (obj != null) {
                Components components = getComponents(obj, "components", result);
                openAPI.setComponents(components);
                this.components=components;
            }

            obj = getObject("paths", rootNode, true, location, result);
            if (obj != null) {
                Paths paths = getPaths(obj, "paths", result);
                openAPI.setPaths(paths);
            }


            ArrayNode array = getArray("servers", rootNode, false, location, result);
            if (array != null && array.size() > 0) {
                openAPI.setServers(getServersList(array, String.format("%s.%s", location, "servers"), result, path));
            }else {
                Server defaultServer = new Server();
                defaultServer.setUrl("/");
                List  servers = new ArrayList<>();
                servers.add(defaultServer);
                openAPI.setServers(servers);
            }

            obj = getObject("externalDocs", rootNode, false, location, result);
            if (obj != null) {
                ExternalDocumentation externalDocs = getExternalDocs(obj, "externalDocs", result);
                openAPI.setExternalDocs(externalDocs);
            }

            array = getArray("tags", rootNode, false, location, result);
            if (array != null && array.size() > 0) {
                openAPI.setTags(getTagList(array, "tags", result));
            }

            array = getArray("security", rootNode, false, location, result);
            if (array != null && array.size() > 0) {
                List securityRequirements = getSecurityRequirementsList(array, "security", result);
                if (securityRequirements != null && securityRequirements. size() > 0) {
                    openAPI.setSecurity(securityRequirements);
                }
            }

            Map  extensions = getExtensions(rootNode);
            if(extensions != null && extensions.size() > 0) {
                openAPI.setExtensions(extensions);
            }

            Set keys = getKeys(rootNode);
            for(String key : keys) {
                if(!ROOT_KEYS.contains(key) && !key.startsWith("x-")) {
                    result.extra(location, key, node.get(key));
                }
            }

        } else {
            result.invalidType(location, "openapi", "object", node);
            result.invalid();
            return null;
        }

        return openAPI;
    }

    public String mungedRef(String refString) {
        // Ref: IETF RFC 3966, Section 5.2.2
        if (!refString.contains(":") &&   // No scheme
                !refString.startsWith("#") && // Path is not empty
                !refString.startsWith("/") && // Path is not absolute
                refString.indexOf(".") > 0) { // Path does not start with dot but contains "." (file extension)
            return "./" + refString;
        }
        return null;
    }

    public Map getExtensions(ObjectNode node){

        Map extensions = new LinkedHashMap<>();

        Set keys = getKeys(node);
        for(String key : keys) {
            if(key.startsWith("x-")) {
                extensions.put(key, Json.mapper().convertValue(node.get(key), Object.class));
            }
        }
        return  extensions;

    }

    public Components getComponents(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Components components = new Components();

        ObjectNode node = getObject("schemas",obj,false, location ,result);
        if(node != null) {
            components.setSchemas(getSchemas(node,String.format("%s.%s", location, "schemas"),result));
        }

        node = getObject("responses",obj,false, location,result);
        if(node != null) {
            components.setResponses(getResponses(node, String.format("%s.%s", location, "responses"),result));
        }

        node = getObject("parameters",obj,false, location ,result);
        if(node != null) {
            components.setParameters(getParameters(node, String.format("%s.%s", location, "parameters"),result));
        }
        node = getObject("examples",obj,false,location,result);
        if(node != null) {
            components.setExamples(getExamples(node, String.format("%s.%s", location, "examples"),result));
        }

        node = getObject("requestBodies",obj,false,location,result);
        if(node != null) {
            components.setRequestBodies(getRequestBodies(node, String.format("%s.%s", location, "requestBodies"),result));
        }

        node = getObject("headers",obj,false,location,result);
        if(node != null) {
            components.setHeaders(getHeaders(node, String.format("%s.%s", location, "headers"),result));
        }

        node = getObject("securitySchemes",obj,false,location,result);
        if(node != null) {
            components.setSecuritySchemes(getSecuritySchemes(node, String.format("%s.%s", location, "securitySchemes"),result));
        }

        node = getObject("links",obj,false,location,result);
        if(node != null) {
            components.setLinks(getLinks(node, String.format("%s.%s", location, "links"),result));
        }

        node = getObject("callbacks",obj,false,location,result);
        if(node != null) {
            components.setCallbacks(getCallbacks(node, String.format("%s.%s", location, "callbacks"),result));
        }
        components.setExtensions(new LinkedHashMap<>());

        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            components.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!COMPONENTS_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }


        return  components;
    }

    public List getTagList(ArrayNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        List tags = new ArrayList<>();
        Set tagsTracker = new HashSet<>();
        for (JsonNode item : obj) {
            if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
                Tag tag = getTag((ObjectNode) item, location, result);
                if (tag != null) {
                    tags.add(tag);

                    if(tagsTracker.contains((String)tag.getName())) {
                        result.uniqueTags(location,tag.getName());
                    }

                    tagsTracker.add(tag.getName());
                }
            }
        }
        return tags;
    }

    public Tag getTag(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }

        Tag tag = new Tag();

        String value = getString("name", obj, true, location, result);
        if(StringUtils.isNotBlank(value)) {
            tag.setName(value);
        }

        value = getString("description", obj, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            tag.setDescription(value);
        }

        ObjectNode docs = getObject("externalDocs",obj,false,location,result);
        ExternalDocumentation externalDocs = getExternalDocs(docs, String.format("%s.%s", location, "externalDocs"), result);
        if (externalDocs != null) {
            tag.setExternalDocs(externalDocs);
        }

        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            tag.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!TAG_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }

        return tag;
    }



    public List getServersList(ArrayNode obj, String location, ParseResult result, String path) {

        List servers = new ArrayList<>();
        if (obj == null) {
            return null;

        }
        for (JsonNode item : obj) {
            if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
                Server server = getServer((ObjectNode) item, location, result, path);
                if (server != null) {
                    servers.add(server);
                }else{
                    Server defaultServer = new Server();
                    defaultServer.setUrl("/");
                    servers.add(defaultServer);
                }
            }
        }
        return servers;
    }
    
    public List getServersList(ArrayNode obj, String location, ParseResult result) {
		return getServersList(obj, location, result, null);
	}

	public Server getServer(ObjectNode obj, String location, ParseResult result) {
		return getServer(obj, location, result, null);
	}

    public Server getServer(ObjectNode obj, String location, ParseResult result, String path) {
        if (obj == null) {
            return null;
        }

        Server server = new Server();

        String value = getString("url", obj, true, location, result);
        if(StringUtils.isNotBlank(value)) {
			if(!isValidURL(value) && path != null){
				try {
					final URI absURI = new URI(path);
					if("http".equals(absURI.getScheme()) || "https".equals(absURI.getScheme())){
						value = absURI.resolve(new URI(value)).toString();
					}
				} catch (URISyntaxException e) {
					e.printStackTrace();
				}

			}
            server.setUrl(value);
        }

        value = getString("description", obj, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            server.setDescription(value);
        }
        if (obj.get("variables") != null) {
            ObjectNode variables = getObject("variables", obj, false, location, result);
            ServerVariables serverVariables = getServerVariables(variables, String.format("%s.%s", location, "variables"), result);
            if (serverVariables != null && serverVariables.size() > 0) {
                server.setVariables(serverVariables);
            }
        }


        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            server.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!SERVER_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }


        return server;
    }
    
    boolean isValidURL(String urlString){
		try {
			URL url = new URL(urlString);
			url.toURI();
			return true;
		} catch (Exception exception) {
			return false;
		}
	}

    public ServerVariables getServerVariables(ObjectNode obj, String location, ParseResult result){
        ServerVariables serverVariables = new ServerVariables();
        if (obj == null) {
            return null;
        }

        Set serverKeys = getKeys(obj);
        for (String serverName : serverKeys) {
            JsonNode serverValue = obj.get(serverName);
            ObjectNode server = (ObjectNode) serverValue;
            ServerVariable serverVariable = getServerVariable(server, String.format("%s.%s", location, serverName), result);
            serverVariables.addServerVariable(serverName,serverVariable);
        }

        return serverVariables;
    }

    public ServerVariable getServerVariable(ObjectNode obj, String location, ParseResult result){
        if(obj == null){
            return null;
        }

        ServerVariable serverVariable = new ServerVariable();

        ArrayNode arrayNode = getArray("enum",obj,false,location,result);
        if (arrayNode != null) {
            List _enum = new ArrayList<>();
            for(JsonNode n : arrayNode) {
                if(n.isValueNode()) {
                    _enum.add(n.asText());
                    serverVariable.setEnum(_enum);
                }
                else {
                    result.invalidType(location, "enum", "value", n);
                }
            }
        }
        String value = getString("default", obj, true, String.format("%s.%s", location, "default"), result);
        if(StringUtils.isNotBlank(value)) {
            serverVariable.setDefault(value);
        }

        value = getString("description", obj, false, String.format("%s.%s", location, "description"), result);
        if(StringUtils.isNotBlank(value)) {
            serverVariable.setDescription(value);
        }

        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            serverVariable.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!SERVER_VARIABLE_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }

        return serverVariable;
    }

    //PathsObject

    public Paths getPaths(ObjectNode obj, String location, ParseResult result) {
        final Paths paths = new Paths();
        if (obj == null) {
            return null;
        }
        Set pathKeys = getKeys(obj);
        for (String pathName : pathKeys) {
            JsonNode pathValue = obj.get(pathName);
            if(pathName.startsWith("x-")) {
                Map  extensions = getExtensions(obj);
                if(extensions != null && extensions.size() > 0) {
                    paths.setExtensions(extensions);
                }
            } else {
                if (!pathValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                    result.invalidType(location, pathName, "object", pathValue);
                } else {
                    if(!pathName.startsWith("/")){
                        result.warning(location," Resource "+pathName+ " should start with /");
                    }
                    ObjectNode path = (ObjectNode) pathValue;
                    PathItem pathObj = getPathItem(path,String.format("%s.'%s'", location,pathName), result);
                    String[] eachPart = pathName.split("/");
                    Arrays.stream(eachPart)
                            .filter(part -> part.startsWith("{") && part.endsWith("}") && part.length() > 2)
                            .forEach(part -> {
                                String pathParam = part.substring(1, part.length() - 1);
                                boolean definedInPathLevel = isPathParamDefined(pathParam, pathObj.getParameters());
                                if (!definedInPathLevel) {
                                    List operationsInAPath = getAllOperationsInAPath(pathObj);
                                    operationsInAPath.forEach(operation -> {
                                        if (!isPathParamDefined(pathParam, operation.getParameters())) {
                                            result.warning(location + ".'" + pathName + "'"," Declared path parameter " + pathParam + " needs to be defined as a path parameter in path or operation level");
                                            return;
                                        }
                                    });
                                }
                            });
                    paths.put(pathName, pathObj);
                }
            }
        }
        return paths;
    }

    private boolean isPathParamDefined(String pathParam, List parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return false;
        } else {
            Parameter pathParamDefined = parameters.stream()
                            .filter(parameter -> (parameter.get$ref() != null) || (pathParam.equals(parameter.getName()) && "path".equals(parameter.getIn())))
                            .findFirst()
                            .orElse(null);
            if (pathParamDefined == null) {
                return false;
            }
        }
        return true;
    }

    private void addToOperationsList(List operationsList, Operation operation) {
        if (operation == null) {
            return;
        }
        operationsList.add(operation);
    }

    public List getAllOperationsInAPath(PathItem pathObj) {
        List operations = new ArrayList<>();
        addToOperationsList(operations, pathObj.getGet());
        addToOperationsList(operations, pathObj.getPut());
        addToOperationsList(operations, pathObj.getPost());
        addToOperationsList(operations, pathObj.getPatch());
        addToOperationsList(operations, pathObj.getDelete());
        addToOperationsList(operations, pathObj.getTrace());
        addToOperationsList(operations, pathObj.getOptions());
        addToOperationsList(operations, pathObj.getHead());
        return operations;
    }

    public PathItem getPathItem(ObjectNode obj, String location, ParseResult result) {


        PathItem pathItem = new PathItem();

        if (obj.get("$ref") != null) {
            JsonNode ref = obj.get("$ref");

            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    pathItem.set$ref(mungedRef);
                }else{
                    pathItem.set$ref(ref.textValue());
                }
                return pathItem;
            } else if (ref.getNodeType().equals(JsonNodeType.OBJECT)) {
                ObjectNode node = (ObjectNode) ref;

                // extra keys
                Set keys = getKeys(node);
                for (String key : keys) {
                    result.extra(location, key, node.get(key));
                }
            }
            return null;
        }

        String value = getString("summary", obj, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            pathItem.setSummary(value);
        }

        value = getString("description", obj, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            pathItem.setDescription(value);
        }

        ArrayNode parameters = getArray("parameters", obj, false, location, result);
        if(parameters != null && parameters.size()> 0) {
            pathItem.setParameters(getParameterList(parameters, location, result));
        }

        ArrayNode servers = getArray("servers", obj, false, location, result);
        if(servers != null && servers.size() >0) {
            pathItem.setServers(getServersList(servers, location, result));
        }

        ObjectNode node = getObject("get", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(get)", result);
            if (operation != null) {
                pathItem.setGet(operation);
            }
        }
        node = getObject("put", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(put)", result);
            if (operation != null) {
                pathItem.setPut(operation);
            }
        }
        node = getObject("post", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(post)", result);
            if (operation != null) {
                pathItem.setPost(operation);
            }
        }
        node = getObject("head", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(head)", result);
            if (operation != null) {
                pathItem.setHead(operation);
            }
        }
        node = getObject("delete", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(delete)", result);
            if (operation != null) {
                pathItem.setDelete(operation);
            }
        }
        node = getObject("patch", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(patch)", result);
            if (operation != null) {
                pathItem.setPatch(operation);
            }
        }
        node = getObject("options", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(options)", result);
            if (operation != null) {
                pathItem.setOptions(operation);
            }
        }
        node = getObject("trace", obj, false, location, result);
        if (node != null) {
            Operation operation = getOperation(node, location + "(trace)", result);
            if (operation != null) {
                pathItem.setTrace(operation);
            }
        }

        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            pathItem.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!PATHITEM_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }

        return pathItem;
    }


    public ExternalDocumentation getExternalDocs(ObjectNode node, String location, ParseResult result) {
        ExternalDocumentation externalDocs = null;

        if (node != null) {
            externalDocs = new ExternalDocumentation();

            String value = getString("description", node, false, location, result);
            if(StringUtils.isNotBlank(value)) {
                externalDocs.description(value);
            }

            value = getString("url", node, true, location, result);
            if(StringUtils.isNotBlank(value)) {
                externalDocs.url(value);
            }

            Map  extensions = getExtensions(node);
            if(extensions != null && extensions.size() > 0) {
                externalDocs.setExtensions(extensions);
            }

            Set keys = getKeys(node);
            for(String key : keys) {
                if(!EXTERNAL_DOCS_KEYS.contains(key) && !key.startsWith("x-")) {
                    result.extra(location, key, node.get(key));
                }
            }
        }

        return externalDocs;
    }


    public String getString(String key, ObjectNode node, boolean required, String location, ParseResult result, Set uniqueValues) {
        String value = null;
        JsonNode v = node.get(key);
        if (node == null || v == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        } else if (!v.isValueNode()) {
            result.invalidType(location, key, "string", node);
        } else {
            value = v.asText();
            if (uniqueValues != null && !uniqueValues.add(value)) {
                result.unique(location, "operationId");
                result.invalid();
            }
        }
        return value;
    }

    public String getString(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        return getString(key, node, required, location, result, null);
    }

    public Set getKeys(ObjectNode node) {
        Set keys = new LinkedHashSet<>();
        if (node == null) {
            return keys;
        }

        Iterator it = node.fieldNames();
        while (it.hasNext()) {
            keys.add(it.next());
        }

        return keys;
    }


    public ObjectNode getObject(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        JsonNode value = node.get(key);
        ObjectNode object = null;
        if (value == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        } else if (!value.getNodeType().equals(JsonNodeType.OBJECT)) {
            result.invalidType(location, key, "object", value);
            if (required) {
                result.invalid();
            }
        } else {
            object = (ObjectNode) value;
        }
        return object;
    }

    public Info getInfo(ObjectNode node, String location, ParseResult result) {
        if (node == null)
            return null;

        Info info = new Info();

        String value = getString("title", node, true, location, result);
        if(StringUtils.isNotBlank(value)) {
            info.setTitle(value);
        }

        value = getString("description", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            info.setDescription(value);
        }

        value = getString("termsOfService", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            info.setTermsOfService(value);
        }

        ObjectNode obj = getObject("contact", node, false, "contact", result);
        Contact contact = getContact(obj, String.format("%s.%s", location, "contact"), result);
        if(obj != null) {
            info.setContact(contact);
        }
        obj = getObject("license", node, false, location, result);
        License license = getLicense(obj, String.format("%s.%s", location, "license"), result);
        if(obj != null) {
            info.setLicense(license);
        }

        value = getString("version", node, true, location, result);
        if(StringUtils.isNotBlank(value)) {
            info.setVersion(value);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            info.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!INFO_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return info;
    }

    public License getLicense(ObjectNode node, String location, ParseResult result) {
        if (node == null)
            return null;

        License license = new License();

        String value = getString("name", node, true, location, result);
        if(StringUtils.isNotBlank(value)) {
            license.setName(value);
        }

        value = getString("url", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            try {
               new URL(value);
            }
            catch (Exception e) {
                result.warning(location,value);
            }
            license.setUrl(value);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            license.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!LICENSE_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return license;
    }

    public Contact getContact(ObjectNode node, String location, ParseResult result) {
        if (node == null)
            return null;

        Contact contact = new Contact();

        String value = getString("name", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            contact.setName(value);
        }

        value = getString("url", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            try {
                new URL(value);
            }
            catch (Exception e) {
                result.warning(location,value);
            }
            contact.setUrl(value);
        }

        value = getString("email", node, false, location, result);
        if(StringUtils.isNotBlank(value)) {
            contact.setEmail(value);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            contact.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!CONTACT_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return contact;
    }

    public Content getContent(ObjectNode node, String location, ParseResult result){
        if (node == null) {
            return null;
        }
        Content content = new Content();

        Set keys = getKeys(node);
        for(String key : keys) {
            MediaType mediaType = getMediaType((ObjectNode) node.get(key), location, result);
            if (mediaType != null) {
                content.addMediaType(key, mediaType);
            }
        }

        return content;
    }

    public MediaType getMediaType(ObjectNode contentNode, String location, ParseResult result){
        if (contentNode == null) {
            return null;
        }
        MediaType mediaType = new MediaType();

        ObjectNode schemaObject = getObject("schema",contentNode,false,location,result);
        if(schemaObject != null){
            mediaType.setSchema(getSchema(schemaObject,String.format("%s.%s", location, "schema"),result));
        }


        ObjectNode encodingObject = getObject("encoding",contentNode,false,location,result);
        if(encodingObject!=null) {
            mediaType.setEncoding(getEncodingMap(encodingObject, String.format("%s.%s", location, "encoding"), result));
        }
        Map  extensions = getExtensions(contentNode);
        if(extensions != null && extensions.size() > 0) {
            mediaType.setExtensions(extensions);
        }

        ObjectNode examplesObject = getObject("examples",contentNode,false,location,result);
        if(examplesObject!=null) {
            mediaType.setExamples(getExamples(examplesObject, String.format("%s.%s", location, "examples"), result));
        }

        Object example = getAnyExample("example",contentNode, location,result);
        if (example != null){
            mediaType.setExample(example);
        }


        Set keys = getKeys(contentNode);
        for(String key : keys) {
            if(!MEDIATYPE_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, contentNode.get(key));
            }
        }

        return mediaType;
    }

    public Map getEncodingMap(ObjectNode node, String location, ParseResult result){
        if (node == null) {
            return null;
        }
        Map encodings = new LinkedHashMap<>();
        Set keys = getKeys(node);
        for(String key : keys) {
            Encoding encoding = getEncoding((ObjectNode) node.get(key), location, result);
            if (encoding != null) {
                encodings.put(key, encoding);
            }
        }
        return encodings;
    }

    public Encoding getEncoding(ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        Encoding encoding = new Encoding();

        String value = getString("contentType", node, true, location, result);
        encoding.setContentType(value);

        value = getString("style", node, false, location, result);

        if (StringUtils.isBlank(value)) {
            encoding.setStyle(Encoding.StyleEnum.FORM);
        } else {
            if (value.equals(Encoding.StyleEnum.FORM.toString())) {
                encoding.setStyle(Encoding.StyleEnum.FORM);
            } else if (value.equals(Encoding.StyleEnum.DEEP_OBJECT.toString())) {
                encoding.setStyle(Encoding.StyleEnum.DEEP_OBJECT);
            } else if (value.equals(Encoding.StyleEnum.PIPE_DELIMITED.toString())) {
                encoding.setStyle(Encoding.StyleEnum.PIPE_DELIMITED);
            } else if (value.equals(Encoding.StyleEnum.SPACE_DELIMITED.toString())) {
                encoding.setStyle(Encoding.StyleEnum.SPACE_DELIMITED);
            } else {
                result.invalidType(location, "style", "string", node);
            }
        }

        Boolean explode = getBoolean("explode", node, false, location, result);
        if (explode != null) {
            encoding.setExplode(explode);
        }

        Boolean allowReserved = getBoolean("allowReserved", node, false, location, result);
        if (allowReserved != null) {
            encoding.setAllowReserved(allowReserved);
        }
        ObjectNode headersObject = getObject("headers", node, false, location, result);
        if (headersObject!= null){
            encoding.setHeaders(getHeaders(headersObject, location, result));
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            encoding.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!ENCODING_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return encoding;
    }

    public Map getLinks(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map links = new LinkedHashMap<>();


        Set linkKeys = getKeys(obj);
        for(String linkName : linkKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            linkName)) {
                result.warning(location, "Link name "+ linkName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }

            JsonNode linkValue = obj.get(linkName);
            if (!linkValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, linkName, "object", linkValue);
            } else {
                ObjectNode link = (ObjectNode) linkValue;
                Link linkObj = getLink(link, String.format("%s.%s", location, linkName), result);
                if(linkObj !=null) {
                    links.put(linkName, linkObj);
                }
            }

        }
        return links;
    }

    public Link getLink(ObjectNode linkNode, String location, ParseResult result) {
        if (linkNode == null) {
            return null;
        }

        Link link = new Link();

        JsonNode ref = linkNode.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    link.set$ref(mungedRef);
                }else{
                    link.set$ref(ref.textValue());
                }

                return link;
            } else {
                result.invalidType(location, "$ref", "string", linkNode);
                return null;
            }
        }

        String value = getString("operationRef", linkNode, false, location, result);
        if(StringUtils.isNotBlank(value)){
            link.setOperationRef(value);
        }

        value = getString("operationId", linkNode, false, location, result);
        if(StringUtils.isNotBlank(value)){
            link.setOperationId(value);
        }

        ObjectNode parametersObject = getObject("parameters",linkNode,false,location,result);
        if (parametersObject!= null) {
            link.setParameters(getLinkParameters(parametersObject, location, result));
        }

        String requestBody = getString("requestBody",linkNode,false,location,result);
        if (requestBody!= null) {
            link.setRequestBody(requestBody);
        }

        ObjectNode headerObject = getObject("headers",linkNode,false,location,result);
        if (headerObject!= null) {
            link.setHeaders(getHeaders(headerObject, location, result));
        }

        ObjectNode serverObject = getObject("server",linkNode,false,location,result);
        if (serverObject!= null) {
             link.setServer(getServer(serverObject, location, result));
        }

        value = getString("description", linkNode, false, location, result);
        if(StringUtils.isNotBlank(value)){
            link.setDescription(value);
        }

        Map  extensions = getExtensions(linkNode);
        if(extensions != null && extensions.size() > 0) {
            link.setExtensions(extensions);
        }

        Set keys = getKeys(linkNode);
        for(String key : keys) {
            if(!LINK_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, linkNode.get(key));
            }
        }

        return link;
    }

    private Map getLinkParameters(ObjectNode parametersObject, String location, ParseResult result) {

        Map  linkParameters = new LinkedHashMap<>();

        Set keys = getKeys(parametersObject);
        for(String name : keys) {
            JsonNode value = parametersObject.get(name);
            linkParameters.put(name, value.asText());
        }

        return linkParameters;
    }

    public Map  getCallbacks(ObjectNode node, String location, ParseResult result){
        if (node == null) {
            return null;
        }
        Map callbacks = new LinkedHashMap<>();
        Set keys = getKeys(node);
        for(String key : keys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            key)) {
                result.warning(location, "Callback key "+ key + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }
            Callback callback = getCallback((ObjectNode) node.get(key), location, result);
            if (callback != null) {
                callbacks.put(key, callback);
            }
        }
        return callbacks;
    }

    public Callback getCallback(ObjectNode node,String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        Callback callback = new Callback();

        Set keys = getKeys(node);
        for(String name : keys) {
            JsonNode value = node.get(name);
            if (node!= null){
                JsonNode ref = node.get("$ref");
                if (ref != null) {
                    if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                        String mungedRef = mungedRef(ref.textValue());
                        if (mungedRef != null) {
                            callback.set$ref(mungedRef);
                        }else{
                            callback.set$ref(ref.textValue());
                        }
                        return callback;
                    } else {
                        result.invalidType(location, "$ref", "string", node);
                        return null;
                    }
                }
                callback.addPathItem(name,getPathItem((ObjectNode) value,location,result));

                Map  extensions = getExtensions(node);
                if(extensions != null && extensions.size() > 0) {
                    callback.setExtensions(extensions);
                }
            }
        }

        return callback;
    }

    public XML getXml(ObjectNode node, String location, ParseResult result){
        if (node == null) {
            return null;
        }
        XML xml = new XML();

        String value = getString("name", node, false, String.format("%s.%s", location, "name"), result);
        if(StringUtils.isNotBlank(value)){
            xml.setName(value);
        }

        value = getString("namespace", node, false, String.format("%s.%s", location, "namespace"), result);
        if(StringUtils.isNotBlank(value)){
            xml.setNamespace(value);
        }

        value = getString("prefix", node, false, String.format("%s.%s", location, "prefix"), result);
        if(StringUtils.isNotBlank(value)){
            xml.setPrefix(value);
        }

        Boolean attribute = getBoolean("attribute", node, false, location, result);
        if(attribute != null){
            xml.setAttribute(attribute);
        }

        Boolean wrapped = getBoolean("wrapped", node, false, location, result);
        if(wrapped != null){
            xml.setWrapped(wrapped);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            xml.setExtensions(extensions);
        }


        Set keys = getKeys(node);
        for(String key : keys) {
            if(!XML_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }



        return xml;

    }

    public ArrayNode getArray(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        JsonNode value = node.get(key);
        ArrayNode arrayNode = null;
        if (value == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        } else if (!value.getNodeType().equals(JsonNodeType.ARRAY)) {
            result.invalidType(location, key, "array", value);
        } else {
            arrayNode = (ArrayNode) value;
        }
        return arrayNode;
    }

    public Boolean getBoolean(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        Boolean value = null;
        JsonNode v = node.get(key);
        if (node == null || v == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        } else {
            if (v.getNodeType().equals(JsonNodeType.BOOLEAN)) {
                value = v.asBoolean();
            } else if (v.getNodeType().equals(JsonNodeType.STRING)) {
                String stringValue = v.textValue();
                return Boolean.parseBoolean(stringValue);
            }
        }
        return value;
    }

    public BigDecimal getBigDecimal(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        BigDecimal value = null;
        JsonNode v = node.get(key);
        if (node == null || v == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        }
        else if(v.getNodeType().equals(JsonNodeType.NUMBER)) {
            value = new BigDecimal(v.asText());
        }
        else if(!v.isValueNode()) {
            result.invalidType(location, key, "double", node);
        }
        return value;
    }



    public Integer getInteger(String key, ObjectNode node, boolean required, String location, ParseResult result) {
        Integer value = null;
        JsonNode v = node.get(key);
        if (node == null || v == null) {
            if (required) {
                result.missing(location, key);
                result.invalid();
            }
        }
        else if(v.getNodeType().equals(JsonNodeType.NUMBER)) {
            if (v.isInt()) {
                value = v.intValue();
            }
        }
        else if(!v.isValueNode()) {
            result.invalidType(location, key, "integer", node);
        }
        return value;
    }

    public Map getParameters(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map parameters = new LinkedHashMap<>();
        Set filter = new HashSet<>();
        Parameter parameter=null;

        Set parameterKeys = getKeys(obj);
        for(String parameterName : parameterKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            parameterName)) {
                result.warning(location, "Parameter name "+ parameterName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }

            JsonNode parameterValue = obj.get(parameterName);
            if (parameterValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                ObjectNode parameterObj = (ObjectNode) parameterValue;
                if(parameterObj != null) {
                     parameter = getParameter(parameterObj, String.format("%s.%s", location, parameterName), result);
                    if (parameter != null) {
                        parameters.put(parameterName, parameter);
                    }
                }
            }

        }
        return parameters;
    }


    public List getParameterList(ArrayNode obj, String location, ParseResult result) {
        List parameters = new ArrayList<>();
        if (obj == null) {
            return parameters;
        }
        for (JsonNode item : obj) {
            if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
                Parameter parameter = getParameter((ObjectNode) item, location, result);
                if (parameter != null) {
                    parameters.add(parameter);
                }
            }
        }
        Set filter = new HashSet<>();


        parameters.stream().map(this::getParameterDefinition).forEach(param -> {
            String ref = param.get$ref();
            if(!filter.add(param.getName()+"#"+param.getIn())) {
                if(ref != null) {
                    if (ref.startsWith(REFERENCE_SEPARATOR)) {// validate if it's inline param also
                        result.warning(location, "There are duplicate parameter values");
                    }
                }else{
                    result.warning(location, "There are duplicate parameter values");
                }
            }
        });
        return parameters;
    }
    
    private Parameter getParameterDefinition(Parameter parameter) {
        if (parameter.get$ref() == null) {
            return parameter;
        }
        Object parameterSchemaName = extractSimpleName(parameter.get$ref()).getLeft();
        return Optional.ofNullable(components)
            .map(Components::getParameters)
            .map(parameters -> parameters.get(parameterSchemaName))
            .orElse(parameter);
            
    }

    public Parameter getParameter(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }


        Parameter parameter = null;

        JsonNode ref = obj.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                parameter = new Parameter();
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    parameter.set$ref(mungedRef);
                }else{
                    parameter.set$ref(ref.textValue());
                }
                Map  extensions = getExtensions(obj);
                if(extensions != null && extensions.size() > 0) {
                  parameter.setExtensions(extensions);
                }
                return parameter;
            } else {
                result.invalidType(location, "$ref", "string", obj);
                return null;
            }
        }

        String l = null;
        JsonNode ln = obj.get("name");
        if(ln != null) {
            l = ln.asText();
        }
        else {
            l = "['unknown']";
        }
        location += ".[" + l + "]";

        String value = getString("in", obj, true, location, result);

        if (StringUtils.isBlank(value)) {
            return null;
        }


        if (QUERY_PARAMETER.equals(value)) {
            parameter = new QueryParameter();
        } else if (HEADER_PARAMETER.equals(value)) {
            parameter = new HeaderParameter();
        } else if (PATH_PARAMETER.equals(value)) {
            parameter = new PathParameter();
        } else if (COOKIE_PARAMETER.equals(value)) {
            parameter = new CookieParameter();
        }

        if (parameter == null) {
            result.invalidType(location, "in", "string", obj);
            return null;
        }


        parameter.setIn(value);

        value = getString("name", obj, true, location, result);
        if (StringUtils.isNotBlank(value)) {
            parameter.setName(value);
        }

        value = getString("description", obj, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            parameter.setDescription(value);
        }

        Boolean required = getBoolean("required", obj, false, location, result);
        if (required != null) {
            parameter.setRequired(required);
        }else {
            parameter.setRequired(false);
        }


        Boolean deprecated = getBoolean("deprecated", obj, false, location, result);
        if (deprecated != null) {
            parameter.setDeprecated(deprecated);
        }

        if (parameter instanceof QueryParameter) {
            Boolean allowEmptyValue = getBoolean("allowEmptyValue", obj, false, location, result);
            if (allowEmptyValue != null) {
                parameter.setAllowEmptyValue(allowEmptyValue);
            }
        }

        value = getString("style", obj, false, location, result);
        setStyle(value, parameter, location, obj, result);

        Boolean explode = getBoolean("explode", obj, false, location, result);
        if (explode != null) {
            parameter.setExplode(explode);
        } else if(parameter.getStyle().equals(StyleEnum.FORM)){
            parameter.setExplode(Boolean.TRUE);
        } else {
            parameter.setExplode(Boolean.FALSE);
        }
        

        ObjectNode parameterObject = getObject("schema",obj,false,location,result);
        if (parameterObject!= null) {
            parameter.setSchema(getSchema(parameterObject,String.format("%s.%s", location, "schemas"),result));
        }

        ObjectNode examplesObject = getObject("examples",obj,false,location,result);
        if(examplesObject!=null) {
            parameter.setExamples(getExamples(examplesObject, String.format("%s.%s", location, "examples"), result));
        }

        Object example = getAnyExample("example", obj, location,result);
        if (example != null){
            parameter.setExample(example);
        }

        ObjectNode contentNode = getObject("content",obj,false,location,result);
        if(contentNode!= null) {
            parameter.setContent(getContent(contentNode, String.format("%s.%s", location, "content"), result));
        }

        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            parameter.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
           if(!PARAMETER_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
           }
        }

        return parameter;
    }


    public Map getHeaders(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map headers = new LinkedHashMap<>();

        Set headerKeys = getKeys(obj);
        for(String headerName : headerKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            headerName)) {
                result.warning(location, "Header name "+ headerName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }
            JsonNode headerValue = obj.get(headerName);
            if (!headerValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, headerName, "object", headerValue);
            } else {
                ObjectNode header = (ObjectNode) headerValue;
                Header headerObj = getHeader(header, String.format("%s.%s", location, headerName), result);
                if(headerObj != null) {
                    headers.put(headerName, headerObj);
                }
            }

        }

        return headers;
    }

    public Header getHeader(ObjectNode headerNode, String location, ParseResult result) {
        if (headerNode == null) {
            return null;
        }

        Header header = new Header();

        JsonNode ref = headerNode.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    header.set$ref(mungedRef);
                }else{
                    header.set$ref(ref.textValue());
                }
                return header;
            } else {
                result.invalidType(location, "$ref", "string", headerNode);
                return null;
            }
        }



        String value = getString("description", headerNode, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            header.setDescription(value);
        }

        Boolean required = getBoolean("required", headerNode, false, location, result);
        if (required != null) {
            header.setRequired(required);
        }

        Boolean deprecated = getBoolean("deprecated", headerNode, false, location, result);
        if (deprecated != null) {
            header.setDeprecated(deprecated);
        }

        Boolean explode = getBoolean("explode", headerNode, false, location, result);
        if (explode != null) {
            header.setExplode(explode);
        } else {
            header.setExplode(Boolean.FALSE);
        }

        header.setStyle(Header.StyleEnum.SIMPLE);

        ObjectNode headerObject = getObject("schema",headerNode,false,location,result);
        if (headerObject!= null) {
            header.setSchema(getSchema(headerObject, location, result));
        }

        ObjectNode examplesObject = getObject("examples",headerNode,false,location,result);
        if(examplesObject!=null) {
            header.setExamples(getExamples(examplesObject, location, result));
        }

        Object example = getAnyExample("example", headerNode, location,result);
        if (example != null){
            header.setExample(example);
        }

        ObjectNode contentNode = getObject("content",headerNode,false,location,result);
        if (contentNode!= null){
            header.setContent(getContent(contentNode,String.format("%s.%s", location, "content"),result));
        }

        Map  extensions = getExtensions(headerNode);
        if(extensions != null && extensions.size() > 0) {
            header.setExtensions(extensions);
        }

        Set oAuthFlowKeys = getKeys(headerNode);
        for(String key : oAuthFlowKeys) {
            if(!HEADER_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, headerNode.get(key));
            }
        }

        return header;
    }


    public Object getAnyExample(String nodeKey,ObjectNode node, String location, ParseResult result ){
        JsonNode example = node.get(nodeKey);
        if (example != null) {
            if (example.getNodeType().equals(JsonNodeType.STRING)) {
                String value = getString(nodeKey, node, false, location, result);
                if (StringUtils.isNotBlank(value)) {
                    return value;
                }
            } if (example.getNodeType().equals(JsonNodeType.NUMBER)) {
                Integer integerExample = getInteger(nodeKey, node, false, location, result);
                if (integerExample != null) {
                    return integerExample;
                }else {
                    BigDecimal bigDecimalExample = getBigDecimal(nodeKey, node, false, location, result);
                    if (bigDecimalExample != null) {
                        return bigDecimalExample;
                    }
                }
            } else if (example.getNodeType().equals(JsonNodeType.OBJECT)) {
                ObjectNode objectValue = getObject(nodeKey, node, false, location, result);
                if (objectValue != null) {
                   return objectValue;
                }
            } else if (example.getNodeType().equals(JsonNodeType.ARRAY)) {
                ArrayNode arrayValue = getArray(nodeKey, node, false, location, result);
                if (arrayValue != null) {
                    return arrayValue;
                }
            } else if (example.getNodeType().equals(JsonNodeType.BOOLEAN)){
                Boolean bool = getBoolean(nodeKey,node,false,location,result);
                if (bool != null){
                    return bool;
                }
            }
        }
        return null;
    }

    public Map getSecuritySchemes(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map securitySchemes = new LinkedHashMap<>();

        Set securitySchemeKeys = getKeys(obj);
        for(String securitySchemeName : securitySchemeKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            securitySchemeName)) {
                result.warning(location, "SecurityScheme name "+ securitySchemeName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }
            JsonNode securitySchemeValue = obj.get(securitySchemeName);
            if (!securitySchemeValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, securitySchemeName, "object", securitySchemeValue);
            } else {
                ObjectNode securityScheme = (ObjectNode) securitySchemeValue;
                SecurityScheme securitySchemeObj = getSecurityScheme(securityScheme, String.format("%s.%s", location, securitySchemeName), result);
                if (securityScheme != null) {
                    securitySchemes.put(securitySchemeName, securitySchemeObj);
                }
            }
        }
        return securitySchemes;
    }

    public SecurityScheme getSecurityScheme(ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        SecurityScheme securityScheme = new SecurityScheme();

        JsonNode ref = node.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    securityScheme.set$ref(mungedRef);
                }else{
                    securityScheme.set$ref(ref.textValue());
                }
                return securityScheme;
            } else {
                result.invalidType(location, "$ref", "string", node);
                return null;
            }
        }

        boolean descriptionRequired, bearerFormatRequired, nameRequired, inRequired, schemeRequired, flowsRequired, openIdConnectRequired;
        descriptionRequired = bearerFormatRequired = nameRequired = inRequired = schemeRequired = flowsRequired = openIdConnectRequired = false;
        
        String value = getString("type", node, true, location, result);
        if (StringUtils.isNotBlank(value)) {
            if (SecurityScheme.Type.APIKEY.toString().equals(value)) {
                securityScheme.setType(SecurityScheme.Type.APIKEY);
                nameRequired = inRequired = true;
            } else if (SecurityScheme.Type.HTTP.toString().equals(value)) {
                securityScheme.setType(SecurityScheme.Type.HTTP);
                schemeRequired = true;
            } else if (SecurityScheme.Type.OAUTH2.toString().equals(value)) {
                securityScheme.setType(SecurityScheme.Type.OAUTH2);
                flowsRequired = true;
            } else if (SecurityScheme.Type.OPENIDCONNECT.toString().equals(value)) {
                securityScheme.setType(SecurityScheme.Type.OPENIDCONNECT);
                openIdConnectRequired = true;
            }else{
                result.invalidType(location + ".type", "type", "http|apiKey|oauth2|openIdConnect ", node);
            }
        }
        value = getString("description", node, descriptionRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            securityScheme.setDescription(value);
        }

        value = getString("name", node, nameRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            securityScheme.setName(value);
        }

        final String securitySchemeIn = getString("in", node, inRequired, location, result);
        final Optional matchingIn = Arrays.stream(SecurityScheme.In.values())
                .filter(in -> in.toString().equals(securitySchemeIn))
                .findFirst();

        securityScheme.setIn(matchingIn.orElse(null));

        value = getString("scheme", node, schemeRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            securityScheme.setScheme(value);
        }

        value = getString("bearerFormat", node, bearerFormatRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            securityScheme.setBearerFormat(value);
        }

        ObjectNode flowsObject = getObject("flows", node, flowsRequired, location, result);
        if (flowsObject!= null) {
            securityScheme.setFlows(getOAuthFlows(flowsObject, location, result));
        }

        value = getString("openIdConnectUrl", node, openIdConnectRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            securityScheme.setOpenIdConnectUrl(value);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            securityScheme.setExtensions(extensions);
        }

        Set securitySchemeKeys = getKeys(node);
        for(String key : securitySchemeKeys) {
            if(!SECURITY_SCHEME_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return securityScheme;
    }

    public OAuthFlows getOAuthFlows(ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        OAuthFlows oAuthFlows = new OAuthFlows();

        ObjectNode objectNode = getObject("implicit", node, false, location, result);
        if(objectNode!= null) {
            oAuthFlows.setImplicit(getOAuthFlow("implicit", objectNode, location, result));
        }

        objectNode = getObject("password", node, false, location, result);
        if(objectNode!= null) {
            oAuthFlows.setPassword(getOAuthFlow("password", objectNode, location, result));
        }

        objectNode = getObject("clientCredentials", node, false, location, result);
        if(objectNode!= null) {
            oAuthFlows.setClientCredentials(getOAuthFlow("clientCredentials", objectNode, location, result));
        }

        objectNode = getObject("authorizationCode", node, false, location, result);
        if(objectNode!= null) {
            oAuthFlows.setAuthorizationCode(getOAuthFlow("authorizationCode", objectNode, location, result));
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            oAuthFlows.setExtensions(extensions);
        }

        Set oAuthFlowKeys = getKeys(node);
        for(String key : oAuthFlowKeys) {
            if(!OAUTHFLOWS_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }


        return oAuthFlows;
    }

    public OAuthFlow getOAuthFlow(String oAuthFlowType, ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        OAuthFlow oAuthFlow = new OAuthFlow();

        boolean authorizationUrlRequired, tokenUrlRequired, refreshUrlRequired, scopesRequired;
        authorizationUrlRequired = tokenUrlRequired = refreshUrlRequired = false;
        scopesRequired = true;
        switch (oAuthFlowType) {
          case "implicit":
            authorizationUrlRequired=true;
            break;
          case "password":
            tokenUrlRequired=true;
            break;
          case "clientCredentials":
            tokenUrlRequired=true;
            break;
          case "authorizationCode":
            authorizationUrlRequired = tokenUrlRequired=true;
            break;
        }
        
        String value = getString("authorizationUrl", node, authorizationUrlRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            oAuthFlow.setAuthorizationUrl(value);
        }

        value = getString("tokenUrl", node, tokenUrlRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            oAuthFlow.setTokenUrl(value);
        }

        value = getString("refreshUrl", node, refreshUrlRequired, location, result);
        if (StringUtils.isNotBlank(value)) {
            oAuthFlow.setRefreshUrl(value);
        }

        ObjectNode scopesObject = getObject("scopes",node, scopesRequired,location,result);

        Scopes scope = new Scopes();
        Set keys = getKeys(scopesObject);
        for(String name : keys) {
            JsonNode scopeValue = scopesObject.get(name);
            if (scopesObject!= null){
                scope.addString(name,scopeValue.asText());
                oAuthFlow.setScopes(scope);
            }
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            oAuthFlow.setExtensions(extensions);
        }

        Set oAuthFlowKeys = getKeys(node);
        for(String key : oAuthFlowKeys) {
            if(!OAUTHFLOW_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return oAuthFlow;
    }

    public Map getSchemas(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map schemas = new LinkedHashMap<>();

        Set schemaKeys = getKeys(obj);
        for (String schemaName : schemaKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            schemaName)) {
                result.warning(location, "Schema name "+ schemaName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }
            JsonNode schemaValue = obj.get(schemaName);
                if (!schemaValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                    result.invalidType(location, schemaName, "object", schemaValue);
                } else {
                    ObjectNode schema = (ObjectNode) schemaValue;
                    Schema schemaObj = getSchema(schema, String.format("%s.%s", location, schemaName), result);
                    if(schemaObj != null) {
                        schemas.put(schemaName, schemaObj);
                    }
                }
        }

        return schemas;
    }

    public Discriminator getDiscriminator (ObjectNode node, String location, ParseResult result){
        Discriminator discriminator = new Discriminator();

        String value = getString("propertyName",node,true,location,result);
        if (StringUtils.isNotBlank(value)){
            discriminator.setPropertyName(value);
        }

        ObjectNode mappingNode = getObject("mapping", node,false, location, result);
        if(mappingNode != null){
            Map mapping = new LinkedHashMap<>();
            Set keys = getKeys(mappingNode);
            for(String key : keys) {
                mapping.put(key, mappingNode.get(key).asText());
            }
            discriminator.setMapping(mapping);
        }

        return discriminator;

    }

    public Schema getSchema(ObjectNode node, String location, ParseResult result){
        if(node== null){
            return null;
        }


        Schema schema = null;
        ArrayNode oneOfArray = getArray("oneOf", node, false, location, result);
        ArrayNode allOfArray = getArray("allOf", node, false, location, result);
        ArrayNode anyOfArray = getArray("anyOf", node, false, location, result);
        ObjectNode itemsNode = getObject("items", node, false, location, result);
        ObjectNode additionalPropertiesNode = getObject("additionalProperties", node, false, location, result);
        Boolean additionalPropertiesBoolean = getBoolean("additionalProperties", node, false, location, result);


        if((allOfArray != null )||(anyOfArray != null)|| (oneOfArray != null)) {
            ComposedSchema composedSchema = new ComposedSchema();

            if (allOfArray != null) {

                for (JsonNode n : allOfArray) {
                    if (n.isObject()) {
                        schema = getSchema((ObjectNode) n, location, result);
                        composedSchema.addAllOfItem(schema);
                    }
                }
                schema = composedSchema;
            }
            if (anyOfArray != null) {

                for (JsonNode n : anyOfArray) {
                    if (n.isObject()) {
                        schema = getSchema((ObjectNode) n, location, result);
                        composedSchema.addAnyOfItem(schema);
                    }
                }
                schema = composedSchema;
            }
            if (oneOfArray != null) {

                for (JsonNode n : oneOfArray) {
                    if (n.isObject()) {
                        schema = getSchema((ObjectNode) n, location, result);
                        composedSchema.addOneOfItem(schema);
                    }
                }
                schema = composedSchema;
            }
        }

        if(itemsNode != null) {
            ArraySchema items = new ArraySchema();
            if (itemsNode.getNodeType().equals(JsonNodeType.OBJECT)){
                items.setItems(getSchema(itemsNode, location, result));
            }else if (itemsNode.getNodeType().equals(JsonNodeType.ARRAY)){
                for (JsonNode n : itemsNode) {
                    if (n.isValueNode()) {
                        items.setItems(getSchema(itemsNode, location, result));
                    }
                }
            }
            schema = items;
        }

        if(additionalPropertiesNode != null) {
            MapSchema mapSchema = new MapSchema();
            if (additionalPropertiesNode.getNodeType().equals(JsonNodeType.OBJECT)) {
                ObjectNode additionalPropertiesObj = getObject("additionalProperties", node, false, location, result);
                if (additionalPropertiesObj != null) {
                    Schema additionalProperties = getSchema(additionalPropertiesObj, location, result);
                    if (additionalProperties != null) {
                        mapSchema.setAdditionalProperties(additionalProperties);
                        schema = mapSchema;
                    }
                }
            }
        } else if(additionalPropertiesBoolean != null){
            MapSchema mapSchema = new MapSchema();
            if (additionalPropertiesBoolean) {
                mapSchema.setAdditionalProperties(additionalPropertiesBoolean);
                schema = mapSchema;
            }else{
                ObjectSchema objectSchema = new ObjectSchema();
                objectSchema.setAdditionalProperties(additionalPropertiesBoolean);
                schema = objectSchema;
            }
        }



        if (schema == null){
            schema = SchemaTypeUtil.createSchemaByType(node);
        }

        JsonNode ref = node.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {

                if(location.startsWith("paths")){
                    try{
                        String components[]=ref.asText().split("#/components");
                        if((ref.asText().startsWith("#/components"))&&(components.length>1)) {
                            String[] childComponents = components[1].split("/");
                            String[] newChildComponents = Arrays.copyOfRange(childComponents, 1, childComponents.length);
                            boolean isValidComponent = ReferenceValidator.valueOf(newChildComponents[0])
                                                                         .validateComponent(this.components,
                                                                                         newChildComponents[1]);
                            if (!isValidComponent) {
                                result.missing(location, ref.asText());
                            }
                        }
                    }catch (Exception e){
                        result.missing(location, ref.asText());
                    }
                }

                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    schema.set$ref(mungedRef);
                }else{
                    schema.set$ref(ref.asText());
                }
                Map extensions = getExtensions(node);
                if(extensions != null && extensions.size() > 0) {
                  schema.setExtensions(extensions);
                }
                return schema;
            } else {
                result.invalidType(location, "$ref", "string", node);
                return null;
            }
        }


        String value = getString("title",node,false,location,result);
        if (StringUtils.isNotBlank(value)) {
            schema.setTitle(value);
        }

        ObjectNode discriminatorNode = getObject("discriminator", node, false, location, result);
        if (discriminatorNode != null) {
            schema.setDiscriminator(getDiscriminator(discriminatorNode,location,result));
        }

        BigDecimal bigDecimal = getBigDecimal("multipleOf",node,false,location,result);
        if(bigDecimal != null) {
            schema.setMultipleOf(bigDecimal);
        }

        bigDecimal = getBigDecimal("maximum", node, false, location, result);
        if(bigDecimal != null) {
            schema.setMaximum(bigDecimal);
        }

        Boolean bool = getBoolean("exclusiveMaximum", node, false, location, result);
        if (bool != null) {
            schema.setExclusiveMaximum(bool);
        }

        bigDecimal = getBigDecimal("minimum", node, false, location, result);
        if(bigDecimal != null) {
            schema.setMinimum(bigDecimal);
        }

        bool = getBoolean("exclusiveMinimum", node, false, location, result);
        if(bool != null) {
            schema.setExclusiveMinimum(bool);
        }

        Integer integer = getInteger("minLength", node, false, location, result);
        if(integer != null){
            schema.setMinLength(integer);
        }

        integer = getInteger("maxLength", node, false, location, result);
        if(integer != null){
            schema.setMaxLength(integer);
        }

        String pattern = getString("pattern", node, false, location, result);
        if (StringUtils.isNotBlank(pattern)) {
            schema.setPattern(pattern);
        }

        integer = getInteger("maxItems", node, false, location, result);
        if(integer != null) {
            schema.setMaxItems(integer);
        }
        integer = getInteger("minItems", node, false, location, result);
        if(integer != null){
            schema.setMinItems(integer);
        }

        bool = getBoolean("uniqueItems", node, false, location, result);
        if(bool != null){
            schema.setUniqueItems(bool);
        }

        integer = getInteger("maxProperties", node, false, location, result);
        if (integer != null){
            schema.setMaxProperties(integer);
        }

        integer = getInteger("minProperties", node, false, location, result);
        if(integer != null) {
            schema.setMinProperties(integer);
        }

        ArrayNode required = getArray("required", node, false, location, result);
        if(required != null) {
            List requiredList = new ArrayList<>();
            for (JsonNode n : required) {
                if(n.getNodeType().equals(JsonNodeType.STRING)) {
                    requiredList.add(((TextNode) n).textValue());
                }
                else {
                    result.invalidType(location, "required", "string", n);
                }
            }
            if(requiredList.size() > 0) {
                schema.setRequired(requiredList);
            }
        }

        ArrayNode enumArray = getArray("enum", node, false, location, result);
        if(enumArray != null) {
            for (JsonNode n : enumArray) {
                if (n.isNumber()) {
                    schema.addEnumItemObject(n.numberValue());
                } else if (n.isValueNode()) {
                    try {
                        schema.addEnumItemObject( getDecodedObject( schema, n.asText(null)));
                    }
                    catch( ParseException e) {
                        result.invalidType( location, String.format( "enum=`%s`", e.getMessage()), schema.getFormat(), n);
                    }
                } else {
                    result.invalidType(location, "enum", "value", n);
                }
            }
        }

        value = getString("type",node,false,location,result);
        if (StringUtils.isNotBlank(value) && StringUtils.isBlank(schema.getType())) {
            schema.setType(value);
        }else{
            // may have an enum where type can be inferred
            JsonNode enumNode = node.get("enum");
            if(enumNode != null && enumNode.isArray()) {
                String type = inferTypeFromArray((ArrayNode) enumNode);
                schema.setType(type);
            }
        }

        ObjectNode notObj = getObject("not", node, false, location, result);
        if(notObj != null) {
            Schema not = getSchema(notObj, location, result);
            if(not != null) {
                schema.setNot(not);
            }
        }



        Map  properties = new LinkedHashMap<>();
        ObjectNode propertiesObj = getObject("properties", node, false, location, result);
        Schema property = null;

        Set keys = getKeys(propertiesObj);
        for(String name : keys) {
            JsonNode propertyValue = propertiesObj.get(name);
            if (!propertyValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, "properties", "object", propertyValue);
            } else {
                if (propertiesObj!= null){
                    property = getSchema((ObjectNode) propertyValue, location, result);
                    if(property != null) {
                        properties.put(name, property);
                    }
                }
            }
        }
        if (propertiesObj != null) {
            schema.setProperties(properties);
        }

        value = getString("description",node,false,location,result);
        if (StringUtils.isNotBlank(value)) {
            schema.setDescription(value);
        }

        value = getString("format", node, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            schema.setFormat(value);
        }

        //sets default value according to the schema type
        if(node.get("default")!= null) {
            if(!StringUtils.isBlank(schema.getType())) {
                if (schema.getType().equals("array")) {
                    ArrayNode array = getArray("default", node, false, location, result);
                    if (array != null) {
                        schema.setDefault(array);
                    }
                } else if (schema.getType().equals("string")) {
                    value = getString("default", node, false, location, result);
                    if (value != null) {
                        try {
                            schema.setDefault(getDecodedObject(schema, value));
                        } catch (ParseException e) {
                            result.invalidType(location, String.format("default=`%s`", e.getMessage()), schema.getFormat(), node);
                        }
                    }
                } else if (schema.getType().equals("boolean")) {
                    bool = getBoolean("default", node, false, location, result);
                    if (bool != null) {
                        schema.setDefault(bool);
                    }
                } else if (schema.getType().equals("object")) {
                    Object object = getObject("default", node, false, location, result);
                    if (object != null) {
                        schema.setDefault(object);
                    }
                } else if (schema.getType().equals("integer")) {
                    Integer number = getInteger("default", node, false, location, result);
                    if (number != null) {
                        schema.setDefault(number);
                    }
                } else if (schema.getType().equals("number")) {
                    BigDecimal number = getBigDecimal("default", node, false, location, result);
                    if (number != null) {
                        schema.setDefault(number);
                    }
                }
            }
        }


        bool = getBoolean("nullable", node, false, location, result);
        if(bool != null) {
            schema.setNullable(bool);
        }

        bool = getBoolean("readOnly", node, false, location, result);
        if(bool != null) {
            schema.setReadOnly(bool);
        }

        bool = getBoolean("writeOnly", node, false, location, result);
        if(bool != null){
            schema.setWriteOnly(bool);
        }

        ObjectNode xmlNode = getObject("xml", node, false, location, result);
        if (xmlNode != null) {
            XML xml = getXml(xmlNode, location, result);
            if (xml != null) {
                schema.setXml(xml);
            }
        }

        ObjectNode externalDocs = getObject("externalDocs", node, false, location, result);
        if(externalDocs != null) {
            ExternalDocumentation docs = getExternalDocs(externalDocs, location , result);
            if(docs != null) {
                schema.setExternalDocs(docs);
            }
        }

        Object example = getAnyExample("example", node, location,result);
        if (example != null){
            schema.setExample(example);
        }

        bool = getBoolean("deprecated", node, false, location, result);
        if(bool != null){
            schema.setDeprecated(bool);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            schema.setExtensions(extensions);
        }

        Set schemaKeys = getKeys(node);
        for(String key : schemaKeys) {
            if(!SCHEMA_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return schema;

    }


    /**
     * Decodes the given string and returns an object applicable to the given schema.
     * Throws a ParseException if no applicable object can be recognized.
     */
    private Object getDecodedObject( Schema schema, String objectString) throws ParseException {
        Object object = 
            objectString == null?
            null :

            schema.getClass().equals( DateSchema.class)?
            toDate( objectString) :

            schema.getClass().equals( DateTimeSchema.class)?
            toDateTime( objectString) :

            schema.getClass().equals( ByteArraySchema.class)?
            toBytes( objectString) :

            objectString;

        if( object == null && objectString != null) {
            throw new ParseException( objectString, 0);
        }

        return object;
    }


    /**
     * Returns the Date represented by the given RFC3339 date-time string.
     * Returns null if this string can't be parsed as Date.
     */
    private Date toDateTime( String dateString) {
        // Note: For this conversion, regex matching is better than SimpleDateFormat, etc.
        // Optional elements (e.g. milliseconds) are not directly handled by SimpleDateFormat.
        // Also, SimpleDateFormat is not thread-safe.
        Matcher matcher = RFC3339_DATE_TIME_PATTERN.matcher( dateString);

        Date dateTime = null;
        if( matcher.matches()) {
            try {
                String year = matcher.group(1);
                String month = matcher.group(2);
                String day = matcher.group(3);
                String hour = matcher.group(4);
                String min = matcher.group(5);
                String sec = matcher.group(6);
                String ms = matcher.group(7);
                String zone = matcher.group(10);

                Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone( zone == null? "GMT" : "GMT" + zone));
                calendar.set( YEAR, Integer.parseInt( year));
                calendar.set( MONTH, Integer.parseInt( month) - 1);
                calendar.set( DAY_OF_MONTH, Integer.parseInt( day));
                calendar.set( HOUR_OF_DAY, Integer.parseInt( hour));
                calendar.set( MINUTE, Integer.parseInt( min));
                calendar.set( SECOND, Integer.parseInt( sec));
                calendar.set( MILLISECOND, ms == null? 0 : (int) (Double.parseDouble( ms) * 1000));

                dateTime = calendar.getTime();
            }
            catch( Exception ignore) {
            }
        }

        return dateTime;
    }
    

  /**
   * Returns the Date represented by the given RFC3339 full-date string.
   * Returns null if this string can't be parsed as Date.
   */
    private Date toDate( String dateString) {
        Matcher matcher = RFC3339_DATE_PATTERN.matcher( dateString);

        Date date = null;
        if( matcher.matches()) {
            String year = matcher.group(1);
            String month = matcher.group(2);
            String day = matcher.group(3);

            try {
                date=
                    new Calendar.Builder()
                    .setDate( Integer.parseInt( year), Integer.parseInt( month) - 1, Integer.parseInt( day))
                    .build()
                    .getTime();
            }
            catch( Exception ignore) {
            }
        }

        return date;
    }
    

  /**
   * Returns the byte array represented by the given base64-encoded string.
   * Returns null if this string is not a valid base64 encoding.
   */
    private byte[] toBytes( String byteString) {
        byte[] bytes;
        
        try {
            bytes = Base64.getDecoder().decode( byteString);
        }
        catch( Exception e) {
            bytes = null;
        }

        return bytes;
    }




    public Map getExamples(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map examples = new LinkedHashMap<>();

        Set exampleKeys = getKeys(obj);
        for(String exampleName : exampleKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            exampleName)) {
                result.warning(location, "Example name "+ exampleName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }

            JsonNode exampleValue = obj.get(exampleName);
            if (!exampleValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, exampleName, "object", exampleValue);
            } else {
                ObjectNode example = (ObjectNode) exampleValue;
                if(example != null) {
                    Example exampleObj = getExample(example, String.format("%s.%s", location, exampleName), result);
                    if(exampleObj != null) {
                        examples.put(exampleName, exampleObj);
                    }
                }
            }
        }
        return examples;
    }

    public List getExampleList(ArrayNode obj, String location, ParseResult result) {
        List examples = new ArrayList<>();
        if (obj == null) {
            return examples;
        }
        for (JsonNode item : obj) {
            if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
                Example example = getExample((ObjectNode) item, location, result);
                if (example != null) {
                    examples.add(example);
                }
            }
        }
        return examples;
    }

    public Example getExample(ObjectNode node, String location, ParseResult result) {
        if (node == null)
            return null;

        Example example = new Example();

        JsonNode ref = node.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    example.set$ref(mungedRef);
                }else{
                    example.set$ref(ref.textValue());
                }
                return example;
            } else {
                result.invalidType(location, "$ref", "string", node);
                return null;
            }
        }

        String value = getString("summary", node, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            example.setSummary(value);
        }

        value = getString("description", node, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            example.setDescription(value);
        }

        Object sample = getAnyExample("value", node, location,result);
        if (sample != null){
            example.setValue(sample);
        }


        value = getString("externalValue", node, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            example.setExternalValue(value);
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            example.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!EXAMPLE_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }


        return example;
    }


    public void setStyle(String value, Parameter parameter, String location, ObjectNode obj, ParseResult result) {
        if (StringUtils.isBlank(value)) {
            if (QUERY_PARAMETER.equals(parameter.getIn()) || COOKIE_PARAMETER.equals(parameter.getIn())) {
                parameter.setStyle(Parameter.StyleEnum.FORM);
            } else if (PATH_PARAMETER.equals(parameter.getIn()) || HEADER_PARAMETER.equals(parameter.getIn())) {
                parameter.setStyle(Parameter.StyleEnum.SIMPLE);
            }
        } else {
            if (value.equals(Parameter.StyleEnum.FORM.toString())) {
                parameter.setStyle(Parameter.StyleEnum.FORM);
            } else if (value.equals(Parameter.StyleEnum.DEEPOBJECT.toString())) {
                parameter.setStyle(Parameter.StyleEnum.DEEPOBJECT);
            } else if (value.equals(Parameter.StyleEnum.LABEL.toString())) {
                parameter.setStyle(Parameter.StyleEnum.LABEL);
            } else if (value.equals(Parameter.StyleEnum.MATRIX.toString())) {
                parameter.setStyle(Parameter.StyleEnum.MATRIX);
            } else if (value.equals(Parameter.StyleEnum.PIPEDELIMITED.toString())) {
                parameter.setStyle(Parameter.StyleEnum.PIPEDELIMITED);
            } else if (value.equals(Parameter.StyleEnum.SIMPLE.toString())) {
                parameter.setStyle(Parameter.StyleEnum.SIMPLE);
            } else if (value.equals(Parameter.StyleEnum.SPACEDELIMITED.toString())) {
                parameter.setStyle(Parameter.StyleEnum.SPACEDELIMITED);
            } else {
                result.invalidType(location, "style", "string", obj);
            }
        }
    }

    public ApiResponses getResponses(ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        ApiResponses apiResponses = new ApiResponses();
        Set keys = getKeys(node);

        for (String key : keys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            key)) {
                result.warning(location, "Response key "+ key + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }

            if (key.startsWith("x-")) {
                Map  extensions = getExtensions(node);
                if(extensions != null && extensions.size() > 0) {
                    apiResponses.setExtensions(extensions);
                }
            } else {
                ObjectNode obj = getObject(key, node, false, String.format("%s.%s", location, "responses"), result);
                if (obj != null) {
                    ApiResponse response = getResponse(obj, String.format("%s.%s", location, key), result);
                    if (response != null) {
                        apiResponses.put(key, response);
                    }
                }
            }
        }

        return apiResponses;
    }

    public ApiResponse getResponse(ObjectNode node, String location, ParseResult result) {
        if (node == null) {
            return null;
        }

        ApiResponse apiResponse = new ApiResponse();
        JsonNode ref = node.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    apiResponse.set$ref(mungedRef);
                }else{
                    apiResponse.set$ref(ref.textValue());
                }
                Map  extensions = getExtensions(node);
                if (extensions != null && extensions.size() > 0) {
                  apiResponse.setExtensions(extensions);
                }
                return apiResponse;
            } else {
                result.invalidType(location, "$ref", "string", node);
                return null;
            }
        }

        String value = getString("description", node, true, location, result);
        if (StringUtils.isNotBlank(value)) {
            apiResponse.description(value);
        }


        ObjectNode headerObject = getObject("headers", node, false, location, result);
        if (headerObject != null) {
            Map headers = getHeaders(headerObject, location, result);
            if (headers != null &&  headers.size() > 0) {
                apiResponse.setHeaders(headers);
            }
        }

        ObjectNode linksObj = getObject("links", node, false, location, result);
        if (linksObj != null) {
             Map links = getLinks(linksObj, location, result);
             if(links != null && links.size() > 0) {
                 apiResponse.setLinks(links);
             }
        }

        ObjectNode contentObject = getObject("content", node, false, location, result);
        if (contentObject != null) {
            apiResponse.setContent(getContent(contentObject, String.format("%s.%s", location, "content"), result));
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            apiResponse.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!RESPONSE_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }


        return apiResponse;
    }

    public List getTagsStrings(ArrayNode nodes, String location, ParseResult result) {
        if (nodes == null)
            return null;

        List tags = new ArrayList<>();

        for (JsonNode node : nodes) {
            if (node.getNodeType().equals(JsonNodeType.STRING)) {
                tags.add(node.textValue());
            }
        }
        return tags;
    }




    public Operation getOperation(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Operation operation = new Operation();

        ArrayNode array = getArray("tags", obj, false, location, result);
        List tags = getTagsStrings(array, String.format("%s.%s", location, "tags"), result);
        if (tags != null) {
            operation.setTags(tags);
        }
        String value = getString("summary", obj, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            operation.setSummary(value);
        }

        value = getString("description", obj, false, location, result);
        if (StringUtils.isNotBlank(value)) {
            operation.setDescription(value);
        }

        ObjectNode externalDocs = getObject("externalDocs", obj, false, location, result);
        ExternalDocumentation docs = getExternalDocs(externalDocs, String.format("%s.%s", location, "externalDocs"), result);
        if(docs != null) {
            operation.setExternalDocs(docs);
        }
        value = getString("operationId", obj, false, location, result, operationIDs);
        if (StringUtils.isNotBlank(value)) {
            operation.operationId(value);
        }

        ArrayNode parameters = getArray("parameters", obj, false, location, result);
        if (parameters != null){
            operation.setParameters(getParameterList(parameters, String.format("%s.%s", location, "parameters"), result));
        }

        final ObjectNode requestObjectNode = getObject("requestBody", obj, false, location, result);
        if (requestObjectNode != null){
            operation.setRequestBody(getRequestBody(requestObjectNode, String.format("%s.%s", location, "requestBody"), result));
        }

        ObjectNode responsesNode = getObject("responses", obj, true, location, result);
        ApiResponses responses = getResponses(responsesNode, String.format("%s.%s", location, "responses"), result);
        if(responses != null) {
            operation.setResponses(responses);
        }

        ObjectNode callbacksNode = getObject("callbacks", obj, false, location, result);
        Map callbacks = getCallbacks(callbacksNode, String.format("%s.%s", location, "callbacks"), result);
        if(callbacks != null){
            operation.setCallbacks(callbacks);
        }

        Boolean deprecated = getBoolean("deprecated", obj, false, location, result);
        if (deprecated != null) {
            operation.setDeprecated(deprecated);
        }

        array = getArray("servers", obj, false, location, result);
        if (array != null && array.size() > 0) {
            operation.setServers(getServersList(array, String.format("%s.%s", location, "servers"), result));
        }


        array = getArray("security", obj, false, location, result);
        if (array != null) {
            operation.setSecurity(getSecurityRequirementsList(array, String.format("%s.%s", location, "security"), result));
        }


        Map  extensions = getExtensions(obj);
        if(extensions != null && extensions.size() > 0) {
            operation.setExtensions(extensions);
        }

        Set keys = getKeys(obj);
        for(String key : keys) {
            if(!OPERATION_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, obj.get(key));
            }
        }


        return operation;
    }

    public List getSecurityRequirementsList(ArrayNode nodes, String location, ParseResult result) {
        if (nodes == null)
            return null;

        List securityRequirements = new ArrayList<>();

        for (JsonNode node : nodes) {
            if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
                SecurityRequirement securityRequirement = new SecurityRequirement();
                Set keys = getKeys((ObjectNode) node);
                if (keys.size() == 0){
                    securityRequirements.add(securityRequirement);
                }else {
                    for (String key : keys) {
                        if (key != null) {
                            JsonNode value = node.get(key);
                            if (key != null && JsonNodeType.ARRAY.equals(value.getNodeType())) {
                                ArrayNode arrayNode = (ArrayNode) value;
                                List scopes = Stream
                                        .generate(arrayNode.elements()::next)
                                        .map((n) -> n.asText())
                                        .limit(arrayNode.size())
                                        .collect(Collectors.toList());
                                securityRequirement.addList(key, scopes);
                            }
                        }
                    }
                    if (securityRequirement.size() > 0) {
                        securityRequirements.add(securityRequirement);
                    }
                }
            }
        }

        return securityRequirements;

    }

    public Map getRequestBodies(ObjectNode obj, String location, ParseResult result) {
        if (obj == null) {
            return null;
        }
        Map bodies = new LinkedHashMap<>();

        Set bodyKeys = getKeys(obj);
        for(String bodyName : bodyKeys) {
            if (!Pattern.matches("^[a-zA-Z0-9\\.\\-_]+$",
                            bodyName)) {
                result.warning(location, "RequestBody name "+ bodyName + " doesn't adhere to regular expression ^[a-zA-Z0-9\\.\\-_]+$");
            }
            JsonNode bodyValue = obj.get(bodyName);
            if (!bodyValue.getNodeType().equals(JsonNodeType.OBJECT)) {
                result.invalidType(location, bodyName, "object", bodyValue);
            } else {
                ObjectNode bodyObj = (ObjectNode) bodyValue;
                RequestBody body = getRequestBody(bodyObj, String.format("%s.%s", location, bodyName), result);
                if(body != null) {
                    bodies.put(bodyName, body);
                }
            }
        }
        return bodies;
    }


    protected RequestBody getRequestBody(ObjectNode node, String location, ParseResult result) {
        if (node == null){
            return null;
        }
        final RequestBody body = new RequestBody();


        JsonNode ref = node.get("$ref");
        if (ref != null) {
            if (ref.getNodeType().equals(JsonNodeType.STRING)) {
                String mungedRef = mungedRef(ref.textValue());
                if (mungedRef != null) {
                    body.set$ref(mungedRef);
                }else{
                    body.set$ref(ref.textValue());
                }
                Map  extensions = getExtensions(node);
                if(extensions != null && extensions.size() > 0) {
                  body.setExtensions(extensions);
                }
                return body;
            } else {
                result.invalidType(location, "$ref", "string", node);
                return null;
            }
        }




        final String description = getString("description", node, false, location, result);
        if (StringUtils.isNotBlank(description)) {
            body.setDescription(description);
        }

        final Boolean required = getBoolean("required", node, false, location, result);
        if(required != null) {
            body.setRequired(required);
        }

        final ObjectNode contentNode = getObject("content", node, true, location, result);
        if (contentNode != null) {
            body.setContent(getContent(contentNode, location + ".content", result));
        }

        Map  extensions = getExtensions(node);
        if(extensions != null && extensions.size() > 0) {
            body.setExtensions(extensions);
        }

        Set keys = getKeys(node);
        for(String key : keys) {
            if(!REQUEST_BODY_KEYS.contains(key) && !key.startsWith("x-")) {
                result.extra(location, key, node.get(key));
            }
        }

        return body;
    }

    public String inferTypeFromArray(ArrayNode an) {
        if(an.size() == 0) {
            return "string";
        }
        String type = null;
        for(int i = 0; i < an.size(); i++) {
            JsonNode element = an.get(0);
            if(element.isBoolean()) {
                if(type == null) {
                    type = "boolean";
                }
                else if(!"boolean".equals(type)) {
                    type = "string";
                }
            }
            else if(element.isNumber()) {
                if(type == null) {
                    type = "number";
                }
                else if(!"number".equals(type)) {
                    type = "string";
                }
            }
            else {
                type = "string";
            }
        }

        return type;
    }


    protected static class ParseResult {
        private boolean valid = true;
        private Map extra = new LinkedHashMap<>();
        private Map unsupported = new LinkedHashMap<>();
        private Map invalidType = new LinkedHashMap<>();
        private List missing = new ArrayList<>();
        private List warnings = new ArrayList<>();
        private List unique = new ArrayList<>();
        private List uniqueTags = new ArrayList<>();

        public ParseResult() {
        }

        public void unsupported(String location, String key, JsonNode value) {
            unsupported.put(new Location(location, key), value);
        }

        public void extra(String location, String key, JsonNode value) {
            extra.put(new Location(location, key), value);
        }

        public void missing(String location, String key) {
            missing.add(new Location(location, key));
        }
      
        public void warning(String location, String key) {
            warnings.add(new Location(location, key));
        }
      
        public void unique(String location, String key) {
            unique.add(new Location(location, key));

        }

        public void uniqueTags(String location, String key) {uniqueTags.add(new Location(location,key));}

        public void invalidType(String location, String key, String expectedType, JsonNode value) {
            invalidType.put(new Location(location, key), expectedType);
        }

        public void invalid() {
            this.valid = false;
        }

        public boolean isValid() {
          return this.valid;
        }

        public List getMessages() {
            List messages = new ArrayList();
            for (Location l : extra.keySet()) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is unexpected";
                messages.add(message);
            }
            for (Location l : invalidType.keySet()) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is not of type `" + invalidType.get(l) + "`";
                messages.add(message);
            }
            for (Location l : missing) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is missing";
                messages.add(message);
            }
            for (Location l : warnings) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location +l.key;
                messages.add(message);
            }
            for (Location l : unsupported.keySet()) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is unsupported";
                messages.add(message);
            }
            for (Location l : unique) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is repeated";
                messages.add(message);
            }
            for (Location l : uniqueTags) {
                String location = l.location.equals("") ? "" : l.location + ".";
                String message = "attribute " + location + l.key + " is repeated";
                messages.add(message);
            }
            return messages;
        }
    }


    protected static class Location {
        private String location;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Location)) return false;

            Location location1 = (Location) o;

            if (location != null ? !location.equals(location1.location) : location1.location != null) return false;
            return !(key != null ? !key.equals(location1.key) : location1.key != null);

        }

        @Override
        public int hashCode() {
            int result = location != null ? location.hashCode() : 0;
            result = 31 * result + (key != null ? key.hashCode() : 0);
            return result;
        }

        private String key;

        public Location(String location, String key) {
            this.location = location;
            this.key = key;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy