1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.io.json;
7   
8   import com.fasterxml.jackson.core.JsonFactory;
9   import com.fasterxml.jackson.core.JsonParser;
10  
11  import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
12  import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
13  import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
14  import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
15  import gov.nist.secauto.metaschema.core.model.IBoundObject;
16  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17  import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer;
18  import gov.nist.secauto.metaschema.databind.io.DeserializationFeature;
19  import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
20  
21  import java.io.IOException;
22  import java.io.Reader;
23  import java.net.URI;
24  
25  import edu.umd.cs.findbugs.annotations.NonNull;
26  import nl.talsmasoftware.lazy4j.Lazy;
27  
28  public class DefaultJsonDeserializer<CLASS extends IBoundObject>
29      extends AbstractDeserializer<CLASS> {
30    private Lazy<JsonFactory> factory;
31  
32    /**
33     * Construct a new JSON deserializer that will parse the bound class identified
34     * by the {@code classBinding}.
35     *
36     * @param definition
37     *          the bound class information for the Java type this deserializer is
38     *          operating on
39     */
40    public DefaultJsonDeserializer(@NonNull IBoundDefinitionModelAssembly definition) {
41      super(definition);
42      resetFactory();
43    }
44  
45    protected final void resetFactory() {
46      this.factory = Lazy.lazy(this::newFactoryInstance);
47    }
48  
49    @Override
50    protected void configurationChanged(IMutableConfiguration<DeserializationFeature<?>> config) {
51      super.configurationChanged(config);
52      resetFactory();
53    }
54  
55    /**
56     * Get a JSON factory instance.
57     * <p>
58     * This method can be used by sub-classes to create a customized factory
59     * instance.
60     *
61     * @return the factory
62     */
63    @NonNull
64    protected JsonFactory newFactoryInstance() {
65      return JsonFactoryFactory.instance();
66    }
67  
68    /**
69     * Get the parser factory associated with this deserializer.
70     *
71     * @return the factory instance
72     */
73    @NonNull
74    protected JsonFactory getJsonFactory() {
75      return ObjectUtils.notNull(factory.get());
76    }
77  
78    /**
79     * Using the managed JSON factory, create a new JSON parser instance using the
80     * provided reader.
81     *
82     * @param reader
83     *          the reader for the parser to read data from
84     * @return the new parser
85     * @throws IOException
86     *           if an error occurred while creating the parser
87     */
88    @SuppressWarnings("resource") // reader resource not owned
89    @NonNull
90    protected final JsonParser newJsonParser(@NonNull Reader reader) throws IOException {
91      return ObjectUtils.notNull(getJsonFactory().createParser(reader));
92    }
93  
94    @Override
95    protected INodeItem deserializeToNodeItemInternal(@NonNull Reader reader, @NonNull URI documentUri)
96        throws IOException {
97      INodeItem retval;
98      try (JsonParser jsonParser = newJsonParser(reader)) {
99        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 }