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