JsonDatatypeManager.java

  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. package gov.nist.secauto.metaschema.schemagen.json.datatype;

  27. import com.fasterxml.jackson.databind.JsonNode;
  28. import com.fasterxml.jackson.databind.ObjectMapper;
  29. import com.fasterxml.jackson.databind.node.ObjectNode;

  30. import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader;
  31. import gov.nist.secauto.metaschema.core.util.CollectionUtil;
  32. import gov.nist.secauto.metaschema.core.util.ObjectUtils;
  33. import gov.nist.secauto.metaschema.schemagen.SchemaGenerationException;
  34. import gov.nist.secauto.metaschema.schemagen.datatype.AbstractDatatypeManager;

  35. import java.io.IOException;
  36. import java.io.InputStream;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Set;
  40. import java.util.concurrent.ConcurrentHashMap;
  41. import java.util.regex.Matcher;
  42. import java.util.regex.Pattern;
  43. import java.util.stream.Collectors;
  44. import java.util.stream.Stream;

  45. import edu.umd.cs.findbugs.annotations.NonNull;

  46. public class JsonDatatypeManager
  47.     extends AbstractDatatypeManager {
  48.   private static final Map<String, List<String>> DATATYPE_DEPENDENCY_MAP = new ConcurrentHashMap<>();
  49.   private static final Pattern DEFINITION_REF_PATTERN = Pattern.compile("^#/definitions/(.+)$");
  50.   private static final Map<String, JsonNode> JSON_DATATYPES = new ConcurrentHashMap<>();

  51.   static {
  52.     JsonNode jsonData;
  53.     try (InputStream is
  54.         = ModuleLoader.class.getResourceAsStream("/schema/json/metaschema-datatypes.json")) {
  55.       ObjectMapper objectMapper = new ObjectMapper();
  56.       jsonData = objectMapper.readTree(is);
  57.     } catch (IOException ex) {
  58.       throw new IllegalStateException(ex);
  59.     }

  60.     // analyze datatypes for dependencies
  61.     for (String ref : getDatatypeTranslationMap().values()) {
  62.       JsonNode refNode = jsonData.at("/definitions/" + ref);
  63.       if (!refNode.isMissingNode()) {
  64.         JSON_DATATYPES.put(ref, refNode);

  65.         List<String> dependencies = getDependencies(refNode).collect(Collectors.toList());
  66.         if (!dependencies.isEmpty()) {
  67.           DATATYPE_DEPENDENCY_MAP.put(ref, dependencies);
  68.         }
  69.       }
  70.     }
  71.   }

  72.   private static Stream<String> getDependencies(@NonNull JsonNode node) {
  73.     Stream<String> retval = Stream.empty();
  74.     for (Map.Entry<String, JsonNode> entry : CollectionUtil.toIterable(ObjectUtils.notNull(node.fields()))) {
  75.       JsonNode value = entry.getValue();
  76.       assert value != null;
  77.       if ("$ref".equals(entry.getKey())) {
  78.         Matcher matcher = DEFINITION_REF_PATTERN.matcher(value.asText());
  79.         if (matcher.matches()) {
  80.           String dependency = matcher.group(1);
  81.           retval = Stream.concat(retval, Stream.of(dependency));
  82.         }
  83.       }

  84.       if (value.isArray()) {
  85.         for (JsonNode child : CollectionUtil.toIterable(ObjectUtils.notNull(value.elements()))) {
  86.           assert child != null;
  87.           retval = Stream.concat(retval, getDependencies(child));
  88.         }
  89.       }
  90.     }
  91.     return retval;
  92.   }

  93.   public void generateDatatypes(@NonNull ObjectNode definitionsObject) {
  94.     Set<String> requiredJsonDatatypes = getUsedTypes();
  95.     // resolve dependencies
  96.     for (String datatype : CollectionUtil.toIterable(ObjectUtils.notNull(
  97.         requiredJsonDatatypes.stream()
  98.             .flatMap(datatype -> {
  99.               Stream<String> result;
  100.               List<String> dependencies = DATATYPE_DEPENDENCY_MAP.get(datatype);
  101.               if (dependencies == null) {
  102.                 result = Stream.of(datatype);
  103.               } else {
  104.                 result = Stream.concat(Stream.of(datatype), dependencies.stream());
  105.               }
  106.               return result;
  107.             }).distinct()
  108.             .sorted()
  109.             .iterator()))) {

  110.       JsonNode definition = JSON_DATATYPES.get(datatype);
  111.       if (definition == null) {
  112.         throw new SchemaGenerationException("Missing JSON datatype definition for: /definitions/" + datatype);
  113.       }
  114.       definitionsObject.set(datatype, definition);
  115.     }
  116.   }

  117. }