001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.schemagen.json;
007
008import com.fasterxml.jackson.core.JsonFactory;
009import com.fasterxml.jackson.core.JsonGenerator;
010import com.fasterxml.jackson.core.JsonGenerator.Feature;
011import com.fasterxml.jackson.databind.ObjectMapper;
012import com.fasterxml.jackson.databind.node.ObjectNode;
013
014import java.io.IOException;
015import java.io.Writer;
016
017import dev.metaschema.core.configuration.IConfiguration;
018import dev.metaschema.core.model.IModule;
019import dev.metaschema.core.util.ObjectUtils;
020import dev.metaschema.schemagen.AbstractSchemaGenerator;
021import dev.metaschema.schemagen.SchemaGenerationException;
022import dev.metaschema.schemagen.SchemaGenerationFeature;
023import dev.metaschema.schemagen.json.impl.IJsonSchema;
024import dev.metaschema.schemagen.json.impl.JsonDatatypeManager;
025import dev.metaschema.schemagen.json.impl.JsonGenerationState;
026import dev.metaschema.schemagen.json.impl.JsonSchemaModule;
027import edu.umd.cs.findbugs.annotations.NonNull;
028
029/**
030 * Generates JSON Schema documents from Metaschema modules.
031 * <p>
032 * This generator produces JSON Schema draft-07 compatible schemas that can be
033 * used to validate JSON and YAML content conforming to the Metaschema model.
034 */
035public class JsonSchemaGenerator
036    extends AbstractSchemaGenerator<JsonGenerator, JsonDatatypeManager, JsonGenerationState> {
037  @NonNull
038  private final JsonFactory jsonFactory;
039
040  /**
041   * Constructs a new JSON schema generator using a default JSON factory.
042   */
043  public JsonSchemaGenerator() {
044    this(new JsonFactory());
045  }
046
047  /**
048   * Constructs a new JSON schema generator using the specified JSON factory.
049   *
050   * @param jsonFactory
051   *          the Jackson JSON factory to use for creating JSON generators
052   */
053  public JsonSchemaGenerator(@NonNull JsonFactory jsonFactory) {
054    this.jsonFactory = jsonFactory;
055  }
056
057  /**
058   * Retrieves the JSON factory used by this generator.
059   *
060   * @return the JSON factory instance
061   */
062  @NonNull
063  public JsonFactory getJsonFactory() {
064    return jsonFactory;
065  }
066
067  @SuppressWarnings("resource")
068  @Override
069  protected JsonGenerator newWriter(Writer out) {
070    try {
071      return ObjectUtils.notNull(getJsonFactory().createGenerator(out)
072          .setCodec(new ObjectMapper())
073          .useDefaultPrettyPrinter()
074          .disable(Feature.AUTO_CLOSE_TARGET));
075    } catch (IOException ex) {
076      throw new SchemaGenerationException(ex);
077    }
078  }
079
080  @Override
081  protected JsonGenerationState newGenerationState(
082      IModule module,
083      JsonGenerator schemaWriter,
084      IConfiguration<SchemaGenerationFeature<?>> configuration) {
085    return new JsonGenerationState(module, schemaWriter, configuration);
086  }
087
088  @Override
089  protected void generateSchema(JsonGenerationState state) {
090    IModule module = state.getModule();
091
092    IJsonSchema moduleSchema = new JsonSchemaModule(module, state);
093    ObjectNode schemaNode = ObjectUtils.notNull(state.getJsonNodeFactory().objectNode());
094    moduleSchema.generateInlineJsonSchema(schemaNode, state);
095
096    try {
097      state.writeObject(schemaNode);
098    } catch (IOException ex) {
099      throw new SchemaGenerationException(ex);
100    }
101  }
102}