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