1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.schemagen.json.datatype;
28
29 import com.fasterxml.jackson.databind.JsonNode;
30 import com.fasterxml.jackson.databind.ObjectMapper;
31 import com.fasterxml.jackson.databind.node.ObjectNode;
32
33 import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader;
34 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
35 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
36 import gov.nist.secauto.metaschema.schemagen.SchemaGenerationException;
37 import gov.nist.secauto.metaschema.schemagen.datatype.AbstractDatatypeManager;
38
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47 import java.util.stream.Collectors;
48 import java.util.stream.Stream;
49
50 import edu.umd.cs.findbugs.annotations.NonNull;
51
52 public class JsonDatatypeManager
53 extends AbstractDatatypeManager {
54 private static final Map<String, List<String>> DATATYPE_DEPENDENCY_MAP = new ConcurrentHashMap<>();
55 private static final Pattern DEFINITION_REF_PATTERN = Pattern.compile("^#/definitions/(.+)$");
56 private static final Map<String, JsonNode> JSON_DATATYPES = new ConcurrentHashMap<>();
57
58 static {
59 JsonNode jsonData;
60 try (InputStream is
61 = ModuleLoader.class.getResourceAsStream("/schema/json/metaschema-datatypes.json")) {
62 ObjectMapper objectMapper = new ObjectMapper();
63 jsonData = objectMapper.readTree(is);
64 } catch (IOException ex) {
65 throw new IllegalStateException(ex);
66 }
67
68
69 for (String ref : getDatatypeTranslationMap().values()) {
70 JsonNode refNode = jsonData.at("/definitions/" + ref);
71 if (!refNode.isMissingNode()) {
72 JSON_DATATYPES.put(ref, refNode);
73
74 List<String> dependencies = getDependencies(refNode).collect(Collectors.toList());
75 if (!dependencies.isEmpty()) {
76 DATATYPE_DEPENDENCY_MAP.put(ref, dependencies);
77 }
78 }
79 }
80 }
81
82 private static Stream<String> getDependencies(@NonNull JsonNode node) {
83 Stream<String> retval = Stream.empty();
84 for (Map.Entry<String, JsonNode> entry : CollectionUtil.toIterable(ObjectUtils.notNull(node.fields()))) {
85 JsonNode value = entry.getValue();
86 assert value != null;
87 if ("$ref".equals(entry.getKey())) {
88 Matcher matcher = DEFINITION_REF_PATTERN.matcher(value.asText());
89 if (matcher.matches()) {
90 String dependency = matcher.group(1);
91 retval = Stream.concat(retval, Stream.of(dependency));
92 }
93 }
94
95 if (value.isArray()) {
96 for (JsonNode child : CollectionUtil.toIterable(ObjectUtils.notNull(value.elements()))) {
97 assert child != null;
98 retval = Stream.concat(retval, getDependencies(child));
99 }
100 }
101 }
102 return retval;
103 }
104
105 public void generateDatatypes(@NonNull ObjectNode definitionsObject) {
106 Set<String> requiredJsonDatatypes = getUsedTypes();
107
108 for (String datatype : CollectionUtil.toIterable(ObjectUtils.notNull(
109 requiredJsonDatatypes.stream()
110 .flatMap(datatype -> {
111 Stream<String> result;
112 List<String> dependencies = DATATYPE_DEPENDENCY_MAP.get(datatype);
113 if (dependencies == null) {
114 result = Stream.of(datatype);
115 } else {
116 result = Stream.concat(Stream.of(datatype), dependencies.stream());
117 }
118 return result;
119 }).distinct()
120 .sorted()
121 .iterator()))) {
122
123 JsonNode definition = JSON_DATATYPES.get(datatype);
124 if (definition == null) {
125 throw new SchemaGenerationException("Missing JSON datatype definition for: /definitions/" + datatype);
126 }
127 definitionsObject.set(datatype, definition);
128 }
129 }
130
131 }