1
2
3
4
5
6 package gov.nist.secauto.metaschema.schemagen.json.impl;
7
8 import com.fasterxml.jackson.databind.node.ArrayNode;
9 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
10 import com.fasterxml.jackson.databind.node.ObjectNode;
11
12 import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
13 import gov.nist.secauto.metaschema.core.model.IChoiceGroupInstance;
14 import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
15 import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute;
16 import gov.nist.secauto.metaschema.core.model.ModelType;
17 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
18 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19 import gov.nist.secauto.metaschema.schemagen.json.IDefinitionJsonSchema;
20 import gov.nist.secauto.metaschema.schemagen.json.IJsonGenerationState;
21 import gov.nist.secauto.metaschema.schemagen.json.impl.IJsonProperty.PropertyCollection;
22
23 import java.io.IOException;
24 import java.util.Collection;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.function.Function;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31
32 import edu.umd.cs.findbugs.annotations.NonNull;
33 import edu.umd.cs.findbugs.annotations.Nullable;
34 import nl.talsmasoftware.lazy4j.Lazy;
35
36 public class AssemblyDefinitionJsonSchema
37 extends AbstractModelDefinitionJsonSchema<IAssemblyDefinition> {
38
39 private final Lazy<List<IGroupableModelInstanceJsonProperty<?>>> groupableModelInstances;
40
41 private final Map<INamedModelInstanceAbsolute, IGroupableModelInstanceJsonProperty<?>> choiceInstances;
42
43 public AssemblyDefinitionJsonSchema(
44 @NonNull IAssemblyDefinition definition,
45 @Nullable String jsonKeyFlagName,
46 @Nullable String discriminatorProperty,
47 @Nullable String discriminatorValue,
48 @NonNull IJsonGenerationState state) {
49 super(definition, jsonKeyFlagName, discriminatorProperty, discriminatorValue);
50 this.groupableModelInstances = Lazy.lazy(() -> ObjectUtils.notNull(definition.getModelInstances().stream()
51 .filter(instance -> !(instance instanceof IChoiceInstance))
52 .map(instance -> {
53 IGroupableModelInstanceJsonProperty<?> property;
54 if (instance instanceof INamedModelInstanceAbsolute) {
55 INamedModelInstanceAbsolute named = (INamedModelInstanceAbsolute) instance;
56 property = new NamedModelInstanceJsonProperty(named, state);
57 } else if (instance instanceof IChoiceGroupInstance) {
58 IChoiceGroupInstance choice = (IChoiceGroupInstance) instance;
59 property = new ChoiceGroupInstanceJsonProperty(choice, state);
60 } else {
61 throw new UnsupportedOperationException(
62 "model instance class not supported: " + instance.getClass().getName());
63 }
64 return property;
65 })
66 .collect(Collectors.toUnmodifiableList())));
67
68 this.choiceInstances = definition.getChoiceInstances().stream()
69 .flatMap(choice -> explodeChoice(ObjectUtils.requireNonNull(choice)))
70 .collect(Collectors.toUnmodifiableMap(
71 Function.identity(),
72 instance -> new NamedModelInstanceJsonProperty(ObjectUtils.requireNonNull(instance), state)));
73 }
74
75 private static Stream<? extends INamedModelInstanceAbsolute> explodeChoice(@NonNull IChoiceInstance choice) {
76 return choice.getNamedModelInstances().stream();
77 }
78
79 @NonNull
80 protected List<IGroupableModelInstanceJsonProperty<?>> getGroupableModelInstances() {
81 return ObjectUtils.notNull(groupableModelInstances.get());
82 }
83
84 @Override
85 public void gatherDefinitions(
86 @NonNull Map<IKey, IDefinitionJsonSchema<?>> gatheredDefinitions,
87 @NonNull IJsonGenerationState state) {
88
89 if (!gatheredDefinitions.containsKey(getKey())) {
90 super.gatherDefinitions(gatheredDefinitions, state);
91
92
93 for (IGroupableModelInstanceJsonProperty<?> property : getGroupableModelInstances()) {
94 property.gatherDefinitions(gatheredDefinitions, state);
95 }
96
97
98 this.choiceInstances.values().forEach(property -> {
99 property.gatherDefinitions(gatheredDefinitions, state);
100 });
101 }
102 }
103
104 @Override
105 protected void generateBody(
106 IJsonGenerationState state,
107 ObjectNode obj) throws IOException {
108 obj.put("type", "object");
109
110 PropertyCollection properties = new PropertyCollection();
111
112
113 String discriminatorProperty = getDiscriminatorProperty();
114 if (discriminatorProperty != null) {
115 ObjectNode discriminatorObj = state.getJsonNodeFactory().objectNode();
116 discriminatorObj.put("const", getDiscriminatorValue());
117 properties.addProperty(discriminatorProperty, discriminatorObj);
118 }
119
120
121 for (FlagInstanceJsonProperty flag : getFlagProperties()) {
122 assert flag != null;
123 flag.generateProperty(properties, state);
124 }
125
126
127 for (IGroupableModelInstanceJsonProperty<?> property : getGroupableModelInstances()) {
128 assert property != null;
129 property.generateProperty(properties, state);
130 }
131
132 Collection<? extends IChoiceInstance> choices = getDefinition().getChoiceInstances();
133 if (choices.isEmpty()) {
134 properties.generate(obj);
135 obj.put("additionalProperties", false);
136 } else {
137 List<PropertyCollection> propertyChoices = CollectionUtil.singletonList(properties);
138 propertyChoices = explodeChoices(choices, propertyChoices, state);
139
140 if (propertyChoices.size() == 1) {
141 propertyChoices.iterator().next().generate(obj);
142 } else if (propertyChoices.size() > 1) {
143 generateChoices(propertyChoices, obj, state);
144 }
145 }
146 }
147
148 protected void generateChoices(
149 List<PropertyCollection> propertyChoices,
150 @NonNull ObjectNode definitionNode,
151 @NonNull IJsonGenerationState state) {
152 ArrayNode anyOfdNode = ObjectUtils.notNull(JsonNodeFactory.instance.arrayNode());
153 for (PropertyCollection propertyChoice : propertyChoices) {
154 ObjectNode choiceDefinitionNode = ObjectUtils.notNull(JsonNodeFactory.instance.objectNode());
155 propertyChoice.generate(choiceDefinitionNode);
156 choiceDefinitionNode.put("additionalProperties", false);
157 anyOfdNode.add(choiceDefinitionNode);
158 }
159 definitionNode.set("anyOf", anyOfdNode);
160 }
161
162 protected List<PropertyCollection> explodeChoices(
163 @NonNull Collection<? extends IChoiceInstance> choices,
164 @NonNull List<PropertyCollection> propertyChoices,
165 @NonNull IJsonGenerationState state) throws IOException {
166
167 List<PropertyCollection> retval = propertyChoices;
168
169 for (IChoiceInstance choice : choices) {
170 List<PropertyCollection> newRetval = new LinkedList<>();
171 for (INamedModelInstanceAbsolute optionInstance : choice.getNamedModelInstances()) {
172 if (ModelType.CHOICE.equals(optionInstance.getModelType())) {
173
174 newRetval.addAll(explodeChoices(
175 CollectionUtil.singleton((IChoiceInstance) optionInstance),
176 retval,
177 state));
178 } else {
179
180 for (PropertyCollection oldInstanceProperties : retval) {
181 @SuppressWarnings("null")
182 @NonNull
183 PropertyCollection newInstanceProperties = oldInstanceProperties.copy();
184
185
186 choiceInstances.get(optionInstance)
187 .generateProperty(newInstanceProperties, state);
188
189 newRetval.add(newInstanceProperties);
190 }
191 }
192 }
193 retval = newRetval;
194 }
195 return retval;
196 }
197 }