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) {
369 if (value == null && LOGGER.isWarnEnabled()) {
370 LOGGER.atWarn().log("Missing property value{}",
371 JsonUtil.generateLocationMessage(getReader(), getSource()));
372 }
373 return value;
374 }
375
376 @Override
377 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundInstanceModelAssembly instance)
378 throws IOException {
379 IBoundInstanceFlag jsonKey = instance.getJsonKey();
380 IBoundDefinitionModelComplex definition = instance.getDefinition();
381 return readComplexDefinitionObject(
382 parentItem,
383 definition,
384 jsonKey,
385 new PropertyBodyHandler(instance.getJsonProperties()),
386 getProblemHandler());
387 }
388
389 @Override
390 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundInstanceModelGroupedAssembly instance)
391 throws IOException {
392 return readComplexDefinitionObject(
393 parentItem,
394 instance.getDefinition(),
395 instance.getEffectiveJsonKey(),
396 new PropertyBodyHandler(instance.getJsonProperties()),
397 new GroupedInstanceProblemHandler(instance, getProblemHandler()));
398 }
399
400 @Override
401 public IBoundObject readItemAssembly(IBoundObject parentItem, IBoundDefinitionModelAssembly definition)
402 throws IOException {
403 return readComplexDefinitionObject(
404 parentItem,
405 definition,
406 null,
407 new PropertyBodyHandler(definition.getJsonProperties()),
408 getProblemHandler());
409 }
410
411 @NonNull
412 private Object readScalarItem(@NonNull IFeatureScalarItemValueHandler handler)
413 throws IOException {
414 return handler.getJavaTypeAdapter().parse(getReader(), getSource());
415 }
416
417 @NonNull
418 private IBoundObject readFieldObject(
419 @Nullable IBoundObject parentItem,
420 @NonNull IBoundDefinitionModelFieldComplex definition,
421 @NonNull Map<String, IBoundProperty<?>> jsonProperties,
422 @Nullable IBoundInstanceFlag jsonKey,
423 @NonNull IJsonProblemHandler problemHandler) throws IOException {
424 IBoundInstanceFlag jsonValueKey = definition.getJsonValueKeyFlagInstance();
425 IJsonProblemHandler actualProblemHandler = jsonValueKey == null
426 ? problemHandler
427 : new JsomValueKeyProblemHandler(problemHandler, jsonValueKey);
428
429 IBoundObject retval;
430 if (jsonProperties.isEmpty() && jsonValueKey == null) {
431 retval = readComplexDefinitionObject(
432 parentItem,
433 definition,
434 jsonKey,
435 (def, parent, problem) -> {
436 IBoundFieldValue fieldValue = definition.getFieldValue();
437 Object item = readItemFieldValue(parent, fieldValue);
438 if (item != null) {
439 fieldValue.setValue(parent, item);
440 }
441 },
442 actualProblemHandler);
443
444 } else {
445 retval = readComplexDefinitionObject(
446 parentItem,
447 definition,
448 jsonKey,
449 new PropertyBodyHandler(jsonProperties),
450 actualProblemHandler);
451 }
452 return retval;
453 }
454
455 @NonNull
456 private IBoundObject readComplexDefinitionObject(
457 @Nullable IBoundObject parentItem,
458 @NonNull IBoundDefinitionModelComplex definition,
459 @Nullable IBoundInstanceFlag jsonKey,
460 @NonNull DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler,
461 @NonNull IJsonProblemHandler problemHandler) throws IOException {
462 DefinitionBodyHandler<IBoundDefinitionModelComplex> actualBodyHandler = jsonKey == null
463 ? bodyHandler
464 : new JsonKeyBodyHandler(jsonKey, bodyHandler);
465
466 @SuppressWarnings("PMD.CloseResource")
467 JsonLocation location = getReader().currentLocation();
468
469
470 IBoundObject item = definition.newInstance(
471 JsonLocation.NA.equals(location)
472 ? null
473 : () -> new MetaschemaData(ObjectUtils.requireNonNull(location)));
474
475 try {
476
477 definition.callBeforeDeserialize(item, parentItem);
478
479
480 actualBodyHandler.accept(definition, item, problemHandler);
481
482
483 definition.callAfterDeserialize(item, parentItem);
484 } catch (BindingException ex) {
485 throw new IOException(ex);
486 }
487
488 return item;
489 }
490
491 @SuppressWarnings("resource")
492 @Override
493 public IBoundObject readChoiceGroupItem(IBoundObject parentItem, IBoundInstanceModelChoiceGroup instance)
494 throws IOException {
495 @SuppressWarnings("PMD.CloseResource")
496 JsonParser parser = getReader();
497 ObjectNode node = parser.readValueAsTree();
498
499 String discriminatorProperty = instance.getJsonDiscriminatorProperty();
500 JsonNode discriminatorNode = node.get(discriminatorProperty);
501 if (discriminatorNode == null) {
502 throw new IllegalArgumentException(String.format(
503 "Unable to find discriminator property '%s' for object at '%s'.",
504 discriminatorProperty,
505 JsonUtil.toString(parser, getSource())));
506 }
507 String discriminator = ObjectUtils.requireNonNull(discriminatorNode.asText());
508
509 IBoundInstanceModelGroupedNamed actualInstance = instance.getGroupedModelInstance(discriminator);
510 assert actualInstance != null;
511
512 IBoundObject retval;
513 try (JsonParser newParser = node.traverse(parser.getCodec())) {
514 assert newParser != null;
515 push(newParser);
516
517
518 retval = actualInstance.readItem(parentItem, this);
519 assert newParser.currentToken() == null;
520 pop(newParser);
521 }
522
523
524 parser.nextToken();
525
526 return retval;
527 }
528
529 private final class JsonKeyBodyHandler implements DefinitionBodyHandler<IBoundDefinitionModelComplex> {
530 @NonNull
531 private final IBoundInstanceFlag jsonKey;
532 @NonNull
533 private final DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler;
534
535 private JsonKeyBodyHandler(
536 @NonNull IBoundInstanceFlag jsonKey,
537 @NonNull DefinitionBodyHandler<IBoundDefinitionModelComplex> bodyHandler) {
538 this.jsonKey = jsonKey;
539 this.bodyHandler = bodyHandler;
540 }
541
542 @Override
543 public void accept(
544 IBoundDefinitionModelComplex definition,
545 IBoundObject parent,
546 IJsonProblemHandler problemHandler)
547 throws IOException {
548 @SuppressWarnings("PMD.CloseResource")
549 JsonParser parser = getReader();
550 URI resource = getSource();
551 JsonUtil.assertCurrent(parser, resource, JsonToken.FIELD_NAME);
552
553
554 String key = ObjectUtils.notNull(parser.currentName());
555 try {
556 Object value = jsonKey.getDefinition().getJavaTypeAdapter().parse(key);
557 jsonKey.setValue(parent, ObjectUtils.notNull(value.toString()));
558 } catch (IllegalArgumentException ex) {
559 throw new IOException(
560 String.format("Malformed data '%s'%s. %s",
561 key,
562 JsonUtil.generateLocationMessage(parser, resource),
563 ex.getLocalizedMessage()),
564 ex);
565 }
566
567
568 parser.nextToken();
569
570
571
572
573
574
575 bodyHandler.accept(definition, parent, problemHandler);
576
577
578
579 }
580 }
581
582 private final class PropertyBodyHandler implements DefinitionBodyHandler<IBoundDefinitionModelComplex> {
583 @NonNull
584 private final Map<String, IBoundProperty<?>> jsonProperties;
585
586 private PropertyBodyHandler(@NonNull Map<String, IBoundProperty<?>> jsonProperties) {
587 this.jsonProperties = jsonProperties;
588 }
589
590 @Override
591 public void accept(
592 IBoundDefinitionModelComplex definition,
593 IBoundObject parent,
594 IJsonProblemHandler problemHandler)
595 throws IOException {
596 @SuppressWarnings("PMD.CloseResource")
597 JsonParser parser = getReader();
598 URI resource = getSource();
599
600
601 JsonUtil.assertAndAdvance(parser, resource, JsonToken.START_OBJECT);
602
603
604 Map<String, IBoundProperty<?>> remainingInstances = new HashMap<>(jsonProperties);
605
606
607 while (JsonToken.FIELD_NAME.equals(parser.currentToken())) {
608
609
610 String propertyName = ObjectUtils.notNull(parser.currentName());
611 if (LOGGER.isTraceEnabled()) {
612 LOGGER.trace("reading property {}", propertyName);
613 }
614
615 IBoundProperty<?> property = remainingInstances.get(propertyName);
616
617 boolean handled = false;
618 if (property != null) {
619
620 parser.nextToken();
621
622 Object value = readObjectProperty(parent, property);
623 if (value != null) {
624 property.setValue(parent, value);
625 }
626
627
628 remainingInstances.remove(propertyName);
629 handled = true;
630 }
631
632 if (!handled && !problemHandler.handleUnknownProperty(
633 definition,
634 parent,
635 propertyName,
636 MetaschemaJsonReader.this)) {
637 if (LOGGER.isWarnEnabled()) {
638 LOGGER.warn("Skipping unhandled JSON field '{}' {}.", propertyName, JsonUtil.toString(parser, resource));
639 }
640 JsonUtil.assertAndAdvance(parser, resource, JsonToken.FIELD_NAME);
641 JsonUtil.skipNextValue(parser, resource);
642 }
643
644
645
646 JsonUtil.assertCurrent(parser, resource, JsonToken.FIELD_NAME, JsonToken.END_OBJECT);
647 }
648
649 problemHandler.handleMissingInstances(
650 definition,
651 parent,
652 ObjectUtils.notNull(remainingInstances.values()));
653
654
655 JsonUtil.assertAndAdvance(parser, resource, JsonToken.END_OBJECT);
656 }
657 }
658
659 private static final class GroupedInstanceProblemHandler implements IJsonProblemHandler {
660 @NonNull
661 private final IBoundInstanceModelGroupedNamed instance;
662 @NonNull
663 private final IJsonProblemHandler delegate;
664
665 private GroupedInstanceProblemHandler(
666 @NonNull IBoundInstanceModelGroupedNamed instance,
667 @NonNull IJsonProblemHandler delegate) {
668 this.instance = instance;
669 this.delegate = delegate;
670 }
671
672 @Override
673 public void handleMissingInstances(
674 IBoundDefinitionModelComplex parentDefinition,
675 IBoundObject targetObject,
676 Collection<? extends IBoundProperty<?>> unhandledInstances) throws IOException {
677 delegate.handleMissingInstances(parentDefinition, targetObject, unhandledInstances);
678 }
679
680 @Override
681 public boolean handleUnknownProperty(
682 IBoundDefinitionModelComplex definition,
683 IBoundObject parentItem,
684 String fieldName,
685 IJsonParsingContext parsingContext) throws IOException {
686 boolean retval;
687 if (instance.getParentContainer().getJsonDiscriminatorProperty().equals(fieldName)) {
688 JsonUtil.skipNextValue(parsingContext.getReader(), parsingContext.getSource());
689 retval = true;
690 } else {
691 retval = delegate.handleUnknownProperty(definition, parentItem, fieldName, parsingContext);
692 }
693 return retval;
694 }
695 }
696
697 private final class JsomValueKeyProblemHandler implements IJsonProblemHandler {
698 @NonNull
699 private final IJsonProblemHandler delegate;
700 @NonNull
701 private final IBoundInstanceFlag jsonValueKeyFlag;
702 private boolean foundJsonValueKey;
703
704 private JsomValueKeyProblemHandler(
705 @NonNull IJsonProblemHandler delegate,
706 @NonNull IBoundInstanceFlag jsonValueKeyFlag) {
707 this.delegate = delegate;
708 this.jsonValueKeyFlag = jsonValueKeyFlag;
709 }
710
711 @Override
712 public void handleMissingInstances(
713 IBoundDefinitionModelComplex parentDefinition,
714 IBoundObject targetObject,
715 Collection<? extends IBoundProperty<?>> unhandledInstances) throws IOException {
716 delegate.handleMissingInstances(parentDefinition, targetObject, unhandledInstances);
717 }
718
719 @Override
720 public boolean handleUnknownProperty(
721 IBoundDefinitionModelComplex definition,
722 IBoundObject parentItem,
723 String fieldName,
724 IJsonParsingContext parsingContext) throws IOException {
725 boolean retval;
726 if (foundJsonValueKey) {
727 retval = delegate.handleUnknownProperty(definition, parentItem, fieldName, parsingContext);
728 } else {
729 @SuppressWarnings("PMD.CloseResource")
730 JsonParser parser = parsingContext.getReader();
731 URI resource = parsingContext.getSource();
732
733
734 String key = ObjectUtils.notNull(parser.currentName());
735 try {
736 Object keyValue = jsonValueKeyFlag.getJavaTypeAdapter().parse(key);
737 jsonValueKeyFlag.setValue(ObjectUtils.notNull(parentItem), keyValue);
738 } catch (IllegalArgumentException ex) {
739 throw new IOException(
740 String.format("Malformed data '%s'%s. %s",
741 key,
742 JsonUtil.generateLocationMessage(parser, resource),
743 ex.getLocalizedMessage()),
744 ex);
745 }
746
747 JsonUtil.assertAndAdvance(parser, resource, JsonToken.FIELD_NAME);
748
749 IBoundFieldValue fieldValue = ((IBoundDefinitionModelFieldComplex) definition).getFieldValue();
750 Object value = readItemFieldValue(ObjectUtils.notNull(parentItem), fieldValue);
751 if (value != null) {
752 fieldValue.setValue(ObjectUtils.notNull(parentItem), value);
753 }
754
755 retval = foundJsonValueKey = true;
756 }
757 return retval;
758 }
759 }
760
761 private class ModelInstanceReadHandler<ITEM>
762 extends AbstractModelInstanceReadHandler<ITEM> {
763
764 protected ModelInstanceReadHandler(
765 @NonNull IBoundInstanceModel<ITEM> instance,
766 @NonNull IBoundObject parentItem) {
767 super(instance, parentItem);
768 }
769
770 @Override
771 public List<ITEM> readList() throws IOException {
772 @SuppressWarnings("PMD.CloseResource")
773 JsonParser parser = getReader();
774 URI resource = getSource();
775
776 List<ITEM> items = new LinkedList<>();
777 switch (parser.currentToken()) {
778 case START_ARRAY:
779
780 JsonUtil.assertAndAdvance(parser, resource, JsonToken.START_ARRAY);
781
782
783 while (!JsonToken.END_ARRAY.equals(parser.currentToken())) {
784 items.add(readItem());
785 }
786
787
788 JsonUtil.assertAndAdvance(parser, resource, JsonToken.END_ARRAY);
789 break;
790 case VALUE_NULL:
791 JsonUtil.assertAndAdvance(parser, resource, JsonToken.VALUE_NULL);
792 break;
793 default:
794
795 items.add(readItem());
796 break;
797 }
798 return items;
799 }
800
801 @Override
802 public Map<String, ITEM> readMap() throws IOException {
803 @SuppressWarnings("PMD.CloseResource")
804 JsonParser parser = getReader();
805 URI resource = getSource();
806
807 IBoundInstanceModel<?> instance = getCollectionInfo().getInstance();
808
809 @SuppressWarnings("PMD.UseConcurrentHashMap")
810 Map<String, ITEM> items = new LinkedHashMap<>();
811
812
813
814 JsonUtil.assertAndAdvance(parser, resource, JsonToken.START_OBJECT);
815
816
817 while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
818
819
820 JsonUtil.assertCurrent(parser, resource, JsonToken.FIELD_NAME);
821
822
823 ITEM item = readItem();
824 if (item == null) {
825 throw new IOException(String.format("Null object encountered'%s.",
826 JsonUtil.generateLocationMessage(parser, resource)));
827 }
828
829
830 IBoundInstanceFlag jsonKey = instance.getItemJsonKey(item);
831 assert jsonKey != null;
832
833 Object keyValue = jsonKey.getValue(item);
834 if (keyValue == null) {
835 throw new IOException(String.format("Null value for json-key for definition '%s'",
836 jsonKey.getContainingDefinition().toCoordinates()));
837 }
838 String key;
839 try {
840 key = jsonKey.getJavaTypeAdapter().asString(keyValue);
841 } catch (IllegalArgumentException ex) {
842 throw new IOException(
843 String.format("Malformed data '%s'%s. %s",
844 keyValue,
845 JsonUtil.generateLocationMessage(parser, resource),
846 ex.getLocalizedMessage()),
847 ex);
848 }
849 items.put(key, item);
850
851
852
853
854 JsonUtil.assertCurrent(parser, resource, JsonToken.FIELD_NAME, JsonToken.END_OBJECT);
855 }
856
857
858 JsonUtil.assertAndAdvance(parser, resource, JsonToken.END_OBJECT);
859
860 return items;
861 }
862
863 @Override
864 public ITEM readItem() throws IOException {
865 IBoundInstanceModel<ITEM> instance = getCollectionInfo().getInstance();
866 return instance.readItem(getParentObject(), MetaschemaJsonReader.this);
867 }
868 }
869
870 @SuppressWarnings("PMD.DataClass")
871 private static class MetaschemaData implements IMetaschemaData {
872 private final int line;
873 private final int column;
874 private final long charOffset;
875 private final long byteOffset;
876
877 public MetaschemaData(@NonNull JsonLocation location) {
878 this.line = location.getLineNr();
879 this.column = location.getColumnNr();
880 this.charOffset = location.getCharOffset();
881 this.byteOffset = location.getByteOffset();
882 }
883
884 @Override
885 public int getLine() {
886 return line;
887 }
888
889 @Override
890 public int getColumn() {
891 return column;
892 }
893
894 @Override
895 public long getCharOffset() {
896 return charOffset;
897 }
898
899 @Override
900 public long getByteOffset() {
901 return byteOffset;
902 }
903 }
904
905 @FunctionalInterface
906 private interface DefinitionBodyHandler<DEF extends IBoundDefinitionModelComplex> {
907 void accept(
908 @NonNull DEF definition,
909 @NonNull IBoundObject parent,
910 @NonNull IJsonProblemHandler problemHandler) throws IOException;
911 }
912 }