1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.io.json;
7
8 import com.fasterxml.jackson.core.JsonLocation;
9 import com.fasterxml.jackson.core.JsonParser;
10 import com.fasterxml.jackson.core.JsonToken;
11 import com.fasterxml.jackson.databind.JsonNode;
12 import com.fasterxml.jackson.databind.ObjectMapper;
13 import com.fasterxml.jackson.databind.node.ObjectNode;
14
15 import gov.nist.secauto.metaschema.core.model.IBoundObject;
16 import gov.nist.secauto.metaschema.core.model.IMetaschemaData;
17 import gov.nist.secauto.metaschema.core.model.util.JsonUtil;
18 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19 import gov.nist.secauto.metaschema.databind.io.BindingException;
20 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
21 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
22 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelFieldComplex;
23 import gov.nist.secauto.metaschema.databind.model.IBoundFieldValue;
24 import gov.nist.secauto.metaschema.databind.model.IBoundInstance;
25 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
26 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModel;
27 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelAssembly;
28 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelChoiceGroup;
29 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldComplex;
30 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldScalar;
31 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedAssembly;
32 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedField;
33 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedNamed;
34 import gov.nist.secauto.metaschema.databind.model.IBoundProperty;
35 import gov.nist.secauto.metaschema.databind.model.info.AbstractModelInstanceReadHandler;
36 import gov.nist.secauto.metaschema.databind.model.info.IFeatureScalarItemValueHandler;
37 import gov.nist.secauto.metaschema.databind.model.info.IItemReadHandler;
38 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
39
40 import org.apache.logging.log4j.LogManager;
41 import org.apache.logging.log4j.Logger;
42
43 import java.io.IOException;
44 import java.util.Collection;
45 import java.util.Deque;
46 import java.util.HashMap;
47 import java.util.LinkedHashMap;
48 import java.util.LinkedList;
49 import java.util.List;
50 import java.util.Map;
51
52 import edu.umd.cs.findbugs.annotations.NonNull;
53 import edu.umd.cs.findbugs.annotations.Nullable;
54 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
55 import nl.talsmasoftware.lazy4j.Lazy;
56
57 public class MetaschemaJsonReader
58 implements IJsonParsingContext, IItemReadHandler {
59 private static final Logger LOGGER = LogManager.getLogger(MetaschemaJsonReader.class);
60
61 @NonNull
62 private final Deque<JsonParser> parserStack = new LinkedList<>();
63
64
65
66 @NonNull
67 private final IJsonProblemHandler problemHandler;
68 @NonNull
69 private final Lazy<ObjectMapper> objectMapper;
70
71
72
73
74
75
76
77
78
79
80 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
81 public MetaschemaJsonReader(
82 @NonNull JsonParser parser) throws IOException {
83 this(parser, new DefaultJsonProblemHandler());
84 }
85
86
87
88
89
90
91
92
93
94
95
96 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
97 public MetaschemaJsonReader(
98 @NonNull JsonParser parser,
99 @NonNull IJsonProblemHandler problemHandler) throws IOException {
100 this.problemHandler = problemHandler;
101 this.objectMapper = ObjectUtils.notNull(Lazy.lazy(ObjectMapper::new));
102 push(parser);
103 }
104
105 @SuppressWarnings("resource")
106 @Override
107 public JsonParser getReader() {
108 return ObjectUtils.notNull(parserStack.peek());
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 @SuppressWarnings("resource")
135 public final void push(JsonParser parser) throws IOException {
136 assert !parser.equals(parserStack.peek());
137 if (parser.getCurrentToken() == null) {
138 parser.nextToken();
139 }
140 parserStack.push(parser);
141 }
142
143 @SuppressWarnings("resource")
144 @NonNull
145 public final JsonParser pop(@NonNull JsonParser parser) {
146 JsonParser old = parserStack.pop();
147 assert parser.equals(old);
148 return ObjectUtils.notNull(parserStack.peek());
149 }
150
151 @Override
152 public IJsonProblemHandler getProblemHandler() {
153 return problemHandler;
154 }
155
156 @NonNull
157 protected ObjectMapper getObjectMapper() {
158 return ObjectUtils.notNull(objectMapper.get());
159 }
160
161 @SuppressWarnings("unchecked")
162 @NonNull
163 public <T> T readObject(@NonNull IBoundDefinitionModelComplex definition) throws IOException {
164 return (T) definition.readItem(null, this);
165 }
166
167 @SuppressWarnings({ "unchecked", "resource" })
168 @NonNull
169 public <T> T readObjectRoot(
170 @NonNull IBoundDefinitionModelComplex definition,
171 @NonNull String expectedFieldName) throws IOException {
172 JsonParser parser = getReader();
173
174 boolean hasStartObject = JsonToken.START_OBJECT.equals(parser.currentToken());
175 if (hasStartObject) {
176
177 JsonUtil.assertAndAdvance(parser, JsonToken.START_OBJECT);
178 }
179
180 T retval = null;
181 JsonToken token;
182 while (!JsonToken.END_OBJECT.equals(token = parser.currentToken()) && token != null) {
183 if (!JsonToken.FIELD_NAME.equals(token)) {
184 throw new IOException(String.format("Expected FIELD_NAME token, found '%s'", token.toString()));
185 }
186
187 String propertyName = ObjectUtils.notNull(parser.currentName());
188 if (expectedFieldName.equals(propertyName)) {
189
190 JsonUtil.assertAndAdvance(parser, JsonToken.FIELD_NAME);
191
192
193 retval = (T) definition.readItem(null, this);
194 break;
195 }
196
197 if (!getProblemHandler().handleUnknownProperty(
198 definition,
199 null,
200 propertyName,
201 getReader())) {
202 if (LOGGER.isWarnEnabled()) {
203 LOGGER.warn("Skipping unhandled JSON field '{}'{}.", propertyName, JsonUtil.toString(parser));
204 }
205 JsonUtil.skipNextValue(parser);
206 }
207 }
208
209 if (hasStartObject) {
210
211 JsonUtil.assertAndAdvance(parser, JsonToken.END_OBJECT);
212 }
213
214 if (retval == null) {
215 throw new IOException(String.format("Failed to find property with name '%s'%s.",
216 expectedFieldName,
217 JsonUtil.generateLocationMessage(parser)));
218 }
219 return retval;
220 }
221
222
223
224
225
226 @NonNull
227 private Object readInstance(
228 @NonNull IBoundProperty<?> instance,
229 @NonNull IBoundObject parent) throws IOException {
230 return instance.readItem(parent, this);
231 }
232
233 @NonNull
234 private <T> Object readModelInstance(
235 @NonNull IBoundInstanceModel<T> instance,
236 @NonNull IBoundObject parent) throws IOException {
237 IModelInstanceCollectionInfo<T> collectionInfo = instance.getCollectionInfo();
238 return collectionInfo.readItems(new ModelInstanceReadHandler<>(instance, parent));
239 }
240
241 private Object readFieldValue(
242 @NonNull IBoundFieldValue instance,
243 @NonNull IBoundObject parent) throws IOException {
244
245 return instance.readItem(parent, this);
246 }
247
248 private Object readObjectProperty(
249 @NonNull IBoundObject parent,
250 @NonNull IBoundProperty<?> property) throws IOException {
251 Object retval;
252 if (property instanceof IBoundInstanceModel) {
253 retval = readModelInstance((IBoundInstanceModel<?>) property, parent);
254 } else if (property instanceof IBoundInstance) {
255 retval = readInstance(property, parent);
256 } else {
257 retval = readFieldValue((IBoundFieldValue) property, parent);
258 }
259 return retval;
260 }
261
262 @Override
263 public Object readItemFlag(IBoundObject parentItem, IBoundInstanceFlag instance) throws IOException {
264 return readScalarItem(instance);
265 }
266
267 @Override
268 public Object readItemField(IBoundObject parentItem, IBoundInstanceModelFieldScalar instance) throws IOException {
269 return readScalarItem(instance);
270 }
271
272 @Override
273 public IBoundObject readItemField(IBoundObject parentItem, IBoundInstanceModelFieldComplex instance)
274 throws IOException {
275 return readFieldObject(
276 parentItem,
277 instance.getDefinition(),
278 instance.getJsonProperties(),
279 instance.getEffectiveJsonKey(),
280 getProblemHandler());
281 }
282
283 @Override
284 public IBoundObject readItemField(IBoundObject parentItem, IBoundInstanceModelGroupedField instance)
285 throws IOException {
286 IJsonProblemHandler problemHandler = new GroupedInstanceProblemHandler(instance, getProblemHandler());
287 IBoundDefinitionModelFieldComplex definition = instance.getDefinition();
288 IBoundInstanceFlag jsonValueKeyFlag = definition.getJsonValueKeyFlagInstance();
289
290 IJsonProblemHandler actualProblemHandler = jsonValueKeyFlag == null
291 ? problemHandler
292 : new JsomValueKeyProblemHandler(problemHandler, jsonValueKeyFlag);
293
294 return readComplexDefinitionObject(
295 parentItem,
296 definition,
297 instance.getEffectiveJsonKey(),
298 new PropertyBodyHandler(instance.getJsonProperties()),
299 actualProblemHandler);
300 }
301
302 @Override
303 public IBoundObject readItemField(IBoundObject parentItem, IBoundDefinitionModelFieldComplex definition)
304 throws IOException {
305 return readFieldObject(
306 parentItem,
307 definition,
308 definition.getJsonProperties(),
309 null,
310 getProblemHandler());
311 }
312
313 @Override
314 public Object readItemFieldValue(IBoundObject parentItem, IBoundFieldValue fieldValue) throws IOException {
315
316 return readScalarItem(fieldValue);
317 }
318
319 @Override
320 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundInstanceModelAssembly instance)
321 throws IOException {
322 IBoundInstanceFlag jsonKey = instance.getJsonKey();
323 IBoundDefinitionModelComplex definition = instance.getDefinition();
324 return readComplexDefinitionObject(
325 parentItem,
326 definition,
327 jsonKey,
328 new PropertyBodyHandler(instance.getJsonProperties()),
329 getProblemHandler());
330 }
331
332 @Override
333 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundInstanceModelGroupedAssembly instance)
334 throws IOException {
335 return readComplexDefinitionObject(
336 parentItem,
337 instance.getDefinition(),
338 instance.getEffectiveJsonKey(),
339 new PropertyBodyHandler(instance.getJsonProperties()),
340 new GroupedInstanceProblemHandler(instance, getProblemHandler()));
341 }
342
343 @Override
344 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundDefinitionModelAssembly definition)
345 throws IOException {
346 return readComplexDefinitionObject(
347 parentItem,
348 definition,
349 null,
350 new PropertyBodyHandler(definition.getJsonProperties()),
351 getProblemHandler());
352 }
353
354 @SuppressWarnings("resource")
355 @NonNull
356 private Object readScalarItem(@NonNull IFeatureScalarItemValueHandler handler)
357 throws IOException {
358 return handler.getJavaTypeAdapter().parse(getReader());
359 }
360
361 @NonNull
362 private IBoundObject readFieldObject(
363 @Nullable IBoundObject parentItem,
364 @NonNull IBoundDefinitionModelFieldComplex definition,
365 @NonNull Map<String, IBoundProperty<?>> jsonProperties,
366 @Nullable IBoundInstanceFlag jsonKey,
367 @NonNull IJsonProblemHandler problemHandler) throws IOException {
368 IBoundInstanceFlag jsonValueKey = definition.getJsonValueKeyFlagInstance();
369 IJsonProblemHandler actualProblemHandler = jsonValueKey == null
370 ? problemHandler
371 : new JsomValueKeyProblemHandler(problemHandler, jsonValueKey);
372
373 IBoundObject retval;
374 if (jsonProperties.isEmpty() && jsonValueKey == null) {
375 retval = readComplexDefinitionObject(
376 parentItem,
377 definition,
378 jsonKey,
379 (def, parent, problem) -> {
380 IBoundFieldValue fieldValue = definition.getFieldValue();
381 Object item = readItemFieldValue(parent, fieldValue);
382 fieldValue.setValue(parent, item);
383 },
384 actualProblemHandler);
385
386 } else {
387 retval = readComplexDefinitionObject(
388 parentItem,
389 definition,
390 jsonKey,
391 new PropertyBodyHandler(jsonProperties),
392 actualProblemHandler);
393 }
394 return retval;
395 }
396
397 @NonNull
398 private IBoundObject readComplexDefinitionObject(
399 @Nullable IBoundObject parentItem,
400 @NonNull IBoundDefinitionModelComplex definition,
401 @Nullable IBoundInstanceFlag jsonKey,
402 @NonNull DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler,
403 @NonNull IJsonProblemHandler problemHandler) throws IOException {
404 DefinitionBodyHandler<IBoundDefinitionModelComplex> actualBodyHandler = jsonKey == null
405 ? bodyHandler
406 : new JsonKeyBodyHandler(jsonKey, bodyHandler);
407
408 JsonLocation location = getReader().currentLocation();
409
410
411 IBoundObject item = definition.newInstance(
412 JsonLocation.NA.equals(location)
413 ? null
414 : () -> new MetaschemaData(location));
415
416 try {
417
418 definition.callBeforeDeserialize(item, parentItem);
419
420
421 actualBodyHandler.accept(definition, item, problemHandler);
422
423
424 definition.callAfterDeserialize(item, parentItem);
425 } catch (BindingException ex) {
426 throw new IOException(ex);
427 }
428
429 return item;
430 }
431
432 @SuppressWarnings("resource")
433 @Override
434 public IBoundObject readChoiceGroupItem(IBoundObject parentItem, IBoundInstanceModelChoiceGroup instance)
435 throws IOException {
436 JsonParser parser = getReader();
437 ObjectNode node = parser.readValueAsTree();
438
439 String discriminatorProperty = instance.getJsonDiscriminatorProperty();
440 JsonNode discriminatorNode = node.get(discriminatorProperty);
441 if (discriminatorNode == null) {
442 throw new IllegalArgumentException(String.format(
443 "Unable to find discriminator property '%s' for object at '%s'.",
444 discriminatorProperty,
445 JsonUtil.toString(parser)));
446 }
447 String discriminator = ObjectUtils.requireNonNull(discriminatorNode.asText());
448
449 IBoundInstanceModelGroupedNamed actualInstance = instance.getGroupedModelInstance(discriminator);
450 assert actualInstance != null;
451
452 IBoundObject retval;
453 try (JsonParser newParser = node.traverse(parser.getCodec())) {
454 push(newParser);
455
456
457 retval = actualInstance.readItem(parentItem, this);
458 assert newParser.currentToken() == null;
459 pop(newParser);
460 }
461
462
463 parser.nextToken();
464
465 return retval;
466 }
467
468 private final class JsonKeyBodyHandler implements DefinitionBodyHandler<IBoundDefinitionModelComplex> {
469 @NonNull
470 private final IBoundInstanceFlag jsonKey;
471 @NonNull
472 private final DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler;
473
474 private JsonKeyBodyHandler(
475 @NonNull IBoundInstanceFlag jsonKey,
476 @NonNull DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler) {
477 this.jsonKey = jsonKey;
478 this.bodyHandler = bodyHandler;
479 }
480
481 @Override
482 public void accept(
483 IBoundDefinitionModelComplex definition,
484 IBoundObject parent,
485 IJsonProblemHandler problemHandler)
486 throws IOException {
487 @SuppressWarnings("resource") JsonParser parser = getReader();
488 JsonUtil.assertCurrent(parser, JsonToken.FIELD_NAME);
489
490
491 String key = ObjectUtils.notNull(parser.currentName());
492 Object value = jsonKey.getDefinition().getJavaTypeAdapter().parse(key);
493 jsonKey.setValue(parent, ObjectUtils.notNull(value.toString()));
494
495
496 parser.nextToken();
497
498
499
500
501
502
503 bodyHandler.accept(definition, parent, problemHandler);
504
505
506
507 }
508 }
509
510 private final class PropertyBodyHandler implements DefinitionBodyHandler<IBoundDefinitionModelComplex> {
511 @NonNull
512 private final Map<String, IBoundProperty<?>> jsonProperties;
513
514 private PropertyBodyHandler(@NonNull Map<String, IBoundProperty<?>> jsonProperties) {
515 this.jsonProperties = jsonProperties;
516 }
517
518 @Override
519 public void accept(
520 IBoundDefinitionModelComplex definition,
521 IBoundObject parent,
522 IJsonProblemHandler problemHandler)
523 throws IOException {
524 @SuppressWarnings("resource") JsonParser parser = getReader();
525
526
527 JsonUtil.assertAndAdvance(parser, JsonToken.START_OBJECT);
528
529
530 Map<String, IBoundProperty<?>> remainingInstances = new HashMap<>(jsonProperties);
531
532
533 while (JsonToken.FIELD_NAME.equals(parser.currentToken())) {
534
535
536 String propertyName = ObjectUtils.notNull(parser.currentName());
537 if (LOGGER.isTraceEnabled()) {
538 LOGGER.trace("reading property {}", propertyName);
539 }
540
541 IBoundProperty<?> property = remainingInstances.get(propertyName);
542
543 boolean handled = false;
544 if (property != null) {
545
546 parser.nextToken();
547
548 Object value = readObjectProperty(parent, property);
549 property.setValue(parent, value);
550
551
552 remainingInstances.remove(propertyName);
553 handled = true;
554 }
555
556 if (!handled && !problemHandler.handleUnknownProperty(
557 definition,
558 parent,
559 propertyName,
560 getReader())) {
561 if (LOGGER.isWarnEnabled()) {
562 LOGGER.warn("Skipping unhandled JSON field '{}' {}.", propertyName, JsonUtil.toString(parser));
563 }
564 JsonUtil.assertAndAdvance(parser, JsonToken.FIELD_NAME);
565 JsonUtil.skipNextValue(parser);
566 }
567
568
569
570 JsonUtil.assertCurrent(parser, JsonToken.FIELD_NAME, JsonToken.END_OBJECT);
571 }
572
573 problemHandler.handleMissingInstances(
574 definition,
575 parent,
576 ObjectUtils.notNull(remainingInstances.values()));
577
578
579 JsonUtil.assertAndAdvance(parser, JsonToken.END_OBJECT);
580 }
581 }
582
583 private final class GroupedInstanceProblemHandler implements IJsonProblemHandler {
584 @NonNull
585 private final IBoundInstanceModelGroupedNamed instance;
586 @NonNull
587 private final IJsonProblemHandler delegate;
588
589 private GroupedInstanceProblemHandler(
590 @NonNull IBoundInstanceModelGroupedNamed instance,
591 @NonNull IJsonProblemHandler delegate) {
592 this.instance = instance;
593 this.delegate = delegate;
594 }
595
596 @Override
597 public void handleMissingInstances(
598 IBoundDefinitionModelComplex parentDefinition,
599 IBoundObject targetObject,
600 Collection<? extends IBoundProperty<?>> unhandledInstances) throws IOException {
601 delegate.handleMissingInstances(parentDefinition, targetObject, unhandledInstances);
602 }
603
604 @SuppressWarnings("resource")
605 @Override
606 public boolean handleUnknownProperty(
607 IBoundDefinitionModelComplex definition,
608 IBoundObject parentItem,
609 String fieldName,
610 JsonParser parser) throws IOException {
611 boolean retval;
612 if (instance.getParentContainer().getJsonDiscriminatorProperty().equals(fieldName)) {
613 JsonUtil.skipNextValue(parser);
614 retval = true;
615 } else {
616 retval = delegate.handleUnknownProperty(definition, parentItem, fieldName, getReader());
617 }
618 return retval;
619 }
620 }
621
622 private final class JsomValueKeyProblemHandler implements IJsonProblemHandler {
623 @NonNull
624 private final IJsonProblemHandler delegate;
625 @NonNull
626 private final IBoundInstanceFlag jsonValueKyeFlag;
627 private boolean foundJsonValueKey;
628
629 private JsomValueKeyProblemHandler(
630 @NonNull IJsonProblemHandler delegate,
631 @NonNull IBoundInstanceFlag jsonValueKyeFlag) {
632 this.delegate = delegate;
633 this.jsonValueKyeFlag = jsonValueKyeFlag;
634 }
635
636 @Override
637 public void handleMissingInstances(
638 IBoundDefinitionModelComplex parentDefinition,
639 IBoundObject targetObject,
640 Collection<? extends IBoundProperty<?>> unhandledInstances) throws IOException {
641 delegate.handleMissingInstances(parentDefinition, targetObject, unhandledInstances);
642 }
643
644 @Override
645 public boolean handleUnknownProperty(
646 IBoundDefinitionModelComplex definition,
647 IBoundObject parentItem,
648 String fieldName,
649 JsonParser parser) throws IOException {
650 boolean retval;
651 if (foundJsonValueKey) {
652 retval = delegate.handleUnknownProperty(definition, parentItem, fieldName, parser);
653 } else {
654
655 String key = ObjectUtils.notNull(parser.currentName());
656 Object keyValue = jsonValueKyeFlag.getJavaTypeAdapter().parse(key);
657 jsonValueKyeFlag.setValue(ObjectUtils.notNull(parentItem), keyValue);
658
659
660 JsonUtil.assertAndAdvance(parser, JsonToken.FIELD_NAME);
661
662 IBoundFieldValue fieldValue = ((IBoundDefinitionModelFieldComplex) definition).getFieldValue();
663 Object value = readItemFieldValue(
664 ObjectUtils.notNull(parentItem),
665 fieldValue);
666 fieldValue.setValue(ObjectUtils.notNull(parentItem), value);
667
668 retval = foundJsonValueKey = true;
669 }
670 return retval;
671 }
672
673 }
674
675 private class ModelInstanceReadHandler<ITEM>
676 extends AbstractModelInstanceReadHandler<ITEM> {
677
678 protected ModelInstanceReadHandler(
679 @NonNull IBoundInstanceModel<ITEM> instance,
680 @NonNull IBoundObject parentItem) {
681 super(instance, parentItem);
682 }
683
684 @SuppressWarnings("resource")
685 @Override
686 public List<ITEM> readList() throws IOException {
687 JsonParser parser = getReader();
688
689 List<ITEM> items = new LinkedList<>();
690 switch (parser.currentToken()) {
691 case START_ARRAY:
692
693 JsonUtil.assertAndAdvance(parser, JsonToken.START_ARRAY);
694
695
696 while (!JsonToken.END_ARRAY.equals(parser.currentToken())) {
697 items.add(readItem());
698 }
699
700
701 JsonUtil.assertAndAdvance(parser, JsonToken.END_ARRAY);
702 break;
703 case VALUE_NULL:
704 JsonUtil.assertAndAdvance(parser, JsonToken.VALUE_NULL);
705 break;
706 default:
707
708 items.add(readItem());
709 break;
710 }
711 return items;
712 }
713
714 @SuppressWarnings("resource")
715 @Override
716 public Map<String, ITEM> readMap() throws IOException {
717 JsonParser parser = getReader();
718
719 IBoundInstanceModel<?> instance = getCollectionInfo().getInstance();
720
721 @SuppressWarnings("PMD.UseConcurrentHashMap") Map<String, ITEM> items = new LinkedHashMap<>();
722
723
724
725 JsonUtil.assertAndAdvance(parser, JsonToken.START_OBJECT);
726
727
728 while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
729
730
731 JsonUtil.assertCurrent(parser, JsonToken.FIELD_NAME);
732
733 ITEM item = readItem();
734
735
736 IBoundInstanceFlag jsonKey = instance.getItemJsonKey(item);
737 assert jsonKey != null;
738
739 Object keyValue = jsonKey.getValue(item);
740 if (keyValue == null) {
741 throw new IOException(String.format("Null value for json-key for definition '%s'",
742 jsonKey.getContainingDefinition().toCoordinates()));
743 }
744 String key = jsonKey.getJavaTypeAdapter().asString(keyValue);
745 items.put(key, item);
746
747
748
749
750 JsonUtil.assertCurrent(parser, JsonToken.FIELD_NAME, JsonToken.END_OBJECT);
751 }
752
753
754 JsonUtil.assertAndAdvance(parser, JsonToken.END_OBJECT);
755
756 return items;
757 }
758
759 @Override
760 @NonNull
761 public ITEM readItem() throws IOException {
762 IBoundInstanceModel<ITEM> instance = getCollectionInfo().getInstance();
763 return instance.readItem(getParentObject(), MetaschemaJsonReader.this);
764 }
765 }
766
767 private static class MetaschemaData implements IMetaschemaData {
768 private final int line;
769 private final int column;
770 private final long charOffset;
771 private final long byteOffset;
772
773 public MetaschemaData(@NonNull JsonLocation location) {
774 this.line = location.getLineNr();
775 this.column = location.getColumnNr();
776 this.charOffset = location.getCharOffset();
777 this.byteOffset = location.getByteOffset();
778 }
779
780 @Override
781 public int getLine() {
782 return line;
783 }
784
785 @Override
786 public int getColumn() {
787 return column;
788 }
789
790 @Override
791 public long getCharOffset() {
792 return charOffset;
793 }
794
795 @Override
796 public long getByteOffset() {
797 return byteOffset;
798 }
799 }
800
801 @FunctionalInterface
802 private interface DefinitionBodyHandler<DEF extends IBoundDefinitionModelComplex> {
803 void accept(
804 @NonNull DEF definition,
805 @NonNull IBoundObject parent,
806 @NonNull IJsonProblemHandler problemHandler) throws IOException;
807 }
808 }