public Map<String, Response> responses(ObjectNode node, String location, ParseResult result) {
    if (node == null) return null;

    Map<String, Response> output = new TreeMap<String, Response>();

    Set<String> keys = getKeys(node);

    for (String key : keys) {
      ObjectNode obj = getObject(key, node, false, location + ".responses", result);
      Response response = response(obj, location + "." + key, result);
      output.put(key, response);
    }

    return output;
  }
  public Map<String, Property> properties(ObjectNode node, String location, ParseResult result) {
    if (node == null) {
      return null;
    }
    Map<String, Property> output = new LinkedHashMap<String, Property>();

    Set<String> keys = getKeys(node);
    for (String propertyName : keys) {
      JsonNode propertyNode = node.get(propertyName);
      if (propertyNode.getNodeType().equals(JsonNodeType.OBJECT)) {
        Property property = property((ObjectNode) propertyNode, location, result);
        output.put(propertyName, property);
      } else {
        result.invalidType(location, propertyName, "object", propertyNode);
      }
    }
    return output;
  }
  public Map<String, SecuritySchemeDefinition> securityDefinitions(
      ObjectNode node, String location, ParseResult result) {
    if (node == null) return null;

    Map<String, SecuritySchemeDefinition> output = new HashMap<String, SecuritySchemeDefinition>();
    Set<String> keys = getKeys(node);

    for (String key : keys) {
      ObjectNode obj = getObject(key, node, false, location, result);
      SecuritySchemeDefinition def = securityDefinition(obj, location, result);

      if (def != null) {
        output.put(key, def);
      }
    }

    return output;
  }
  public Map<String, Model> definitions(ObjectNode node, String location, ParseResult result) {
    if (node == null) return null;
    Set<String> schemas = getKeys(node);
    Map<String, Model> output = new LinkedHashMap<String, Model>();

    for (String schemaName : schemas) {
      JsonNode schema = node.get(schemaName);
      if (schema.getNodeType().equals(JsonNodeType.OBJECT)) {
        Model model = definition((ObjectNode) schema, location + "." + schemaName, result);
        if (model != null) {
          output.put(schemaName, model);
        }
      } else {
        result.invalidType(location, schemaName, "object", schema);
      }
    }
    return output;
  }
  public Map<String, Path> paths(ObjectNode obj, String location, ParseResult result) {
    Map<String, Path> output = new LinkedHashMap<>();
    if (obj == null) {
      return null;
    }

    Set<String> pathKeys = getKeys(obj);
    for (String pathName : pathKeys) {
      JsonNode pathValue = obj.get(pathName);
      if (!pathValue.getNodeType().equals(JsonNodeType.OBJECT)) {
        result.invalidType(location, pathName, "object", pathValue);
      } else {
        ObjectNode path = (ObjectNode) pathValue;
        Path pathObj = path(path, location + ".'" + pathName + "'", result);
        output.put(pathName, pathObj);
      }
    }
    return output;
  }
 public List<String> getMessages() {
   List<String> messages = new ArrayList<String>();
   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);
   }
   return messages;
 }
  public Parameter parameter(ObjectNode obj, String location, ParseResult result) {
    if (obj == null) {
      return null;
    }

    Parameter output = null;
    JsonNode ref = obj.get("$ref");
    if (ref != null) {
      if (ref.getNodeType().equals(JsonNodeType.STRING)) {
        return refParameter((TextNode) ref, location, result);
      } 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 (value != null) {
      String type = getString("type", obj, false, location, result);
      String format = getString("format", obj, false, location, result);
      AbstractSerializableParameter<?> sp = null;
      if ("query".equals(value)) {
        sp = new QueryParameter();
      } else if ("header".equals(value)) {
        sp = new HeaderParameter();
      } else if ("path".equals(value)) {
        sp = new PathParameter();
      } else if ("formData".equals(value)) {
        sp = new FormParameter();
      }

      if (sp != null) {
        // type is mandatory when sp != null
        getString("type", obj, true, location, result);
        Map<PropertyBuilder.PropertyId, Object> map =
            new HashMap<PropertyBuilder.PropertyId, Object>();

        map.put(TYPE, type);
        map.put(FORMAT, format);
        String defaultValue = getString("default", obj, false, location, result);
        map.put(DEFAULT, defaultValue);
        sp.setDefault(defaultValue);

        Double dbl = getDouble("maximum", obj, false, location, result);
        if (dbl != null) {
          map.put(MAXIMUM, dbl);
          sp.setMaximum(dbl);
        }

        Boolean bl = getBoolean("exclusiveMaximum", obj, false, location, result);
        if (bl != null) {
          map.put(EXCLUSIVE_MAXIMUM, bl);
          sp.setExclusiveMaximum(bl);
        }

        dbl = getDouble("minimum", obj, false, location, result);
        if (dbl != null) {
          map.put(MINIMUM, dbl);
          sp.setMinimum(dbl);
        }

        bl = getBoolean("exclusiveMinimum", obj, false, location, result);
        if (bl != null) {
          map.put(EXCLUSIVE_MINIMUM, bl);
          sp.setExclusiveMinimum(bl);
        }

        map.put(MAX_LENGTH, getInteger("maxLength", obj, false, location, result));
        map.put(MIN_LENGTH, getInteger("minLength", obj, false, location, result));

        String pat = getString("pattern", obj, false, location, result);
        map.put(PATTERN, pat);
        sp.setPattern(pat);

        Integer iv = getInteger("maxItems", obj, false, location, result);
        map.put(MAX_ITEMS, iv);
        sp.setMaxItems(iv);

        iv = getInteger("minItems", obj, false, location, result);
        map.put(MIN_ITEMS, iv);
        sp.setMinItems(iv);

        map.put(UNIQUE_ITEMS, getBoolean("uniqueItems", obj, false, location, result));

        ArrayNode an = getArray("enum", obj, false, location, result);
        if (an != null) {
          List<String> _enum = new ArrayList<String>();
          for (JsonNode n : an) {
            _enum.add(n.textValue());
          }
          sp.setEnum(_enum);
          map.put(ENUM, _enum);
        }

        Property prop = PropertyBuilder.build(type, format, map);

        if (prop != null) {
          sp.setProperty(prop);
          ObjectNode items = getObject("items", obj, false, location, result);
          if (items != null) {
            Property inner = schema(null, items, location, result);
            sp.setItems(inner);
          }
        }

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

        String collectionFormat = getString("collectionFormat", obj, false, location, result);
        sp.setCollectionFormat(collectionFormat);

        output = sp;
      } else if ("body".equals(value)) {
        output = Json.mapper().convertValue(obj, Parameter.class);
      }
      if (output != null) {
        value = getString("name", obj, true, location, result);
        output.setName(value);

        value = getString("description", obj, false, location, result);
        output.setDescription(value);

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

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