001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.databind.io.json;
007
008import com.fasterxml.jackson.core.JsonFactory;
009import com.fasterxml.jackson.core.JsonParser;
010
011import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
012import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
013import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
014import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
015import gov.nist.secauto.metaschema.core.model.IBoundObject;
016import gov.nist.secauto.metaschema.core.util.ObjectUtils;
017import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer;
018import gov.nist.secauto.metaschema.databind.io.DeserializationFeature;
019import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
020
021import java.io.IOException;
022import java.io.Reader;
023import java.net.URI;
024
025import edu.umd.cs.findbugs.annotations.NonNull;
026import nl.talsmasoftware.lazy4j.Lazy;
027
028public class DefaultJsonDeserializer<CLASS extends IBoundObject>
029    extends AbstractDeserializer<CLASS> {
030  private Lazy<JsonFactory> factory;
031
032  /**
033   * Construct a new JSON deserializer that will parse the bound class identified
034   * by the {@code classBinding}.
035   *
036   * @param definition
037   *          the bound class information for the Java type this deserializer is
038   *          operating on
039   */
040  public DefaultJsonDeserializer(@NonNull IBoundDefinitionModelAssembly definition) {
041    super(definition);
042    resetFactory();
043  }
044
045  protected final void resetFactory() {
046    this.factory = Lazy.lazy(this::newFactoryInstance);
047  }
048
049  @Override
050  protected void configurationChanged(IMutableConfiguration<DeserializationFeature<?>> config) {
051    super.configurationChanged(config);
052    resetFactory();
053  }
054
055  /**
056   * Get a JSON factory instance.
057   * <p>
058   * This method can be used by sub-classes to create a customized factory
059   * instance.
060   *
061   * @return the factory
062   */
063  @NonNull
064  protected JsonFactory newFactoryInstance() {
065    return JsonFactoryFactory.instance();
066  }
067
068  /**
069   * Get the parser factory associated with this deserializer.
070   *
071   * @return the factory instance
072   */
073  @NonNull
074  protected JsonFactory getJsonFactory() {
075    return ObjectUtils.notNull(factory.get());
076  }
077
078  /**
079   * Using the managed JSON factory, create a new JSON parser instance using the
080   * provided reader.
081   *
082   * @param reader
083   *          the reader for the parser to read data from
084   * @return the new parser
085   * @throws IOException
086   *           if an error occurred while creating the parser
087   */
088  @SuppressWarnings("resource") // reader resource not owned
089  @NonNull
090  protected final JsonParser newJsonParser(@NonNull Reader reader) throws IOException {
091    return ObjectUtils.notNull(getJsonFactory().createParser(reader));
092  }
093
094  @Override
095  protected INodeItem deserializeToNodeItemInternal(@NonNull Reader reader, @NonNull URI documentUri)
096      throws IOException {
097    INodeItem retval;
098    try (JsonParser jsonParser = newJsonParser(reader)) {
099      MetaschemaJsonReader parser = new MetaschemaJsonReader(jsonParser);
100      IBoundDefinitionModelAssembly definition = getDefinition();
101      IConfiguration<DeserializationFeature<?>> configuration = getConfiguration();
102
103      if (definition.isRoot()
104          && configuration.isFeatureEnabled(DeserializationFeature.DESERIALIZE_JSON_ROOT_PROPERTY)) {
105        // now parse the root property
106        CLASS value = ObjectUtils.requireNonNull(parser.readObjectRoot(
107            definition,
108            ObjectUtils.notNull(definition.getRootJsonName())));
109
110        retval = INodeItemFactory.instance().newDocumentNodeItem(definition, documentUri, value);
111      } else {
112        // read the top-level definition
113        CLASS value = ObjectUtils.asType(parser.readObject(definition));
114
115        retval = INodeItemFactory.instance().newAssemblyNodeItem(definition, documentUri, value);
116      }
117      return retval;
118    }
119  }
120
121  @Override
122  public CLASS deserializeToValueInternal(@NonNull Reader reader, @NonNull URI documentUri) throws IOException {
123    try (JsonParser jsonParser = newJsonParser(reader)) {
124      MetaschemaJsonReader parser = new MetaschemaJsonReader(jsonParser);
125      IBoundDefinitionModelAssembly definition = getDefinition();
126      IConfiguration<DeserializationFeature<?>> configuration = getConfiguration();
127
128      CLASS retval;
129      if (definition.isRoot()
130          && configuration.isFeatureEnabled(DeserializationFeature.DESERIALIZE_JSON_ROOT_PROPERTY)) {
131
132        // now parse the root property
133        retval = ObjectUtils.requireNonNull(parser.readObjectRoot(
134            definition,
135            ObjectUtils.notNull(definition.getRootJsonName())));
136      } else {
137        // read the top-level definition
138        retval = ObjectUtils.asType(ObjectUtils.requireNonNull(
139            parser.readObject(definition)));
140      }
141      return retval;
142    }
143  }
144}