View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.schemagen.json.impl;
28  
29  import com.fasterxml.jackson.core.JsonGenerator;
30  import com.fasterxml.jackson.databind.node.JsonNodeFactory;
31  import com.fasterxml.jackson.databind.node.ObjectNode;
32  
33  import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
34  import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
35  import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
36  import gov.nist.secauto.metaschema.core.model.IDefinition;
37  import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
38  import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
39  import gov.nist.secauto.metaschema.core.model.IModule;
40  import gov.nist.secauto.metaschema.core.model.IValuedDefinition;
41  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
42  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
43  import gov.nist.secauto.metaschema.schemagen.AbstractGenerationState;
44  import gov.nist.secauto.metaschema.schemagen.SchemaGenerationFeature;
45  import gov.nist.secauto.metaschema.schemagen.json.datatype.JsonDatatypeManager;
46  import gov.nist.secauto.metaschema.schemagen.json.schema.AssemblyDefinitionJsonSchema;
47  import gov.nist.secauto.metaschema.schemagen.json.schema.DataTypeJsonSchema;
48  import gov.nist.secauto.metaschema.schemagen.json.schema.DataTypeRestrictionDefinitionJsonSchema;
49  import gov.nist.secauto.metaschema.schemagen.json.schema.FieldDefinitionJsonSchema;
50  import gov.nist.secauto.metaschema.schemagen.json.schema.FlagDefinitionJsonSchema;
51  import gov.nist.secauto.metaschema.schemagen.json.schema.IDefineableJsonSchema;
52  import gov.nist.secauto.metaschema.schemagen.json.schema.IJsonSchema;
53  
54  import java.io.IOException;
55  import java.util.LinkedHashSet;
56  import java.util.List;
57  import java.util.Map;
58  import java.util.Set;
59  import java.util.concurrent.ConcurrentHashMap;
60  
61  import edu.umd.cs.findbugs.annotations.NonNull;
62  
63  public class JsonGenerationState
64      extends AbstractGenerationState<JsonGenerator, JsonDatatypeManager> {
65  
66    @NonNull
67    private final Set<IDefineableJsonSchema> definitionSchemas = new LinkedHashSet<>();
68    @NonNull
69    private final Map<IDefinition, IJsonSchema> definitionToSchemaMap = new ConcurrentHashMap<>();
70    @NonNull
71    private final Map<IValuedDefinition, IJsonSchema> definitionValueToDataTypeSchemaMap = new ConcurrentHashMap<>();
72    @NonNull
73    private final Map<IDataTypeAdapter<?>, IJsonSchema> dataTypeToSchemaMap = new ConcurrentHashMap<>();
74  
75    public JsonGenerationState(
76        @NonNull IModule module,
77        @NonNull JsonGenerator writer,
78        @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration) {
79      super(module, writer, configuration, new JsonDatatypeManager());
80    }
81  
82    @NonNull
83    public IJsonSchema getDataTypeSchemaForDefinition(@NonNull IValuedDefinition definition) {
84      IJsonSchema retval = definitionValueToDataTypeSchemaMap.get(definition);
85      if (retval == null) {
86        AllowedValueCollection allowedValuesCollection = getContextIndependentEnumeratedValues(definition);
87        List<IAllowedValue> allowedValues = allowedValuesCollection.getValues();
88  
89        if (allowedValues.isEmpty()) {
90          // by default, just use the built-in type
91          retval = getSchema(definition.getJavaTypeAdapter());
92        } else {
93          IDefineableJsonSchema restriction
94              = new DataTypeRestrictionDefinitionJsonSchema(definition, allowedValuesCollection);
95          registerDefinitionSchema(restriction);
96          retval = restriction;
97        }
98        definitionValueToDataTypeSchemaMap.put(definition, retval);
99        retval.resolveSubSchemas(this);
100     }
101     return retval;
102   }
103 
104   @NonNull
105   public IJsonSchema getSchema(@NonNull IDefinition definition) {
106     IJsonSchema retval = definitionToSchemaMap.get(definition);
107     if (retval == null) {
108       IDefineableJsonSchema definitionSchema;
109       switch (definition.getModelType()) {
110       case ASSEMBLY:
111         definitionSchema = new AssemblyDefinitionJsonSchema((IAssemblyDefinition) definition);
112         break;
113       case FIELD:
114         definitionSchema = new FieldDefinitionJsonSchema((IFieldDefinition) definition);
115         break;
116       case FLAG:
117         definitionSchema = new FlagDefinitionJsonSchema((IFlagDefinition) definition);
118         break;
119       default:
120         throw new UnsupportedOperationException(definition.getModelType().toString());
121       }
122       definitionToSchemaMap.put(definition, definitionSchema);
123       registerDefinitionSchema(definitionSchema);
124       definitionSchema.resolveSubSchemas(this);
125       retval = definitionSchema;
126     }
127     return retval;
128   }
129 
130   @NonNull
131   public IJsonSchema getSchema(@NonNull IDataTypeAdapter<?> datatype) {
132     IJsonSchema retval = dataTypeToSchemaMap.get(datatype);
133     if (retval == null) {
134       retval = new DataTypeJsonSchema(
135           getDatatypeManager().getTypeNameForDatatype(datatype),
136           datatype);
137       dataTypeToSchemaMap.put(datatype, retval);
138     }
139     return retval;
140   }
141 
142   protected void registerDefinitionSchema(@NonNull IDefineableJsonSchema schema) {
143     if (schema.isDefinition(this)) {
144       synchronized (definitionSchemas) {
145         if (!definitionSchemas.contains(schema)) {
146           definitionSchemas.add(schema);
147         }
148       }
149     }
150   }
151 
152   public ObjectNode generateDefinitions() {
153     ObjectNode definitionsObject = ObjectUtils.notNull(JsonNodeFactory.instance.objectNode());
154 
155     for (IDefineableJsonSchema schema : definitionSchemas) {
156       schema.generateDefinition(this, definitionsObject);
157     }
158 
159     getDatatypeManager().generateDatatypes(definitionsObject);
160 
161     return definitionsObject;
162   }
163 
164   @SuppressWarnings("resource")
165   public void writeStartObject() throws IOException {
166     getWriter().writeStartObject();
167   }
168 
169   @SuppressWarnings("resource")
170   public void writeEndObject() throws IOException {
171     getWriter().writeEndObject();
172   }
173 
174   @SuppressWarnings("resource")
175   public void writeField(String fieldName, String value) throws IOException {
176     getWriter().writeStringField(fieldName, value);
177 
178   }
179 
180   @SuppressWarnings("resource")
181   public void writeField(String fieldName, ObjectNode obj) throws IOException {
182     JsonGenerator writer = getWriter(); // NOPMD not closable here
183 
184     writer.writeFieldName(fieldName);
185     writer.writeTree(obj);
186   }
187 
188   @Override
189   public void flushWriter() throws IOException {
190     getWriter().flush();
191   }
192 }