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.model.IAssemblyInstanceGrouped;
18  import dev.metaschema.core.util.ObjectUtils;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  import nl.talsmasoftware.lazy4j.Lazy;
21  
22  /**
23   * Supports generation of a JSON schema based on a Metaschema
24   * {@link IAssemblyInstanceGrouped}, which can be generated inline or as a JSON
25   * schema definition.
26   */
27  public class JsonSchemaPropertyGroupedAssembly
28      extends AbstractJsonSchemaPropertyGrouped<IAssemblyInstanceGrouped>
29      implements IJsonSchemaDefinitionAssembly {
30    private final Lazy<List<JsonSchemaHelper.Choice>> choices;
31  
32    /**
33     * Construct a new JSON schema property.
34     *
35     * @param instance
36     *          the instance to construct the property for
37     * @param state
38     *          the JSON generation state used to get JSON schema information
39     */
40    public JsonSchemaPropertyGroupedAssembly(
41        @NonNull IAssemblyInstanceGrouped instance,
42        @NonNull IJsonGenerationState state) {
43      super(instance, state);
44      this.choices = Lazy.of(() -> {
45        List<IJsonSchemaPropertyFlag> flagProperties = getFlagProperties();
46        List<IJsonSchemaPropertyNamed> modelProperties
47            = JsonSchemaHelper.buildModelProperties(instance.getDefinition(), state);
48  
49        List<IJsonSchemaPropertyNamed> properties = new ArrayList<>(flagProperties.size() + modelProperties.size());
50        properties.add(new DiscriminatorProperty());
51        properties.addAll(flagProperties);
52        properties.addAll(modelProperties);
53  
54        JsonSchemaHelper.Choice baseChoice = new JsonSchemaHelper.Choice(properties);
55        return JsonSchemaHelper.explodeChoices(baseChoice, instance.getDefinition().getChoiceInstances(), state)
56            .collect(Collectors.toUnmodifiableList());
57      });
58    }
59  
60    @Override
61    public IAssemblyDefinition getDefinition() {
62      return getInstance().getDefinition();
63    }
64  
65    @Override
66    public List<JsonSchemaHelper.Choice> getChoices() {
67      return ObjectUtils.notNull(choices.get());
68    }
69  
70    @Override
71    public Stream<IJsonSchemaDefinable> collectDefinitions(
72        Set<IJsonSchemaDefinitionAssembly> visited,
73        IJsonGenerationState state) {
74      Set<IJsonSchemaDefinitionAssembly> myVisited
75          = ObjectUtils.notNull(Stream.concat(visited.stream(), Stream.of(this))
76              .collect(Collectors.toUnmodifiableSet()));
77  
78      assert visited.contains(this) || visited.stream()
79          .noneMatch(schema -> schema.getDefinition().equals(getDefinition()));
80  
81      return ObjectUtils.notNull(visited.contains(this)
82          ? Stream.of(this)
83          : Stream.concat(
84              super.collectDefinitions(myVisited, state),
85              choices.get().stream()
86                  .flatMap(choice -> choice.getCombinations().stream()
87                      .flatMap(property -> property.collectDefinitions(myVisited, state)))));
88    }
89  
90    @Override
91    public void generateBody(ObjectNode node, IJsonGenerationState state) {
92      JsonSchemaHelper.generateAssemblyBody(this, node, state);
93    }
94  }