1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.schemagen.json;
7   
8   import com.fasterxml.jackson.core.JsonFactory;
9   import com.fasterxml.jackson.core.JsonGenerator;
10  import com.fasterxml.jackson.core.JsonGenerator.Feature;
11  import com.fasterxml.jackson.databind.ObjectMapper;
12  import com.fasterxml.jackson.databind.node.ObjectNode;
13  
14  import java.io.IOException;
15  import java.io.Writer;
16  
17  import dev.metaschema.core.configuration.IConfiguration;
18  import dev.metaschema.core.model.IModule;
19  import dev.metaschema.core.util.ObjectUtils;
20  import dev.metaschema.schemagen.AbstractSchemaGenerator;
21  import dev.metaschema.schemagen.SchemaGenerationException;
22  import dev.metaschema.schemagen.SchemaGenerationFeature;
23  import dev.metaschema.schemagen.json.impl.IJsonSchema;
24  import dev.metaschema.schemagen.json.impl.JsonDatatypeManager;
25  import dev.metaschema.schemagen.json.impl.JsonGenerationState;
26  import dev.metaschema.schemagen.json.impl.JsonSchemaModule;
27  import edu.umd.cs.findbugs.annotations.NonNull;
28  
29  /**
30   * Generates JSON Schema documents from Metaschema modules.
31   * <p>
32   * This generator produces JSON Schema draft-07 compatible schemas that can be
33   * used to validate JSON and YAML content conforming to the Metaschema model.
34   */
35  public class JsonSchemaGenerator
36      extends AbstractSchemaGenerator<JsonGenerator, JsonDatatypeManager, JsonGenerationState> {
37    @NonNull
38    private final JsonFactory jsonFactory;
39  
40    /**
41     * Constructs a new JSON schema generator using a default JSON factory.
42     */
43    public JsonSchemaGenerator() {
44      this(new JsonFactory());
45    }
46  
47    /**
48     * Constructs a new JSON schema generator using the specified JSON factory.
49     *
50     * @param jsonFactory
51     *          the Jackson JSON factory to use for creating JSON generators
52     */
53    public JsonSchemaGenerator(@NonNull JsonFactory jsonFactory) {
54      this.jsonFactory = jsonFactory;
55    }
56  
57    /**
58     * Retrieves the JSON factory used by this generator.
59     *
60     * @return the JSON factory instance
61     */
62    @NonNull
63    public JsonFactory getJsonFactory() {
64      return jsonFactory;
65    }
66  
67    @SuppressWarnings("resource")
68    @Override
69    protected JsonGenerator newWriter(Writer out) {
70      try {
71        return ObjectUtils.notNull(getJsonFactory().createGenerator(out)
72            .setCodec(new ObjectMapper())
73            .useDefaultPrettyPrinter()
74            .disable(Feature.AUTO_CLOSE_TARGET));
75      } catch (IOException ex) {
76        throw new SchemaGenerationException(ex);
77      }
78    }
79  
80    @Override
81    protected JsonGenerationState newGenerationState(
82        IModule module,
83        JsonGenerator schemaWriter,
84        IConfiguration<SchemaGenerationFeature<?>> configuration) {
85      return new JsonGenerationState(module, schemaWriter, configuration);
86    }
87  
88    @Override
89    protected void generateSchema(JsonGenerationState state) {
90      IModule module = state.getModule();
91  
92      IJsonSchema moduleSchema = new JsonSchemaModule(module, state);
93      ObjectNode schemaNode = ObjectUtils.notNull(state.getJsonNodeFactory().objectNode());
94      moduleSchema.generateInlineJsonSchema(schemaNode, state);
95  
96      try {
97        state.writeObject(schemaNode);
98      } catch (IOException ex) {
99        throw new SchemaGenerationException(ex);
100     }
101   }
102 }