1
2
3
4
5
6 package gov.nist.secauto.metaschema.schemagen.json.impl;
7
8 import com.fasterxml.jackson.core.JsonGenerator;
9 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
10 import com.fasterxml.jackson.databind.node.ObjectNode;
11
12 import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
13 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
14 import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
15 import gov.nist.secauto.metaschema.core.model.IDefinition;
16 import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
17 import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
18 import gov.nist.secauto.metaschema.core.model.IModule;
19 import gov.nist.secauto.metaschema.core.model.IValuedDefinition;
20 import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
21 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
22 import gov.nist.secauto.metaschema.schemagen.AbstractGenerationState;
23 import gov.nist.secauto.metaschema.schemagen.ModuleIndex.DefinitionEntry;
24 import gov.nist.secauto.metaschema.schemagen.SchemaGenerationFeature;
25 import gov.nist.secauto.metaschema.schemagen.json.IDataTypeJsonSchema;
26 import gov.nist.secauto.metaschema.schemagen.json.IDefineableJsonSchema.IKey;
27 import gov.nist.secauto.metaschema.schemagen.json.IDefinitionJsonSchema;
28 import gov.nist.secauto.metaschema.schemagen.json.IJsonGenerationState;
29
30 import java.io.IOException;
31 import java.util.Comparator;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.concurrent.ConcurrentHashMap;
36
37 import edu.umd.cs.findbugs.annotations.NonNull;
38 import edu.umd.cs.findbugs.annotations.Nullable;
39
40 public class JsonGenerationState
41 extends AbstractGenerationState<JsonGenerator, JsonDatatypeManager>
42 implements IJsonGenerationState {
43
44 @NonNull
45 private final JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(true);
46 @NonNull
47 private final Map<IKey, IDefinitionJsonSchema<?>> schemaDefinitions = new HashMap<>();
48 @NonNull
49 private final Map<IValuedDefinition, IDataTypeJsonSchema> definitionValueToDataTypeSchemaMap
50 = new ConcurrentHashMap<>();
51 @NonNull
52 private final Map<IDataTypeAdapter<?>, IDataTypeJsonSchema> dataTypeToSchemaMap = new ConcurrentHashMap<>();
53
54 public JsonGenerationState(
55 @NonNull IModule module,
56 @NonNull JsonGenerator writer,
57 @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration) {
58 super(module, writer, configuration, new JsonDatatypeManager());
59
60
61
62
63
64
65
66
67
68
69
70
71
72 }
73
74 @Override
75 @NonNull
76 public <DEF extends IDefinition> IDefinitionJsonSchema<DEF> getSchema(@NonNull IKey key) {
77 IDefinitionJsonSchema<?> retval = getDefinitionSchema(key, this);
78 return ObjectUtils.asType(ObjectUtils.requireNonNull(retval));
79 }
80
81 @Override
82 @NonNull
83 public IDataTypeJsonSchema getSchema(@NonNull IDataTypeAdapter<?> datatype) {
84 IDataTypeJsonSchema retval = dataTypeToSchemaMap.get(datatype);
85 if (retval == null) {
86 retval = new DataTypeJsonSchema(
87 getDatatypeManager().getTypeNameForDatatype(datatype),
88 datatype);
89 dataTypeToSchemaMap.put(datatype, retval);
90 }
91 return retval;
92 }
93
94
95
96
97
98
99
100
101 private IDefinitionJsonSchema<?> getDefinitionSchema(
102 @NonNull IKey key,
103 @NonNull IJsonGenerationState state) {
104 synchronized (schemaDefinitions) {
105 return schemaDefinitions.computeIfAbsent(key, k -> {
106 IDefinitionJsonSchema<?> retval = newJsonSchema(
107 k.getDefinition(),
108 k.getJsonKeyFlagName(),
109 k.getDiscriminatorProperty(),
110 k.getDiscriminatorValue(),
111 state);
112 assert key.equals(retval.getKey());
113 return retval;
114 });
115 }
116 }
117
118 @Override
119 public boolean isDefinitionRegistered(IDefinitionJsonSchema<?> schema) {
120 return schemaDefinitions.containsKey(schema.getKey());
121 }
122
123 @Override
124 public void registerDefinitionSchema(IDefinitionJsonSchema<?> schema) {
125 IDefinitionJsonSchema<?> old = schemaDefinitions.put(schema.getKey(), schema);
126 assert old == null;
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 @NonNull
146 private static IDefinitionJsonSchema<?> newJsonSchema(
147 @NonNull IDefinition definition,
148 @Nullable String jsonKeyFlagName,
149 @Nullable String discriminatorProperty,
150 @Nullable String discriminatorValue,
151 @NonNull IJsonGenerationState state) {
152 IDefinitionJsonSchema<?> retval;
153 if (definition instanceof IFlagDefinition) {
154 retval = new FlagDefinitionJsonSchema((IFlagDefinition) definition, state);
155 } else if (definition instanceof IAssemblyDefinition) {
156 retval = new AssemblyDefinitionJsonSchema(
157 (IAssemblyDefinition) definition,
158 jsonKeyFlagName,
159 discriminatorProperty,
160 discriminatorValue,
161 state);
162 } else if (definition instanceof IFieldDefinition) {
163 retval = new FieldDefinitionJsonSchema(
164 (IFieldDefinition) definition,
165 jsonKeyFlagName,
166 discriminatorProperty,
167 discriminatorValue,
168 state);
169 } else {
170 throw new IllegalArgumentException("Unsupported definition type" + definition.getClass().getName());
171 }
172 return retval;
173 }
174
175 public ObjectNode generateDefinitions() {
176 @NonNull
177 Map<IKey, IDefinitionJsonSchema<?>> gatheredDefinitions = new HashMap<>();
178
179 getMetaschemaIndex().getDefinitions().stream()
180 .filter(DefinitionEntry::isRoot)
181 .map(DefinitionEntry::getDefinition)
182 .forEachOrdered(def -> {
183 IDefinitionJsonSchema<?> definitionSchema = getSchema(IKey.of(def));
184 assert definitionSchema != null;
185 definitionSchema.gatherDefinitions(gatheredDefinitions, this);
186 });
187
188 ObjectNode definitionsObject = ObjectUtils.notNull(JsonNodeFactory.instance.objectNode());
189
190 gatheredDefinitions.values().stream()
191 .filter(schema -> !isInline(schema.getDefinition()))
192 .sorted(Comparator.comparing(schema -> schema.getDefinitionName(this)))
193 .forEachOrdered(schema -> {
194 schema.generateDefinition(this, definitionsObject);
195 });
196
197 getDatatypeManager().generateDatatypes(definitionsObject);
198
199 return definitionsObject;
200 }
201
202 @Override
203 public JsonNodeFactory getJsonNodeFactory() {
204 return jsonNodeFactory;
205 }
206
207 @Override
208 @NonNull
209 public IDataTypeJsonSchema getDataTypeSchemaForDefinition(@NonNull IValuedDefinition definition) {
210 IDataTypeJsonSchema retval = definitionValueToDataTypeSchemaMap.get(definition);
211 if (retval == null) {
212 AllowedValueCollection allowedValuesCollection = getContextIndependentEnumeratedValues(definition);
213 List<IAllowedValue> allowedValues = allowedValuesCollection.getValues();
214
215 IDataTypeAdapter<?> dataTypeAdapter = definition.getJavaTypeAdapter();
216
217
218 retval = getSchema(dataTypeAdapter);
219 if (!allowedValues.isEmpty()) {
220
221 retval = new DataTypeRestrictionDefinitionJsonSchema(definition, allowedValuesCollection);
222 }
223 definitionValueToDataTypeSchemaMap.put(definition, retval);
224 }
225 return retval;
226 }
227
228 @SuppressWarnings("resource")
229 public void writeStartObject() throws IOException {
230 getWriter().writeStartObject();
231 }
232
233 @SuppressWarnings("resource")
234 public void writeEndObject() throws IOException {
235 getWriter().writeEndObject();
236 }
237
238 @SuppressWarnings("resource")
239 public void writeField(String fieldName, String value) throws IOException {
240 getWriter().writeStringField(fieldName, value);
241
242 }
243
244 @SuppressWarnings("resource")
245 public void writeField(String fieldName, ObjectNode obj) throws IOException {
246 JsonGenerator writer = getWriter();
247
248 writer.writeFieldName(fieldName);
249 writer.writeTree(obj);
250 }
251
252 @SuppressWarnings("resource")
253 @Override
254 public void flushWriter() throws IOException {
255 getWriter().flush();
256 }
257
258 }