001/*
002 * Portions of this software was developed by employees of the National Institute
003 * of Standards and Technology (NIST), an agency of the Federal Government and is
004 * being made available as a public service. Pursuant to title 17 United States
005 * Code Section 105, works of NIST employees are not subject to copyright
006 * protection in the United States. This software may be subject to foreign
007 * copyright. Permission in the United States and in foreign countries, to the
008 * extent that NIST may hold copyright, to use, copy, modify, create derivative
009 * works, and distribute this software and its documentation without fee is hereby
010 * granted on a non-exclusive basis, provided that this notice and disclaimer
011 * of warranty appears in all copies.
012 *
013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
025 */
026
027package gov.nist.secauto.metaschema.databind.io.json;
028
029import com.fasterxml.jackson.core.JsonFactory;
030import com.fasterxml.jackson.core.JsonParser;
031
032import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
033import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
034import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
035import gov.nist.secauto.metaschema.core.util.ObjectUtils;
036import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer;
037import gov.nist.secauto.metaschema.databind.io.DeserializationFeature;
038import gov.nist.secauto.metaschema.databind.model.IAssemblyClassBinding;
039import gov.nist.secauto.metaschema.databind.model.info.IDataTypeHandler;
040
041import java.io.IOException;
042import java.io.Reader;
043import java.net.URI;
044
045import edu.umd.cs.findbugs.annotations.NonNull;
046
047public class DefaultJsonDeserializer<CLASS>
048    extends AbstractDeserializer<CLASS> {
049  private JsonFactory jsonFactory;
050
051  /**
052   * Construct a new JSON deserializer that will parse the bound class identified
053   * by the {@code classBinding}.
054   *
055   * @param classBinding
056   *          the bound class information for the Java type this deserializer is
057   *          operating on
058   */
059  public DefaultJsonDeserializer(@NonNull IAssemblyClassBinding classBinding) {
060    super(classBinding);
061  }
062
063  /**
064   * Get a JSON factory instance.
065   * <p>
066   * This method can be used by sub-classes to create a customized factory
067   * instance.
068   *
069   * @return the factory
070   */
071  @NonNull
072  protected JsonFactory newJsonFactoryInstance() {
073    return JsonFactoryFactory.instance();
074  }
075
076  /**
077   * Get the parser factory associated with this deserializer.
078   *
079   * @return the factory instance
080   */
081  @NonNull
082  protected JsonFactory getJsonFactory() {
083    synchronized (this) {
084      if (jsonFactory == null) {
085        jsonFactory = newJsonFactoryInstance();
086      }
087      assert jsonFactory != null;
088      return jsonFactory;
089    }
090  }
091
092  /**
093   * Using the managed JSON factory, create a new JSON parser instance using the
094   * provided reader.
095   *
096   * @param reader
097   *          the reader for the parser to read data from
098   * @return the new parser
099   * @throws IOException
100   *           if an error occurred while creating the parser
101   */
102  @SuppressWarnings("resource") // reader resource not owned
103  @NonNull
104  protected final JsonParser newJsonParser(@NonNull Reader reader) throws IOException {
105    return ObjectUtils.notNull(getJsonFactory().createParser(reader));
106  }
107
108  @SuppressWarnings("null")
109  @Override
110  protected INodeItem deserializeToNodeItemInternal(@NonNull Reader reader, @NonNull URI documentUri)
111      throws IOException {
112    INodeItem retval;
113    try (JsonParser jsonParser = newJsonParser(reader)) {
114      MetaschemaJsonReader parser = new MetaschemaJsonReader(jsonParser);
115      IAssemblyClassBinding classBinding = getClassBinding();
116      IConfiguration<DeserializationFeature<?>> configuration = getConfiguration();
117
118      if (classBinding.isRoot()
119          && configuration.isFeatureEnabled(DeserializationFeature.DESERIALIZE_JSON_ROOT_PROPERTY)) {
120
121        // now parse the root property
122        CLASS value = ObjectUtils.requireNonNull(parser.read(classBinding));
123
124        // // we should be at the end object
125        // JsonUtil.assertCurrent(parser, JsonToken.END_OBJECT);
126        //
127        // // advance past the end object
128        // JsonToken end = parser.nextToken();
129
130        retval = INodeItemFactory.instance().newDocumentNodeItem(classBinding, documentUri, value);
131      } else {
132        // Make a temporary data type handler for the top-level definition
133        IDataTypeHandler dataTypeHandler = IDataTypeHandler.newDataTypeHandler(classBinding);
134
135        // read the top-level definition
136        CLASS value = dataTypeHandler.readItem(null, parser);
137
138        retval = INodeItemFactory.instance().newAssemblyNodeItem(classBinding, documentUri, value);
139      }
140      return retval;
141    }
142  }
143
144  @Override
145  public CLASS deserializeToValue(@NonNull Reader reader, @NonNull URI documentUri) throws IOException {
146    return INodeItem.toValue(deserializeToNodeItemInternal(reader, documentUri));
147  }
148}