public static void registerObjectsExportedDefinitions(
      @NotNull VirtualFile key,
      @NotNull final JsonSchemaExportedDefinitions definitionsObject,
      @NotNull final JsonSchemaObject object) {
    String id = object.getId();
    if (!StringUtil.isEmptyOrSpaces(id)) {
      id = id.endsWith("#") ? id.substring(0, id.length() - 1) : id;
      final BiFunction<String, Map<String, JsonSchemaObject>, Map<String, JsonSchemaObject>>
          convertor =
              (s, map) -> {
                final Map<String, JsonSchemaObject> converted = new HashMap<>();
                for (Map.Entry<String, JsonSchemaObject> entry : map.entrySet()) {
                  String key1 = entry.getKey();
                  key1 = key1.startsWith("/") ? key1.substring(1) : key1;
                  converted.put(s + key1, entry.getValue());
                }
                return converted;
              };

      final HashMap<String, JsonSchemaObject> map = new HashMap<>();
      map.put("", object);
      final Map<String, JsonSchemaObject> definitions = object.getDefinitions();
      if (definitions != null && !definitions.isEmpty()) {
        map.putAll(convertor.apply("#/definitions/", definitions));
      }
      final Map<String, JsonSchemaObject> properties = object.getProperties();
      if (properties != null && !properties.isEmpty()) {
        map.putAll(convertor.apply("#/properties/", properties));
      }
      definitionsObject.register(key, id, map);
    }
  }
  @NotNull
  public static JsonSchemaObject findRelativeDefinition(
      @NotNull String ref, @NotNull JsonSchemaObject root) {
    if ("#".equals(ref)) {
      return root;
    }
    if (!ref.startsWith("#/"))
      throw new RuntimeException("Non-relative or erroneous reference: " + ref);
    ref = ref.substring(2);
    final String[] parts = ref.split("/");
    JsonSchemaObject current = root;
    for (int i = 0; i < parts.length; i++) {
      if (current == null) throw new RuntimeException("Incorrect reference: " + ref);
      final String part = parts[i];
      if ("definitions".equals(part)) {
        if (i == (parts.length - 1))
          throw new RuntimeException("Incorrect definition reference: " + ref);
        //noinspection AssignmentToForLoopParameter
        current = current.getDefinitions().get(parts[++i]);
        continue;
      }
      if ("properties".equals(part)) {
        if (i == (parts.length - 1))
          throw new RuntimeException("Incorrect properties reference: " + ref);
        //noinspection AssignmentToForLoopParameter
        current = current.getProperties().get(parts[++i]);
        continue;
      }

      current = current.getDefinitions().get(part);
    }
    if (current == null) throw new RuntimeException("Incorrect reference: " + ref);
    return current;
  }
  private static void removeDefinitions(
      JsonSchemaObject root, ArrayList<JsonSchemaObject> objects) {
    final List<JsonSchemaObject> queue = new ArrayList<>(objects.size() + 1);
    queue.addAll(objects);
    queue.add(root);

    for (JsonSchemaObject object : queue) {
      final Map<String, JsonSchemaObject> definitions = object.getDefinitions();
      if (definitions != null) {
        objects.removeAll(definitions.values());
      }
    }
  }
 @Override
 public JsonSchemaObject read(JsonReader in) throws IOException {
   in.beginObject();
   final JsonSchemaObject object = new JsonSchemaObject();
   while (in.peek() == JsonToken.NAME) {
     final String name = in.nextName();
     readSomeProperty(in, name, object);
   }
   in.endObject();
   myAllObjects.add(object);
   if (object.getId() != null) {
     myIds.put(object.getId(), object);
   }
   return object;
 }
 void readSingleDefinition(JsonReader in, String name, JsonSchemaObject object)
     throws IOException {
   if (in.peek() != JsonToken.BEGIN_OBJECT) {
     in
         .skipValue(); // if unknown property has non-object value, than it is not a definition,
                       // lets ignore it
     return;
   }
   final JsonSchemaObject defined = read(in);
   if (defined == null) return;
   Map<String, JsonSchemaObject> definitions = object.getDefinitions();
   if (definitions == null) {
     object.setDefinitions(definitions = new HashMap<>());
   }
   definitions.put(name, defined);
 }
  public static boolean process(
      @NotNull PropertyProcessor processor, @NotNull JsonSchemaObject startSchema) {
    if (!processForVariants(processor, startSchema.getAnyOf())
        || !processForVariants(processor, startSchema.getOneOf())
        || !processForVariants(processor, startSchema.getAllOf())) {
      return false;
    }

    final Map<String, JsonSchemaObject> properties = startSchema.getProperties();
    for (Map.Entry<String, JsonSchemaObject> entry : properties.entrySet()) {
      if (!processor.process(entry.getKey(), entry.getValue())) {
        return false;
      }
    }

    return true;
  }
  private void processReferences(
      JsonSchemaObject root,
      Set<JsonSchemaObject> objects,
      @Nullable JsonSchemaExportedDefinitions definitions) {
    final ArrayDeque<JsonSchemaObject> queue = new ArrayDeque<>();
    queue.addAll(objects);
    int control = 10000;

    while (!queue.isEmpty()) {
      if (--control == 0) throw new RuntimeException("cyclic definitions search");

      final JsonSchemaObject current = queue.removeFirst();
      if ("#".equals(current.getRef())) continue;
      if (current.getRef() != null) {
        final JsonSchemaObject definition =
            findDefinition(myKey, current.getRef(), root, definitions);
        if (definition == null) {
          if (definitions == null) {
            // just skip current item
            current.setRef(null);
            continue;
          }
          throw new RuntimeException("Can not find definition: " + current.getRef());
        }
        if (definition.getRef() != null && !"#".equals(definition.getRef())) {
          queue.addFirst(current);
          queue.addFirst(definition);
          continue;
        }

        final JsonSchemaObject copy = new JsonSchemaObject();
        copy.setDefinitionAddress(current.getRef());
        copy.mergeValues(definition);
        copy.mergeValues(current);
        current.copyValues(copy);
        current.setRef(null);
      }
    }
  }