1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.schemagen.json.impl;
7   
8   import com.fasterxml.jackson.databind.node.ObjectNode;
9   
10  import java.util.ArrayList;
11  import java.util.List;
12  import java.util.Set;
13  import java.util.stream.Collectors;
14  import java.util.stream.Stream;
15  
16  import dev.metaschema.core.model.IAssemblyDefinition;
17  import dev.metaschema.core.qname.IEnhancedQName;
18  import dev.metaschema.core.util.ObjectUtils;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  import edu.umd.cs.findbugs.annotations.Nullable;
21  import nl.talsmasoftware.lazy4j.Lazy;
22  
23  /**
24   * Provides a means to generate a JSON schema based on a Metaschema assembly
25   * definition.
26   */
27  public class JsonSchemaDefinitionAssembly
28      extends AbstractJsonSchemaModelDefinition<IAssemblyDefinition>
29      implements IJsonSchemaDefinitionAssembly {
30    private final Lazy<List<JsonSchemaHelper.Choice>> choices;
31  
32    /**
33     * Construct a new JSON schema definition based on a Metaschema module
34     * definition.
35     *
36     * @param definition
37     *          the Metaschema module definition
38     * @param jsonKeyFlagName
39     *          the JSON key flag to use with thsi definition or {@code null} if no
40     *          JSON key is used
41     * @param state
42     *          the JSON generation state
43     */
44    public JsonSchemaDefinitionAssembly(
45        @NonNull IAssemblyDefinition definition,
46        @Nullable IEnhancedQName jsonKeyFlagName,
47        @NonNull IJsonGenerationState state) {
48      super(definition, jsonKeyFlagName, state);
49      this.choices = Lazy.of(() -> {
50        List<IJsonSchemaPropertyFlag> flagProperties = getFlagProperties();
51        List<IJsonSchemaPropertyNamed> modelProperties = JsonSchemaHelper.buildModelProperties(getDefinition(), state);
52  
53        List<IJsonSchemaPropertyNamed> properties = new ArrayList<>(flagProperties.size() + modelProperties.size());
54        properties.addAll(flagProperties);
55        properties.addAll(modelProperties);
56  
57        JsonSchemaHelper.Choice baseChoice = new JsonSchemaHelper.Choice(properties);
58        return JsonSchemaHelper.explodeChoices(baseChoice, getDefinition().getChoiceInstances(), state)
59            .collect(Collectors.toUnmodifiableList());
60      });
61    }
62  
63    @Override
64    public List<JsonSchemaHelper.Choice> getChoices() {
65      return ObjectUtils.notNull(choices.get());
66    }
67  
68    @Override
69    public Stream<IJsonSchemaDefinable> collectDefinitions(
70        Set<IJsonSchemaDefinitionAssembly> visited,
71        IJsonGenerationState state) {
72      Set<IJsonSchemaDefinitionAssembly> myVisited = ObjectUtils.notNull(Stream.concat(
73          visited.stream(),
74          Stream.of(this))
75          .collect(Collectors.toUnmodifiableSet()));
76  
77      assert visited.contains(this) || visited.stream()
78          .noneMatch(schema -> schema.getDefinition().equals(getDefinition()));
79  
80      return ObjectUtils.notNull(visited.contains(this)
81          ? Stream.of(this)
82          : Stream.concat(
83              super.collectDefinitions(myVisited, state),
84              choices.get().stream()
85                  .flatMap(choice -> choice.getCombinations().stream()
86                      .flatMap(property -> property.collectDefinitions(myVisited, state)
87                          .collect(Collectors.toUnmodifiableList()).stream()))));
88    }
89  
90    @Override
91    public void generateBody(ObjectNode node, IJsonGenerationState state) {
92      JsonSchemaHelper.generateAssemblyBody(this, node, state);
93    }
94  }