public List<SecurityRequirement> securityRequirements(
      ArrayNode node, String location, ParseResult result) {
    if (node == null) return null;

    List<SecurityRequirement> output = new ArrayList<SecurityRequirement>();

    for (JsonNode item : node) {
      SecurityRequirement security = new SecurityRequirement();
      if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
        ObjectNode on = (ObjectNode) item;
        Set<String> keys = getKeys(on);

        for (String key : keys) {
          List<String> scopes = new ArrayList<>();
          ArrayNode obj = getArray(key, on, false, location + ".security", result);
          if (obj != null) {
            for (JsonNode n : obj) {
              if (n.getNodeType().equals(JsonNodeType.STRING)) {
                scopes.add(n.asText());
              } else {
                result.invalidType(location, key, "string", n);
              }
            }
          }
          security.requirement(key, scopes);
        }
      }
      output.add(security);
    }

    return output;
  }
  public Model allOfModel(ObjectNode node, String location, ParseResult result) {
    JsonNode sub = node.get("$ref");
    JsonNode allOf = node.get("allOf");

    if (sub != null) {
      if (sub.getNodeType().equals(JsonNodeType.OBJECT)) {
        return refModel((ObjectNode) sub, location, result);
      } else {
        result.invalidType(location, "$ref", "object", sub);
        return null;
      }
    } else if (allOf != null) {
      ComposedModel model = null;
      // we only support one parent, no multiple inheritance or composition
      if (allOf.getNodeType().equals(JsonNodeType.ARRAY)) {
        model = new ComposedModel();
        int pos = 0;
        for (JsonNode part : allOf) {
          if (part.getNodeType().equals(JsonNodeType.OBJECT)) {
            Model segment = definition((ObjectNode) part, location, result);
            if (segment != null) {
              model.getAllOf().add(segment);
            }
          } else {
            result.invalidType(location, "allOf[" + pos + "]", "object", part);
          }
          pos++;
        }

        List<Model> allComponents = model.getAllOf();
        if (allComponents.size() >= 1) {
          model.setParent(allComponents.get(0));
          if (allComponents.size() >= 2) {
            model.setChild(allComponents.get(allComponents.size() - 1));
            List<RefModel> interfaces = new ArrayList<RefModel>();
            int size = allComponents.size();
            for (Model m : allComponents.subList(1, size - 1)) {
              if (m instanceof RefModel) {
                RefModel ref = (RefModel) m;
                interfaces.add(ref);
              }
            }
            model.setInterfaces(interfaces);
          } else {
            model.setChild(new ModelImpl());
          }
        }
        return model;
      } else {
        result.invalidType(location, "allOf", "array", allOf);
      }

      return model;
    }
    return null;
  }
 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 List<String> tagStrings(ArrayNode nodes, String location, ParseResult result) {
    if (nodes == null) return null;

    List<String> output = new ArrayList<String>();

    for (JsonNode node : nodes) {
      if (node.getNodeType().equals(JsonNodeType.STRING)) {
        output.add(node.textValue());
      }
    }
    return output;
  }
 public List<Parameter> parameters(ArrayNode obj, String location, ParseResult result) {
   List<Parameter> output = new ArrayList<Parameter>();
   if (obj == null) {
     return output;
   }
   for (JsonNode item : obj) {
     if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
       Parameter param = parameter((ObjectNode) item, location, result);
       if (param != null) {
         output.add(param);
       }
     }
   }
   return output;
 }
  public List<Tag> tags(ArrayNode nodes, String location, ParseResult result) {
    if (nodes == null) return null;

    List<Tag> output = new ArrayList<Tag>();

    for (JsonNode node : nodes) {
      if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
        Tag tag = tag((ObjectNode) node, location + ".tags", result);
        if (tag != null) {
          output.add(tag);
        }
      }
    }

    return output;
  }
 public Object extension(JsonNode jsonNode) {
   if (jsonNode.getNodeType().equals(JsonNodeType.BOOLEAN)) {
     return jsonNode.asBoolean();
   }
   if (jsonNode.getNodeType().equals(JsonNodeType.STRING)) {
     return jsonNode.asText();
   }
   if (jsonNode.getNodeType().equals(JsonNodeType.NUMBER)) {
     NumericNode n = (NumericNode) jsonNode;
     if (n.isLong()) {
       return jsonNode.asLong();
     }
     if (n.isInt()) {
       return jsonNode.asInt();
     }
     if (n.isBigDecimal()) {
       return jsonNode.textValue();
     }
     if (n.isBoolean()) {
       return jsonNode.asBoolean();
     }
     if (n.isFloat()) {
       return jsonNode.floatValue();
     }
     if (n.isDouble()) {
       return jsonNode.doubleValue();
     }
     if (n.isShort()) {
       return jsonNode.intValue();
     }
     return jsonNode.asText();
   }
   if (jsonNode.getNodeType().equals(JsonNodeType.ARRAY)) {
     ArrayNode an = (ArrayNode) jsonNode;
     List<Object> o = new ArrayList<Object>();
     for (JsonNode i : an) {
       Object obj = extension(i);
       if (obj != null) {
         o.add(obj);
       }
     }
     return o;
   }
   return jsonNode;
 }
  public Model definition(ObjectNode node, String location, ParseResult result) {
    if (node == null) {
      result.missing(location, "empty schema");
    }
    if (node.get("$ref") != null) {
      return refModel(node, location, result);
    }
    if (node.get("allOf") != null) {
      return allOfModel(node, location, result);
    }
    Model model = null;
    String value = null;

    String type = getString("type", node, false, location, result);
    Model m = new ModelImpl();
    if ("array".equals(type)) {
      ArrayModel am = new ArrayModel();
      ObjectNode propertyNode = getObject("properties", node, false, location, result);
      Map<String, Property> properties = properties(propertyNode, location, result);
      am.setProperties(properties);

      ObjectNode itemsNode = getObject("items", node, false, location, result);
      Property items = property(itemsNode, location, result);
      if (items != null) {
        am.items(items);
      }

      model = am;
    } else {
      ModelImpl impl = new ModelImpl();
      impl.setType(value);

      JsonNode ap = node.get("additionalProperties");
      if (ap != null && ap.getNodeType().equals(JsonNodeType.OBJECT)) {
        impl.setAdditionalProperties(Json.mapper().convertValue(ap, Property.class));
      }

      value = getString("default", node, false, location, result);
      impl.setDefaultValue(value);

      value = getString("format", node, false, location, result);
      impl.setFormat(value);

      value = getString("discriminator", node, false, location, result);
      impl.setDiscriminator(value);

      JsonNode xml = node.get("xml");
      if (xml != null) {
        impl.setXml(Json.mapper().convertValue(xml, Xml.class));
      }

      ObjectNode externalDocs = getObject("externalDocs", node, false, location, result);
      ExternalDocs docs = externalDocs(externalDocs, location, result);
      impl.setExternalDocs(docs);

      ObjectNode properties = getObject("properties", node, true, location, result);
      if (properties != null) {
        Set<String> propertyNames = getKeys(properties);
        for (String propertyName : propertyNames) {
          JsonNode propertyNode = properties.get(propertyName);
          if (propertyNode.getNodeType().equals(JsonNodeType.OBJECT)) {
            ObjectNode on = (ObjectNode) propertyNode;
            Property property = property(on, location, result);
            impl.property(propertyName, property);
          } else {
            result.invalidType(location, "properties", "object", propertyNode);
          }
        }
      }

      // need to set properties first
      ArrayNode required = getArray("required", node, false, location, result);
      if (required != null) {
        List<String> requiredProperties = new ArrayList<String>();
        for (JsonNode n : required) {
          if (n.getNodeType().equals(JsonNodeType.STRING)) {
            requiredProperties.add(((TextNode) n).textValue());
          } else {
            result.invalidType(location, "required", "string", n);
          }
        }
        if (requiredProperties.size() > 0) {
          impl.setRequired(requiredProperties);
        }
      }

      // extra keys
      Set<String> keys = getKeys(node);
      for (String key : keys) {
        if (key.startsWith("x-")) {
          impl.setVendorExtension(key, extension(node.get(key)));
        } else if (!SCHEMA_KEYS.contains(key)) {
          result.extra(location, key, node.get(key));
        }
      }
      if ("{ }".equals(Json.pretty(impl))) return null;
      model = impl;
    }
    JsonNode exampleNode = node.get("example");
    if (exampleNode != null) {
      // we support text or object nodes
      if (exampleNode.getNodeType().equals(JsonNodeType.OBJECT)) {
        ObjectNode on = getObject("example", node, false, location, result);
        if (on != null) {
          model.setExample(on);
        }
      } else {
        model.setExample(exampleNode.asText());
      }
    }

    if (model != null) {
      value = getString("description", node, false, location, result);
      model.setDescription(value);

      value = getString("title", node, false, location, result);
      model.setTitle(value);
    }

    return model;
  }
  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 missing(String location, String key) {
   missing.add(new Location(location, key));
 }