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