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