1
2
3
4
5
6 package gov.nist.secauto.metaschema.schemagen.json.impl.builder;
7
8 import com.fasterxml.jackson.databind.node.ArrayNode;
9 import com.fasterxml.jackson.databind.node.ObjectNode;
10
11 import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
12 import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
13 import gov.nist.secauto.metaschema.core.model.IFlagInstance;
14 import gov.nist.secauto.metaschema.core.model.IGroupable;
15 import gov.nist.secauto.metaschema.core.model.IModelDefinition;
16 import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
17 import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute;
18 import gov.nist.secauto.metaschema.core.model.INamedModelInstanceGrouped;
19 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
20 import gov.nist.secauto.metaschema.schemagen.json.IDataTypeJsonSchema;
21 import gov.nist.secauto.metaschema.schemagen.json.IDefineableJsonSchema.IKey;
22 import gov.nist.secauto.metaschema.schemagen.json.IDefinitionJsonSchema;
23 import gov.nist.secauto.metaschema.schemagen.json.IJsonGenerationState;
24
25 import java.util.LinkedList;
26 import java.util.List;
27
28 import edu.umd.cs.findbugs.annotations.NonNull;
29 import edu.umd.cs.findbugs.annotations.Nullable;
30 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
31
32 public abstract class AbstractCollectionBuilder<T extends AbstractCollectionBuilder<T>>
33 extends AbstractBuilder<T>
34 implements IModelInstanceBuilder<T> {
35 private int minOccurrence = IGroupable.DEFAULT_GROUP_AS_MIN_OCCURS;
36 private int maxOccurrence = IGroupable.DEFAULT_GROUP_AS_MAX_OCCURS;
37
38 @NonNull
39 private final List<IModelInstanceBuilder.IType> types = new LinkedList<>();
40
41 @Override
42 public T addItemType(INamedModelInstanceAbsolute itemType) {
43 types.add(new AbsoluteType(itemType));
44 return thisBuilder();
45 }
46
47 @Override
48 public T addItemType(INamedModelInstanceGrouped itemType) {
49 types.add(new GroupedType(itemType));
50 return thisBuilder();
51 }
52
53 @Override
54 public List<IType> getTypes() {
55 return CollectionUtil.unmodifiableList(types);
56 }
57
58 @Override
59 public T minItems(int min) {
60 if (min < 0) {
61 throw new IllegalArgumentException(
62 String.format("The minimum value '%d' cannot be negative.", min));
63 }
64 minOccurrence = min;
65 return thisBuilder();
66 }
67
68 @Override
69 public T maxItems(int max) {
70 if (max < -1 || max == 0) {
71 throw new IllegalArgumentException(
72 String.format("The maximum value '%d' must be -1 or a positive value.", max));
73 }
74 maxOccurrence = max;
75 return thisBuilder();
76 }
77
78 @Override
79 public int getMinOccurrence() {
80 return minOccurrence;
81 }
82
83 @Override
84 public int getMaxOccurrence() {
85 return maxOccurrence;
86 }
87
88
89
90
91
92
93
94
95
96 protected void buildInternal(
97 @NonNull ObjectNode object,
98 @NonNull IJsonGenerationState state) {
99 if (types.size() == 1) {
100
101 types.iterator().next().build(object, state);
102 } else if (types.size() > 1) {
103
104 ArrayNode anyOf = object.putArray("anyOf");
105 for (IType type : types) {
106 type.build(anyOf, state);
107 }
108 }
109 }
110
111 private abstract static class Type<T extends INamedModelInstance> implements IModelInstanceBuilder.IType {
112 @NonNull
113 private final T namedModelInstance;
114 @Nullable
115 private final IFlagInstance jsonKeyFlag;
116 @Nullable
117 private final String discriminatorProperty;
118 @Nullable
119 private final String discriminatorValue;
120 @NonNull
121 private final IKey key;
122
123 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
124 protected Type(@NonNull T instance) {
125 this.namedModelInstance = instance;
126
127 this.jsonKeyFlag = instance.getEffectiveJsonKey();
128
129 if (instance instanceof INamedModelInstanceGrouped) {
130 INamedModelInstanceGrouped grouped = (INamedModelInstanceGrouped) instance;
131 this.discriminatorProperty = grouped.getParentContainer().getJsonDiscriminatorProperty();
132 this.discriminatorValue = grouped.getEffectiveDisciminatorValue();
133 } else {
134 this.discriminatorProperty = null;
135 this.discriminatorValue = null;
136 }
137 this.key
138 = IKey.of(
139 instance.getDefinition(),
140 jsonKeyFlag == null ? null : jsonKeyFlag.getName(),
141 this.discriminatorProperty,
142 this.discriminatorValue);
143 }
144
145 @NonNull
146 protected T getNamedModelInstance() {
147 return namedModelInstance;
148 }
149
150 @Nullable
151 protected IFlagInstance getJsonKeyFlag() {
152 return jsonKeyFlag;
153 }
154
155 @Nullable
156 protected String getJsonKeyFlagName() {
157 return jsonKeyFlag == null ? null : jsonKeyFlag.getEffectiveName();
158 }
159
160 @Nullable
161 protected String getDiscriminatorProperty() {
162 return discriminatorProperty;
163 }
164
165 @Nullable
166 protected String getDiscriminatorValue() {
167 return discriminatorValue;
168 }
169
170 @Override
171 public IDefinitionJsonSchema<IFlagDefinition> getJsonKeyFlagSchema(@NonNull IJsonGenerationState state) {
172 IFlagInstance jsonKey = getJsonKeyFlag();
173 return jsonKey == null ? null : state.getSchema(IKey.of(jsonKey.getDefinition()));
174 }
175
176 @Override
177 public IDataTypeJsonSchema getJsonKeyDataTypeSchema(IJsonGenerationState state) {
178 IFlagInstance jsonKey = getJsonKeyFlag();
179 return jsonKey == null ? null : state.getDataTypeSchemaForDefinition(jsonKey.getDefinition());
180 }
181
182 @Override
183 public IDefinitionJsonSchema<IModelDefinition> getJsonSchema(IJsonGenerationState state) {
184 return state.getSchema(key);
185 }
186
187 @Override
188 public void build(
189 @NonNull ArrayNode anyOf,
190 @NonNull IJsonGenerationState state) {
191 build(anyOf.addObject(), state);
192 }
193
194 @Override
195 public void build(
196 @NonNull ObjectNode object,
197 @NonNull IJsonGenerationState state) {
198
199 IModelDefinition definition = getNamedModelInstance().getDefinition();
200
201 int flagCount = definition.getFlagInstances().size();
202 if (jsonKeyFlag != null) {
203 --flagCount;
204 }
205
206 if (flagCount > 0) {
207 IDefinitionJsonSchema<IModelDefinition> schema = getJsonSchema(state);
208 schema.generateSchemaOrRef(object, state);
209 } else if (definition instanceof IFieldDefinition) {
210 IDataTypeJsonSchema schema = state.getSchema(((IFieldDefinition) definition).getJavaTypeAdapter());
211 schema.generateSchemaOrRef(object, state);
212 }
213 }
214 }
215
216 private static final class AbsoluteType
217 extends Type<INamedModelInstanceAbsolute> {
218
219 private AbsoluteType(@NonNull INamedModelInstanceAbsolute namedModelInstance) {
220 super(namedModelInstance);
221 }
222 }
223
224 private static final class GroupedType
225 extends Type<INamedModelInstanceGrouped> {
226
227 private GroupedType(@NonNull INamedModelInstanceGrouped namedModelInstance) {
228 super(namedModelInstance);
229 }
230 }
231 }